use proc_macro::TokenStream;
use quote::quote;
use syn::{Data, Fields};
#[proc_macro_derive(MValueCompatible)]
pub fn mvalue_compatible_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
generate_to_mvalue(&ast)
}
fn generate_to_mvalue(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let data = &ast.data;
let fields = match data {
Data::Struct(data_struct) => &data_struct.fields,
_ => panic!("Unsupported data type"),
};
let (from_mvalue, into_mvalue) = generate_conversions(fields);
let gen = quote! {
impl From<s2json_core::MValue> for #name {
fn from(mut m: s2json_core::MValue) -> Self {
#from_mvalue
}
}
impl From<s2json_core::ValueType> for #name {
fn from(value: s2json_core::ValueType) -> Self {
match value {
s2json_core::ValueType::Nested(v) => v.into(),
_ => #name::default(),
}
}
}
impl From<#name> for s2json_core::MValue {
fn from(value: #name) -> s2json_core::MValue {
#into_mvalue
}
}
impl From<#name> for s2json_core::ValueType {
fn from(value: #name) -> s2json_core::ValueType {
s2json_core::ValueType::Nested(value.into())
}
}
};
gen.into()
}
fn generate_conversions(fields: &Fields) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
let mut from_assignments = vec![];
let mut into_insertions = vec![];
for field in fields.iter() {
let field_name = field.ident.as_ref().unwrap();
let field_str = field_name.to_string();
from_assignments.push(quote! {
#field_name: m.remove(#field_str).map(Into::into).unwrap_or_default()
});
into_insertions.push(quote! {
map.insert(#field_str.to_string(), value.#field_name.into());
});
}
let from_mvalue = quote! {
Self {
#(#from_assignments),*
}
};
let into_mvalue = quote! {
let mut map = MValue::new();
#(#into_insertions)*
map
};
(from_mvalue, into_mvalue)
}