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",
|
"base64",
|
||||||
"convert_case",
|
"convert_case",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"macros",
|
||||||
"oapi",
|
"oapi",
|
||||||
"oas3",
|
"oas3",
|
||||||
"openapi3-parser",
|
"openapi3-parser",
|
||||||
@ -521,6 +522,15 @@ dependencies = [
|
|||||||
"logos-codegen",
|
"logos-codegen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "macros"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.90",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.4"
|
version = "2.7.4"
|
||||||
|
@ -21,3 +21,7 @@ wit-parser = "0.222.0"
|
|||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
strum_macros = "0.26.4"
|
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;
|
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 struct Config {
|
||||||
pub package: String,
|
pub package: String,
|
||||||
pub interfaces: Vec<Interface>,
|
pub interfaces: BTreeSet<Interface>,
|
||||||
pub world: World,
|
pub world: World,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)]
|
||||||
pub struct World {
|
pub struct World {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub uses: Vec<UseStatement>,
|
pub uses: Vec<UseStatement>,
|
||||||
@ -15,47 +18,84 @@ pub struct World {
|
|||||||
pub exports: Vec<Interface>,
|
pub exports: Vec<Interface>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)]
|
||||||
pub struct Interface {
|
pub struct Interface {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub records: Vec<Record>,
|
pub records: BTreeSet<Record>,
|
||||||
pub uses: Vec<UseStatement>,
|
pub uses: Vec<UseStatement>,
|
||||||
pub functions: Vec<Function>,
|
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 struct Record {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub fields: Vec<Field>,
|
pub fields: BTreeSet<Field>,
|
||||||
pub added_fields: Vec<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 struct UseStatement {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub items: Vec<String>,
|
pub items: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub parameters: Vec<Parameter>,
|
pub parameters: Vec<Parameter>,
|
||||||
pub return_type: ReturnTy,
|
pub return_type: ReturnTy,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)]
|
||||||
pub struct ReturnTy {
|
pub struct ReturnTy {
|
||||||
pub return_type: String,
|
pub return_type: String,
|
||||||
pub error_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 struct Parameter {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub parameter_type: String,
|
pub parameter_type: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, MergeRight)]
|
||||||
pub struct Field {
|
pub struct Field {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub field_type: WitType,
|
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 base64::Engine;
|
||||||
use tailcall_valid::{Valid, Validator};
|
use tailcall_valid::{Valid, Validator};
|
||||||
use crate::config::config::{Config, Field, Interface, Record};
|
use crate::config::config::{Config, Field, Interface, Record};
|
||||||
use crate::config::wit_types::WitType;
|
use crate::config::wit_types::WitType;
|
||||||
|
use crate::merge_right::MergeRight;
|
||||||
use crate::tryfold::TryFold;
|
use crate::tryfold::TryFold;
|
||||||
|
|
||||||
pub fn fix_args(config: Config) -> Valid<Config, anyhow::Error, anyhow::Error> {
|
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()
|
fix_field()
|
||||||
.try_fold(&(&config, interface, record, &field, &field.field_type), interface.clone())
|
.try_fold(&(&config, interface, record, &field, &field.field_type), interface.clone())
|
||||||
.and_then(|new_interface| {
|
.and_then(|new_interface| {
|
||||||
Valid::succeed(new_interface)
|
Valid::succeed(Some(new_interface))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Valid::succeed(interface.clone())
|
Valid::succeed(None)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}).and_then(|x| {
|
}).and_then(|x| {
|
||||||
let x = x.into_iter().flatten().into_iter().flatten().collect::<Vec<_>>();
|
let x = x.into_iter().flatten().into_iter().flatten().filter_map(|x| x).collect::<Vec<_>>();
|
||||||
config.interfaces = x;
|
|
||||||
|
let mut map = BTreeSet::new();
|
||||||
|
for interface in x {
|
||||||
|
map.insert(interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
config.interfaces = config.interfaces.merge_right(map);
|
||||||
|
|
||||||
Valid::succeed(config)
|
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) => {
|
WitType::Result(a, b) => {
|
||||||
// TODO: this impl needs check.
|
// TODO: this impl needs check.
|
||||||
// eg: -> result<record A {}, record B {}>
|
// 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() {
|
return if !a.is_primitive() {
|
||||||
fix_field().try_fold(&(*config, *interface, *rec, *field, a), o)
|
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) => {
|
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() {
|
let arg_name = if arg_name.is_empty() {
|
||||||
// TODO: maybe we can introduce some transformers
|
// TODO: maybe we can introduce some transformers
|
||||||
// which uses AI to generate better names
|
// 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);
|
let name = format!("{}-{}-{}", rec.name, field.name, arg_name);
|
||||||
|
println!("name: {}", name);
|
||||||
let new_field = Field {
|
let new_field = Field {
|
||||||
name,
|
name: name.clone(),
|
||||||
field_type: ty.clone(),
|
field_type: ty.clone(),
|
||||||
};
|
};
|
||||||
let mut rec = (*rec).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<_>>();
|
let mut filtered_records = o.records.into_iter().filter(|r| r.name == rec.name).collect::<Vec<_>>();
|
||||||
filtered_records.push(rec);
|
filtered_records.push(rec);
|
||||||
|
|
||||||
|
|
||||||
o.records = filtered_records;
|
o.records = filtered_records;
|
||||||
}
|
}
|
||||||
Valid::succeed(o)
|
Valid::succeed(o)*/
|
||||||
|
|
||||||
/* Valid::from_iter(records.iter(), |(_, b)| {
|
/* Valid::from_iter(records.iter(), |(_, b)| {
|
||||||
fix_field()
|
fix_field()
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use std::collections::BTreeSet;
|
||||||
use anyhow::{anyhow, Error};
|
use anyhow::{anyhow, Error};
|
||||||
use tailcall_valid::{Valid, Validator};
|
use tailcall_valid::{Valid, Validator};
|
||||||
use crate::config::config::{Config, Field, Interface, Record};
|
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| {
|
.and_then(|fields| {
|
||||||
let record = Record {
|
let record = Record {
|
||||||
name: record_name.clone(),
|
name: record_name.clone(),
|
||||||
fields,
|
fields: fields.into_iter().collect::<BTreeSet<_>>(),
|
||||||
added_fields: vec![],
|
added_fields: Default::default(),
|
||||||
};
|
};
|
||||||
Valid::succeed(record)
|
Valid::succeed(record)
|
||||||
})
|
})
|
||||||
@ -37,10 +38,10 @@ pub fn handle_types(config: Config, spec: &OpenApiSpec) -> Valid<Config, Error,
|
|||||||
}).and_then(|records| {
|
}).and_then(|records| {
|
||||||
let interface = Interface {
|
let interface = Interface {
|
||||||
name: "types".to_string(),
|
name: "types".to_string(),
|
||||||
records,
|
records: records.into_iter().collect::<BTreeSet<_>>(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
config.interfaces.push(interface);
|
config.interfaces.insert(interface);
|
||||||
Valid::succeed(())
|
Valid::succeed(())
|
||||||
})
|
})
|
||||||
}).and_then(|_| Valid::succeed(config))
|
}).and_then(|_| Valid::succeed(config))
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use tailcall_valid::{Valid, Validator};
|
use tailcall_valid::{Valid, Validator};
|
||||||
use crate::config::to_wit::ToWit;
|
use crate::config::to_wit::ToWit;
|
||||||
|
|
||||||
use crate::ser::{OpenApiSpec, Schema};
|
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 {
|
pub enum WitType {
|
||||||
// Primitive Types
|
// Primitive Types
|
||||||
Bool,
|
Bool,
|
||||||
|
@ -5,6 +5,8 @@ mod config;
|
|||||||
mod transformer;
|
mod transformer;
|
||||||
mod transform;
|
mod transform;
|
||||||
mod tryfold;
|
mod tryfold;
|
||||||
|
mod merge_right;
|
||||||
|
mod primitive;
|
||||||
|
|
||||||
use serde_json::{Map, Value};
|
use serde_json::{Map, Value};
|
||||||
use anyhow::{Result, bail};
|
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();
|
let mut res = to_config(y).to_result();
|
||||||
match res.as_mut() {
|
match res.as_mut() {
|
||||||
Ok(v) => {
|
Ok(v) => {
|
||||||
v.package = "api:todos@1.0.0".to_string();
|
println!("{}", serde_json::to_string_pretty(v).unwrap());
|
||||||
println!("{}", v.to_wit());
|
// v.package = "api:todos@1.0.0".to_string();
|
||||||
|
// println!("{}", v.to_wit());
|
||||||
// let mut resolve = Resolve::new();
|
// let mut resolve = Resolve::new();
|
||||||
// resolve.push_str("foox.wit", &v.to_wit()).expect("TODO: panic message`");
|
// resolve.push_str("foox.wit", &v.to_wit()).expect("TODO: panic message`");
|
||||||
// println!("{:#?}", resolve);
|
// println!("{:#?}", resolve);
|
||||||
|
Loading…
Reference in New Issue
Block a user