From 42cbcf1c7ba2f0e75dda60768a783baada3a4ba3 Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Sun, 22 Dec 2024 13:40:47 -0500 Subject: [PATCH] fix add_fields population --- Cargo.lock | 10 ++ Cargo.toml | 4 + macros/Cargo.lock | 7 + macros/Cargo.toml | 12 ++ macros/src/lib.rs | 9 ++ macros/src/merge_right.rs | 186 +++++++++++++++++++++++ src/config/config.rs | 66 +++++++-- src/config/fixargs.rs | 45 +++++- src/config/handle_files.rs | 9 +- src/config/wit_types.rs | 3 +- src/main.rs | 2 + src/merge_right.rs | 293 +++++++++++++++++++++++++++++++++++++ src/primitive.rs | 29 ++++ src/proto.rs | 5 +- 14 files changed, 652 insertions(+), 28 deletions(-) create mode 100644 macros/Cargo.lock create mode 100644 macros/Cargo.toml create mode 100644 macros/src/lib.rs create mode 100644 macros/src/merge_right.rs create mode 100644 src/merge_right.rs create mode 100644 src/primitive.rs diff --git a/Cargo.lock b/Cargo.lock index 90d11f8..55f62d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -185,6 +185,7 @@ dependencies = [ "base64", "convert_case", "lazy_static", + "macros", "oapi", "oas3", "openapi3-parser", @@ -521,6 +522,15 @@ dependencies = [ "logos-codegen", ] +[[package]] +name = "macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "memchr" version = "2.7.4" diff --git a/Cargo.toml b/Cargo.toml index 34fa354..4c150e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,7 @@ wit-parser = "0.222.0" lazy_static = "1.5.0" base64 = "0.22.1" strum_macros = "0.26.4" +macros = {path = "macros"} + +[workspace] +members = [".", "macros"] diff --git a/macros/Cargo.lock b/macros/Cargo.lock new file mode 100644 index 0000000..8821391 --- /dev/null +++ b/macros/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "macros" +version = "0.1.0" diff --git a/macros/Cargo.toml b/macros/Cargo.toml new file mode 100644 index 0000000..a47dfa6 --- /dev/null +++ b/macros/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "macros" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +syn = { version = "2.0.60", features = ["derive", "full"] } +quote = "1.0.36" +proc-macro2 = "1.0.81" diff --git a/macros/src/lib.rs b/macros/src/lib.rs new file mode 100644 index 0000000..0ced838 --- /dev/null +++ b/macros/src/lib.rs @@ -0,0 +1,9 @@ +use proc_macro::TokenStream; + +mod merge_right; + +use crate::merge_right::expand_merge_right_derive; +#[proc_macro_derive(MergeRight, attributes(merge_right))] +pub fn merge_right_derive(input: TokenStream) -> TokenStream { + expand_merge_right_derive(input) +} diff --git a/macros/src/merge_right.rs b/macros/src/merge_right.rs new file mode 100644 index 0000000..cecf596 --- /dev/null +++ b/macros/src/merge_right.rs @@ -0,0 +1,186 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; +use quote::quote; +use syn::spanned::Spanned; +use syn::{parse_macro_input, Data, DeriveInput, Fields, Index}; + +const MERGE_RIGHT_FN: &str = "merge_right_fn"; +const MERGE_RIGHT: &str = "merge_right"; + +#[derive(Default)] +struct Attrs { + merge_right_fn: Option, +} + +fn get_attrs(attrs: &[syn::Attribute]) -> syn::Result { + let mut attrs_ret = Attrs::default(); + for attr in attrs { + if attr.path().is_ident(MERGE_RIGHT) { + attr.parse_nested_meta(|meta| { + if meta.path.is_ident(MERGE_RIGHT_FN) { + let p: syn::Expr = meta.value()?.parse()?; + let lit = + if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit), .. }) = p { + let suffix = lit.suffix(); + if !suffix.is_empty() { + return Err(syn::Error::new( + lit.span(), + format!("unexpected suffix `{}` on string literal", suffix), + )); + } + lit + } else { + return Err(syn::Error::new( + p.span(), + format!( + "expected merge_right {} attribute to be a string.", + MERGE_RIGHT_FN + ), + )); + }; + let expr_path: syn::ExprPath = lit.parse()?; + attrs_ret.merge_right_fn = Some(expr_path); + Ok(()) + } else { + Err(syn::Error::new(attr.span(), "Unknown helper attribute.")) + } + })?; + } + } + Ok(attrs_ret) +} + +pub fn expand_merge_right_derive(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + let name = input.ident.clone(); + let generics = input.generics.clone(); + let gen = match input.data { + // Implement for structs + Data::Struct(data) => { + let fields = match &data.fields { + Fields::Named(fields) => &fields.named, + Fields::Unnamed(fields) => &fields.unnamed, + Fields::Unit => { + return quote! { + impl crate::merge_right::MergeRight for #name { + fn merge_right(self, other: Self) -> Self { + other + } + } + } + .into() + } + }; + + let merge_logic = fields.iter().enumerate().map(|(i, f)| { + let attrs = get_attrs(&f.attrs); + if let Err(err) = attrs { + panic!("{}", err); + } + let attrs = attrs.unwrap(); + let name = &f.ident; + + match &data.fields { + Fields::Named(_) => { + if let Some(merge_right_fn) = attrs.merge_right_fn { + quote! { + #name: #merge_right_fn(self.#name, other.#name), + } + } else { + quote! { + #name: self.#name.merge_right(other.#name), + } + } + } + Fields::Unnamed(_) => { + let name = Index::from(i); + if let Some(merge_right_fn) = attrs.merge_right_fn { + quote! { + #merge_right_fn(self.#name, other.#name), + } + } else { + quote! { + self.#name.merge_right(other.#name), + } + } + } + Fields::Unit => unreachable!(), + } + }); + + let generics_lt = generics.lt_token; + let generics_gt = generics.gt_token; + let generics_params = generics.params; + + let generics_del = quote! { + #generics_lt #generics_params #generics_gt + }; + + let initializer = match data.fields { + Fields::Named(_) => quote! { + Self { + #(#merge_logic)* + } + }, + Fields::Unnamed(_) => quote! { + Self(#(#merge_logic)*) + }, + Fields::Unit => unreachable!(), + }; + + quote! { + impl #generics_del crate::merge_right::MergeRight for #name #generics_del { + fn merge_right(self, other: Self) -> Self { + #initializer + } + } + } + } + // Implement for enums + Data::Enum(_) => quote! { + impl crate::merge_right::MergeRight for #name { + fn merge_right(self, other: Self) -> Self { + other + } + } + }, + // Optionally handle or disallow unions + Data::Union(_) => { + return syn::Error::new_spanned(input, "Union types are not supported by MergeRight") + .to_compile_error() + .into() + } + }; + + gen.into() +} + +#[cfg(test)] +mod tests { + use syn::{parse_quote, Attribute}; + + use super::*; + + #[test] + fn test_get_attrs_invalid_type() { + let attrs: Vec = vec![parse_quote!(#[merge_right(merge_right_fn = 123)])]; + let result = get_attrs(&attrs); + assert!( + result.is_err(), + "Expected error with non-string type for `merge_right_fn`" + ); + } + + #[test] + fn test_get_attrs_unexpected_suffix() { + let attrs: Vec = + vec![parse_quote!(#[merge_right(merge_right_fn = "some_fn()")])]; + let result = get_attrs(&attrs); + assert!( + result.is_err(), + "Expected error with unexpected suffix on string literal" + ); + } +} diff --git a/src/config/config.rs b/src/config/config.rs index 9444300..ff1ec42 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -1,13 +1,16 @@ +use std::collections::BTreeSet; +use serde::{Deserialize, Serialize}; +use macros::MergeRight; use crate::config::wit_types::WitType; -#[derive(Debug, Clone, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)] pub struct Config { pub package: String, - pub interfaces: Vec, + pub interfaces: BTreeSet, pub world: World, } -#[derive(Debug, Clone, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)] pub struct World { pub name: String, pub uses: Vec, @@ -15,47 +18,84 @@ pub struct World { pub exports: Vec, } -#[derive(Debug, Clone, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)] pub struct Interface { pub name: String, - pub records: Vec, + pub records: BTreeSet, pub uses: Vec, pub functions: Vec, } -#[derive(Debug, Clone, Default, PartialEq, Eq)] +impl PartialOrd for Interface { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.name.cmp(&other.name)) + } +} + +impl Ord for Interface { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.name.cmp(&other.name) + } +} + +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)] pub struct Record { pub name: String, - pub fields: Vec, - pub added_fields: Vec, + pub fields: BTreeSet, + pub added_fields: BTreeSet, } -#[derive(Debug, Clone, Default, PartialEq, Eq)] + +impl PartialOrd for Record { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.name.cmp(&other.name)) + } +} + +impl Ord for Record { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.name.cmp(&other.name) + } +} + +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)] pub struct UseStatement { pub name: String, pub items: Vec, } -#[derive(Debug, Clone, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)] pub struct Function { pub name: String, pub parameters: Vec, pub return_type: ReturnTy, } -#[derive(Debug, Clone, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)] pub struct ReturnTy { pub return_type: String, pub error_type: String, } -#[derive(Debug, Clone, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)] pub struct Parameter { pub name: String, pub parameter_type: String, } -#[derive(Debug, Clone, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)] pub struct Field { pub name: String, pub field_type: WitType, } + +impl PartialOrd for Field { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.name.cmp(&other.name)) + } +} + +impl Ord for Field { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.name.cmp(&other.name) + } +} diff --git a/src/config/fixargs.rs b/src/config/fixargs.rs index b12c953..b507f21 100644 --- a/src/config/fixargs.rs +++ b/src/config/fixargs.rs @@ -1,7 +1,9 @@ +use std::collections::BTreeSet; use base64::Engine; use tailcall_valid::{Valid, Validator}; use crate::config::config::{Config, Field, Interface, Record}; use crate::config::wit_types::WitType; +use crate::merge_right::MergeRight; use crate::tryfold::TryFold; pub fn fix_args(config: Config) -> Valid { @@ -14,16 +16,22 @@ pub fn fix_args(config: Config) -> Valid { fix_field() .try_fold(&(&config, interface, record, &field, &field.field_type), interface.clone()) .and_then(|new_interface| { - Valid::succeed(new_interface) + Valid::succeed(Some(new_interface)) }) } else { - Valid::succeed(interface.clone()) + Valid::succeed(None) } }) }) }).and_then(|x| { - let x = x.into_iter().flatten().into_iter().flatten().collect::>(); - config.interfaces = x; + let x = x.into_iter().flatten().into_iter().flatten().filter_map(|x| x).collect::>(); + + let mut map = BTreeSet::new(); + for interface in x { + map.insert(interface); + } + + config.interfaces = config.interfaces.merge_right(map); Valid::succeed(config) }) @@ -39,7 +47,7 @@ fn fix_field<'a>() -> TryFold<'a, (&'a Config, &'a Interface, &'a Record, &'a Fi WitType::Result(a, b) => { // TODO: this impl needs check. // eg: -> result - // this does not make sense to resolve with rec.name-field.name-A/B + // this does not make sense to resolve with rec.name-A/B return if !a.is_primitive() { fix_field().try_fold(&(*config, *interface, *rec, *field, a), o) @@ -61,7 +69,26 @@ fn fix_field<'a>() -> TryFold<'a, (&'a Config, &'a Interface, &'a Record, &'a Fi }))); } WitType::Record(records) => { - for (arg_name, ty) in records { + let field_name = if field.name.is_empty() { + base64::prelude::BASE64_STANDARD.encode(field.name.as_bytes()) + }else { + field.name.clone() + }; + let name = format!("{}-{}", rec.name, field_name); + + let new_field = Field { + name, + field_type: WitType::Record(records.clone()), + }; + let mut rec = (*rec).clone(); + rec.added_fields.insert(new_field); + // let mut filtered_records = o.records.into_iter().filter(|r| r.name == rec.name).collect::>(); + // filtered_records.push(rec); + o.records.remove(&rec); + o.records.insert(rec); + Valid::succeed(o) + +/* for (arg_name, ty) in records { let arg_name = if arg_name.is_empty() { // TODO: maybe we can introduce some transformers // which uses AI to generate better names @@ -71,8 +98,9 @@ fn fix_field<'a>() -> TryFold<'a, (&'a Config, &'a Interface, &'a Record, &'a Fi }; let name = format!("{}-{}-{}", rec.name, field.name, arg_name); + println!("name: {}", name); let new_field = Field { - name, + name: name.clone(), field_type: ty.clone(), }; let mut rec = (*rec).clone(); @@ -80,9 +108,10 @@ fn fix_field<'a>() -> TryFold<'a, (&'a Config, &'a Interface, &'a Record, &'a Fi let mut filtered_records = o.records.into_iter().filter(|r| r.name == rec.name).collect::>(); filtered_records.push(rec); + o.records = filtered_records; } - Valid::succeed(o) + Valid::succeed(o)*/ /* Valid::from_iter(records.iter(), |(_, b)| { fix_field() diff --git a/src/config/handle_files.rs b/src/config/handle_files.rs index b0d6363..b3f0e0d 100644 --- a/src/config/handle_files.rs +++ b/src/config/handle_files.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeSet; use anyhow::{anyhow, Error}; use tailcall_valid::{Valid, Validator}; use crate::config::config::{Config, Field, Interface, Record}; @@ -28,8 +29,8 @@ pub fn handle_types(config: Config, spec: &OpenApiSpec) -> Valid>(), + added_fields: Default::default(), }; Valid::succeed(record) }) @@ -37,10 +38,10 @@ pub fn handle_types(config: Config, spec: &OpenApiSpec) -> Valid>(), ..Default::default() }; - config.interfaces.push(interface); + config.interfaces.insert(interface); Valid::succeed(()) }) }).and_then(|_| Valid::succeed(config)) diff --git a/src/config/wit_types.rs b/src/config/wit_types.rs index 85910a0..9841711 100644 --- a/src/config/wit_types.rs +++ b/src/config/wit_types.rs @@ -1,9 +1,10 @@ +use serde::{Deserialize, Serialize}; use tailcall_valid::{Valid, Validator}; use crate::config::to_wit::ToWit; use crate::ser::{OpenApiSpec, Schema}; -#[derive(Debug, Clone, Default, PartialEq, Eq, strum_macros::Display)] +#[derive(Debug, Clone, Default, PartialEq, Eq, strum_macros::Display, Serialize, Deserialize)] pub enum WitType { // Primitive Types Bool, diff --git a/src/main.rs b/src/main.rs index 86a3363..42975ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,8 @@ mod config; mod transformer; mod transform; mod tryfold; +mod merge_right; +mod primitive; use serde_json::{Map, Value}; use anyhow::{Result, bail}; diff --git a/src/merge_right.rs b/src/merge_right.rs new file mode 100644 index 0000000..025ce41 --- /dev/null +++ b/src/merge_right.rs @@ -0,0 +1,293 @@ +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use crate::config::wit_types::WitType; + +pub trait MergeRight { + fn merge_right(self, other: Self) -> Self; +} + +impl MergeRight for Option { + fn merge_right(self, other: Self) -> Self { + match (self, other) { + (Some(this), Some(that)) => Some(this.merge_right(that)), + (None, Some(that)) => Some(that), + (Some(this), None) => Some(this), + (None, None) => None, + } + } +} + +impl MergeRight for Vec { + fn merge_right(mut self, other: Self) -> Self { + self.extend(other); + self + } +} + +impl MergeRight for BTreeSet + where + V: Ord, +{ + fn merge_right(self, mut other: Self) -> Self { + other.extend(self); + other + } +} + +impl MergeRight for HashSet + where + V: Eq + std::hash::Hash, +{ + fn merge_right(mut self, other: Self) -> Self { + self.extend(other); + self + } +} + +impl MergeRight for BTreeMap + where + K: Ord, + V: MergeRight, +{ + fn merge_right(mut self, other: Self) -> Self { + for (other_name, mut other_value) in other { + if let Some(self_value) = self.remove(&other_name) { + other_value = self_value.merge_right(other_value); + } + + self.insert(other_name, other_value); + } + self + } +} + +impl MergeRight for HashMap + where + K: Eq + std::hash::Hash, + V: MergeRight, +{ + fn merge_right(mut self, other: Self) -> Self { + for (other_name, mut other_value) in other { + if let Some(self_value) = self.remove(&other_name) { + other_value = self_value.merge_right(other_value); + } + + self.insert(other_name, other_value); + } + self + } +} + +impl MergeRight for WitType { + fn merge_right(self, other: Self) -> Self { + other + } +} + +#[cfg(test)] +mod tests { + use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; + + use serde_json::json; + + use super::MergeRight; + + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] + struct Test(u32); + + impl From for Test { + fn from(value: u32) -> Self { + Self(value) + } + } + + impl MergeRight for Test { + fn merge_right(self, other: Self) -> Self { + Self(self.0 + other.0) + } + } + + #[test] + fn test_option() { + let x: Option = None.merge_right(None); + assert_eq!(x, None); + + let x = Some(Test::from(1)).merge_right(None); + assert_eq!(x, Some(Test::from(1))); + + let x = None.merge_right(Some(Test::from(2))); + assert_eq!(x, Some(Test::from(2))); + + let x = Some(Test::from(1)).merge_right(Some(Test::from(2))); + assert_eq!(x, Some(Test::from(3))); + } + + #[test] + fn test_vec() { + let l: Vec = vec![]; + let r: Vec = vec![]; + assert_eq!(l.merge_right(r), vec![]); + + let l: Vec = vec![Test::from(1), Test::from(2)]; + let r: Vec = vec![]; + assert_eq!(l.merge_right(r), vec![Test::from(1), Test::from(2)]); + + let l: Vec = vec![]; + let r: Vec = vec![Test::from(3), Test::from(4)]; + assert_eq!(l.merge_right(r), vec![Test::from(3), Test::from(4)]); + + let l: Vec = vec![Test::from(1), Test::from(2)]; + let r: Vec = vec![Test::from(3), Test::from(4)]; + assert_eq!( + l.merge_right(r), + vec![Test::from(1), Test::from(2), Test::from(3), Test::from(4)] + ); + } + + #[test] + fn test_btree_set() { + let l: BTreeSet = BTreeSet::from_iter(vec![]); + let r: BTreeSet = BTreeSet::from_iter(vec![]); + assert_eq!(l.merge_right(r), BTreeSet::from_iter(vec![])); + + let l: BTreeSet = BTreeSet::from_iter(vec![Test::from(1), Test::from(2)]); + let r: BTreeSet = BTreeSet::from_iter(vec![]); + assert_eq!( + l.merge_right(r), + BTreeSet::from_iter(vec![Test::from(1), Test::from(2)]) + ); + + let l: BTreeSet = BTreeSet::from_iter(vec![]); + let r: BTreeSet = BTreeSet::from_iter(vec![Test::from(3), Test::from(4)]); + assert_eq!( + l.merge_right(r), + BTreeSet::from_iter(vec![Test::from(3), Test::from(4)]) + ); + + let l: BTreeSet = BTreeSet::from_iter(vec![Test::from(1), Test::from(2)]); + let r: BTreeSet = + BTreeSet::from_iter(vec![Test::from(2), Test::from(3), Test::from(4)]); + assert_eq!( + l.merge_right(r), + BTreeSet::from_iter(vec![ + Test::from(1), + Test::from(2), + Test::from(3), + Test::from(4) + ]) + ); + } + + #[test] + fn test_hash_set() { + let l: HashSet = HashSet::from_iter(vec![]); + let r: HashSet = HashSet::from_iter(vec![]); + assert_eq!(l.merge_right(r), HashSet::from_iter(vec![])); + + let l: HashSet = HashSet::from_iter(vec![Test::from(1), Test::from(2)]); + let r: HashSet = HashSet::from_iter(vec![]); + assert_eq!( + l.merge_right(r), + HashSet::from_iter(vec![Test::from(1), Test::from(2)]) + ); + + let l: HashSet = HashSet::from_iter(vec![]); + let r: HashSet = HashSet::from_iter(vec![Test::from(3), Test::from(4)]); + assert_eq!( + l.merge_right(r), + HashSet::from_iter(vec![Test::from(3), Test::from(4)]) + ); + + let l: HashSet = HashSet::from_iter(vec![Test::from(1), Test::from(2)]); + let r: HashSet = + HashSet::from_iter(vec![Test::from(2), Test::from(3), Test::from(4)]); + assert_eq!( + l.merge_right(r), + HashSet::from_iter(vec![ + Test::from(1), + Test::from(2), + Test::from(3), + Test::from(4) + ]) + ); + } + + #[test] + fn test_btree_map() { + let l: BTreeMap = BTreeMap::from_iter(vec![]); + let r: BTreeMap = BTreeMap::from_iter(vec![]); + assert_eq!(l.merge_right(r), BTreeMap::from_iter(vec![])); + + let l: BTreeMap = + BTreeMap::from_iter(vec![(1, Test::from(1)), (2, Test::from(2))]); + let r: BTreeMap = BTreeMap::from_iter(vec![]); + assert_eq!( + l.merge_right(r), + BTreeMap::from_iter(vec![(1, Test::from(1)), (2, Test::from(2))]) + ); + + let l: BTreeMap = BTreeMap::from_iter(vec![]); + let r: BTreeMap = + BTreeMap::from_iter(vec![(3, Test::from(3)), (4, Test::from(4))]); + assert_eq!( + l.merge_right(r), + BTreeMap::from_iter(vec![(3, Test::from(3)), (4, Test::from(4))]) + ); + + let l: BTreeMap = + BTreeMap::from_iter(vec![(1, Test::from(1)), (2, Test::from(2))]); + let r: BTreeMap = BTreeMap::from_iter(vec![ + (2, Test::from(5)), + (3, Test::from(3)), + (4, Test::from(4)), + ]); + assert_eq!( + l.merge_right(r), + BTreeMap::from_iter(vec![ + (1, Test::from(1)), + (2, Test::from(7)), + (3, Test::from(3)), + (4, Test::from(4)) + ]) + ); + } + + #[test] + fn test_hash_map() { + let l: HashMap = HashMap::from_iter(vec![]); + let r: HashMap = HashMap::from_iter(vec![]); + assert_eq!(l.merge_right(r), HashMap::from_iter(vec![])); + + let l: HashMap = + HashMap::from_iter(vec![(1, Test::from(1)), (2, Test::from(2))]); + let r: HashMap = HashMap::from_iter(vec![]); + assert_eq!( + l.merge_right(r), + HashMap::from_iter(vec![(1, Test::from(1)), (2, Test::from(2))]) + ); + + let l: HashMap = HashMap::from_iter(vec![]); + let r: HashMap = + HashMap::from_iter(vec![(3, Test::from(3)), (4, Test::from(4))]); + assert_eq!( + l.merge_right(r), + HashMap::from_iter(vec![(3, Test::from(3)), (4, Test::from(4))]) + ); + + let l: HashMap = + HashMap::from_iter(vec![(1, Test::from(1)), (2, Test::from(2))]); + let r: HashMap = HashMap::from_iter(vec![ + (2, Test::from(5)), + (3, Test::from(3)), + (4, Test::from(4)), + ]); + assert_eq!( + l.merge_right(r), + HashMap::from_iter(vec![ + (1, Test::from(1)), + (2, Test::from(7)), + (3, Test::from(3)), + (4, Test::from(4)) + ]) + ); + } +} diff --git a/src/primitive.rs b/src/primitive.rs new file mode 100644 index 0000000..8c50cb0 --- /dev/null +++ b/src/primitive.rs @@ -0,0 +1,29 @@ +use std::marker::PhantomData; +use std::num::NonZeroU64; + +use crate::merge_right::MergeRight; + +pub trait Primitive {} + +impl Primitive for bool {} +impl Primitive for char {} +impl Primitive for f32 {} +impl Primitive for f64 {} +impl Primitive for i16 {} +impl Primitive for i32 {} +impl Primitive for i64 {} +impl Primitive for i8 {} +impl Primitive for NonZeroU64 {} +impl Primitive for String {} +impl Primitive for u16 {} +impl Primitive for u32 {} +impl Primitive for u64 {} +impl Primitive for u8 {} +impl Primitive for usize {} +impl Primitive for PhantomData {} + +impl MergeRight for A { + fn merge_right(self, other: Self) -> Self { + other + } +} diff --git a/src/proto.rs b/src/proto.rs index 45e4290..03ed445 100644 --- a/src/proto.rs +++ b/src/proto.rs @@ -29,8 +29,9 @@ mod t { let mut res = to_config(y).to_result(); match res.as_mut() { Ok(v) => { - v.package = "api:todos@1.0.0".to_string(); - println!("{}", v.to_wit()); + println!("{}", serde_json::to_string_pretty(v).unwrap()); + // v.package = "api:todos@1.0.0".to_string(); + // println!("{}", v.to_wit()); // let mut resolve = Resolve::new(); // resolve.push_str("foox.wit", &v.to_wit()).expect("TODO: panic message`"); // println!("{:#?}", resolve);