fix add_fields population
This commit is contained in:
parent
5eeb1b6002
commit
42cbcf1c7b
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -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"
|
||||
|
@ -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"]
|
||||
|
7
macros/Cargo.lock
generated
Normal file
7
macros/Cargo.lock
generated
Normal file
@ -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"
|
12
macros/Cargo.toml
Normal file
12
macros/Cargo.toml
Normal file
@ -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"
|
9
macros/src/lib.rs
Normal file
9
macros/src/lib.rs
Normal file
@ -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)
|
||||
}
|
186
macros/src/merge_right.rs
Normal file
186
macros/src/merge_right.rs
Normal file
@ -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<syn::ExprPath>,
|
||||
}
|
||||
|
||||
fn get_attrs(attrs: &[syn::Attribute]) -> syn::Result<Attrs> {
|
||||
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<Attribute> = 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<Attribute> =
|
||||
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"
|
||||
);
|
||||
}
|
||||
}
|
@ -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<Interface>,
|
||||
pub interfaces: BTreeSet<Interface>,
|
||||
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<UseStatement>,
|
||||
@ -15,47 +18,84 @@ pub struct World {
|
||||
pub exports: Vec<Interface>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)]
|
||||
pub struct Interface {
|
||||
pub name: String,
|
||||
pub records: Vec<Record>,
|
||||
pub records: BTreeSet<Record>,
|
||||
pub uses: Vec<UseStatement>,
|
||||
pub functions: Vec<Function>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
impl PartialOrd for Interface {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
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<Field>,
|
||||
pub added_fields: Vec<Field>,
|
||||
pub fields: BTreeSet<Field>,
|
||||
pub added_fields: BTreeSet<Field>,
|
||||
}
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
|
||||
impl PartialOrd for Record {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
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<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)]
|
||||
pub struct Function {
|
||||
pub name: String,
|
||||
pub parameters: Vec<Parameter>,
|
||||
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<std::cmp::Ordering> {
|
||||
Some(self.name.cmp(&other.name))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Field {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.name.cmp(&other.name)
|
||||
}
|
||||
}
|
||||
|
@ -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<Config, anyhow::Error, anyhow::Error> {
|
||||
@ -14,16 +16,22 @@ pub fn fix_args(config: Config) -> Valid<Config, anyhow::Error, anyhow::Error> {
|
||||
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::<Vec<_>>();
|
||||
config.interfaces = x;
|
||||
let x = x.into_iter().flatten().into_iter().flatten().filter_map(|x| x).collect::<Vec<_>>();
|
||||
|
||||
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<record A {}, record B {}>
|
||||
// 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::<Vec<_>>();
|
||||
// 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::<Vec<_>>();
|
||||
filtered_records.push(rec);
|
||||
|
||||
|
||||
o.records = filtered_records;
|
||||
}
|
||||
Valid::succeed(o)
|
||||
Valid::succeed(o)*/
|
||||
|
||||
/* Valid::from_iter(records.iter(), |(_, b)| {
|
||||
fix_field()
|
||||
|
@ -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<Config, Error,
|
||||
.and_then(|fields| {
|
||||
let record = Record {
|
||||
name: record_name.clone(),
|
||||
fields,
|
||||
added_fields: vec![],
|
||||
fields: fields.into_iter().collect::<BTreeSet<_>>(),
|
||||
added_fields: Default::default(),
|
||||
};
|
||||
Valid::succeed(record)
|
||||
})
|
||||
@ -37,10 +38,10 @@ pub fn handle_types(config: Config, spec: &OpenApiSpec) -> Valid<Config, Error,
|
||||
}).and_then(|records| {
|
||||
let interface = Interface {
|
||||
name: "types".to_string(),
|
||||
records,
|
||||
records: records.into_iter().collect::<BTreeSet<_>>(),
|
||||
..Default::default()
|
||||
};
|
||||
config.interfaces.push(interface);
|
||||
config.interfaces.insert(interface);
|
||||
Valid::succeed(())
|
||||
})
|
||||
}).and_then(|_| Valid::succeed(config))
|
||||
|
@ -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,
|
||||
|
@ -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};
|
||||
|
293
src/merge_right.rs
Normal file
293
src/merge_right.rs
Normal file
@ -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<A: MergeRight> MergeRight for Option<A> {
|
||||
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<A> MergeRight for Vec<A> {
|
||||
fn merge_right(mut self, other: Self) -> Self {
|
||||
self.extend(other);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> MergeRight for BTreeSet<V>
|
||||
where
|
||||
V: Ord,
|
||||
{
|
||||
fn merge_right(self, mut other: Self) -> Self {
|
||||
other.extend(self);
|
||||
other
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> MergeRight for HashSet<V>
|
||||
where
|
||||
V: Eq + std::hash::Hash,
|
||||
{
|
||||
fn merge_right(mut self, other: Self) -> Self {
|
||||
self.extend(other);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> MergeRight for BTreeMap<K, V>
|
||||
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<K, V> MergeRight for HashMap<K, V>
|
||||
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<u32> 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<Test> = 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<Test> = vec![];
|
||||
let r: Vec<Test> = vec![];
|
||||
assert_eq!(l.merge_right(r), vec![]);
|
||||
|
||||
let l: Vec<Test> = vec![Test::from(1), Test::from(2)];
|
||||
let r: Vec<Test> = vec![];
|
||||
assert_eq!(l.merge_right(r), vec![Test::from(1), Test::from(2)]);
|
||||
|
||||
let l: Vec<Test> = vec![];
|
||||
let r: Vec<Test> = vec![Test::from(3), Test::from(4)];
|
||||
assert_eq!(l.merge_right(r), vec![Test::from(3), Test::from(4)]);
|
||||
|
||||
let l: Vec<Test> = vec![Test::from(1), Test::from(2)];
|
||||
let r: Vec<Test> = 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<Test> = BTreeSet::from_iter(vec![]);
|
||||
let r: BTreeSet<Test> = BTreeSet::from_iter(vec![]);
|
||||
assert_eq!(l.merge_right(r), BTreeSet::from_iter(vec![]));
|
||||
|
||||
let l: BTreeSet<Test> = BTreeSet::from_iter(vec![Test::from(1), Test::from(2)]);
|
||||
let r: BTreeSet<Test> = BTreeSet::from_iter(vec![]);
|
||||
assert_eq!(
|
||||
l.merge_right(r),
|
||||
BTreeSet::from_iter(vec![Test::from(1), Test::from(2)])
|
||||
);
|
||||
|
||||
let l: BTreeSet<Test> = BTreeSet::from_iter(vec![]);
|
||||
let r: BTreeSet<Test> = 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<Test> = BTreeSet::from_iter(vec![Test::from(1), Test::from(2)]);
|
||||
let r: BTreeSet<Test> =
|
||||
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<Test> = HashSet::from_iter(vec![]);
|
||||
let r: HashSet<Test> = HashSet::from_iter(vec![]);
|
||||
assert_eq!(l.merge_right(r), HashSet::from_iter(vec![]));
|
||||
|
||||
let l: HashSet<Test> = HashSet::from_iter(vec![Test::from(1), Test::from(2)]);
|
||||
let r: HashSet<Test> = HashSet::from_iter(vec![]);
|
||||
assert_eq!(
|
||||
l.merge_right(r),
|
||||
HashSet::from_iter(vec![Test::from(1), Test::from(2)])
|
||||
);
|
||||
|
||||
let l: HashSet<Test> = HashSet::from_iter(vec![]);
|
||||
let r: HashSet<Test> = 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<Test> = HashSet::from_iter(vec![Test::from(1), Test::from(2)]);
|
||||
let r: HashSet<Test> =
|
||||
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<u32, Test> = BTreeMap::from_iter(vec![]);
|
||||
let r: BTreeMap<u32, Test> = BTreeMap::from_iter(vec![]);
|
||||
assert_eq!(l.merge_right(r), BTreeMap::from_iter(vec![]));
|
||||
|
||||
let l: BTreeMap<u32, Test> =
|
||||
BTreeMap::from_iter(vec![(1, Test::from(1)), (2, Test::from(2))]);
|
||||
let r: BTreeMap<u32, Test> = 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<u32, Test> = BTreeMap::from_iter(vec![]);
|
||||
let r: BTreeMap<u32, Test> =
|
||||
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<u32, Test> =
|
||||
BTreeMap::from_iter(vec![(1, Test::from(1)), (2, Test::from(2))]);
|
||||
let r: BTreeMap<u32, Test> = 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<u32, Test> = HashMap::from_iter(vec![]);
|
||||
let r: HashMap<u32, Test> = HashMap::from_iter(vec![]);
|
||||
assert_eq!(l.merge_right(r), HashMap::from_iter(vec![]));
|
||||
|
||||
let l: HashMap<u32, Test> =
|
||||
HashMap::from_iter(vec![(1, Test::from(1)), (2, Test::from(2))]);
|
||||
let r: HashMap<u32, Test> = 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<u32, Test> = HashMap::from_iter(vec![]);
|
||||
let r: HashMap<u32, Test> =
|
||||
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<u32, Test> =
|
||||
HashMap::from_iter(vec![(1, Test::from(1)), (2, Test::from(2))]);
|
||||
let r: HashMap<u32, Test> = 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))
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
29
src/primitive.rs
Normal file
29
src/primitive.rs
Normal file
@ -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<A> Primitive for PhantomData<A> {}
|
||||
|
||||
impl<A: Primitive> MergeRight for A {
|
||||
fn merge_right(self, other: Self) -> Self {
|
||||
other
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user