extern crate proc_macro;
mod args;
use quote::quote;
pub fn macro_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
use darling::FromDeriveInput;
let input = syn::parse_macro_input!(input as syn::DeriveInput);
let struct_args = args::SerdeDiffStructArgs::from_derive_input(&input).unwrap();
let parsed_fields = parse_fields(&input);
let mut ok_fields: Vec<ParsedField> = vec![];
let mut errors = vec![];
for pf in parsed_fields {
match pf {
Ok(value) => ok_fields.push(value),
Err(e) => errors.push(e),
}
}
if !errors.is_empty() {
return proc_macro::TokenStream::from(darling::Error::multiple(errors).write_errors());
}
generate(&input, struct_args, ok_fields)
}
fn parse_field(f: &syn::Field) -> Result<ParsedField, darling::Error> {
use darling::FromField;
let field_args = args::SerdeDiffFieldArgs::from_field(&f)?;
Ok(ParsedField { field_args })
}
fn parse_fields(input: &syn::DeriveInput) -> Vec<Result<ParsedField, darling::Error>> {
use syn::Data;
use syn::Fields;
match input.data {
Data::Struct(ref data) => {
match data.fields {
Fields::Named(ref fields) => fields.named.iter().map(|f| parse_field(&f)).collect(),
_ => unimplemented!(), }
}
_ => unimplemented!(), }
}
#[derive(Debug)]
struct ParsedField {
field_args: args::SerdeDiffFieldArgs,
}
fn generate(
_input: &syn::DeriveInput,
struct_args: args::SerdeDiffStructArgs,
parsed_fields: Vec<ParsedField>,
) -> proc_macro::TokenStream {
let mut diff_fn_field_handlers = vec![];
for pf in &parsed_fields {
if pf.field_args.skip() {
continue;
}
let ident = pf.field_args.ident().clone();
let ident_as_str = quote!(#ident).to_string();
let ty = pf.field_args.ty();
diff_fn_field_handlers.push(quote! {
{
{
ctx.push_field(#ident_as_str);
__changed__ |= <#ty as serde_diff::SerdeDiff>::diff(&self.#ident, ctx, &other.#ident)?;
ctx.pop_path_element()?;
}
}
});
}
let diff_fn = quote! {
fn diff<'a, S: serde_diff::_serde::ser::SerializeSeq>(&self, ctx: &mut serde_diff::DiffContext<'a, S>, other: &Self) -> Result<bool, S::Error> {
let mut __changed__ = false;
#(#diff_fn_field_handlers)*
Ok(__changed__)
}
};
let mut apply_fn_field_handlers = vec![];
for pf in &parsed_fields {
if pf.field_args.skip() {
continue;
}
let ident = pf.field_args.ident().clone();
let ident_as_str = quote!(#ident).to_string();
let ty = pf.field_args.ty();
apply_fn_field_handlers.push(quote!(
#ident_as_str => __changed__ |= <#ty as serde_diff::SerdeDiff>::apply(&mut self.#ident, seq, ctx)?,
));
}
let apply_fn = quote! {
fn apply<'de, A>(
&mut self,
seq: &mut A,
ctx: &mut serde_diff::ApplyContext,
) -> Result<bool, <A as serde_diff::_serde::de::SeqAccess<'de>>::Error>
where
A: serde_diff::_serde::de::SeqAccess<'de>, {
let mut __changed__ = false;
while let Some(serde_diff::DiffPathElementValue::Field(element)) = ctx.next_path_element(seq)? {
match element.as_ref() {
#(#apply_fn_field_handlers)*
_ => ctx.skip_value(seq)?,
}
}
Ok(__changed__)
}
};
let struct_name = &struct_args.ident;
let diff_impl = quote! {
impl SerdeDiff for #struct_name {
#diff_fn
#apply_fn
}
};
return proc_macro::TokenStream::from(quote! {
#diff_impl
});
}