diffus-derive-0.10.0/.cargo_vcs_info.json0000644000000001120000000000000136370ustar { "git": { "sha1": "0b205781c46319d5cfe709d5aead0a3a9b4a9ff7" } } diffus-derive-0.10.0/.gitignore000064400000000000000000000000360000000000000144020ustar 00000000000000/target **/*.rs.bk Cargo.lock diffus-derive-0.10.0/Cargo.toml0000644000000024700000000000000116460ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "diffus-derive" version = "0.10.0" authors = ["Jim Holmström ", "Johan Gardell <736172+gardell@users.noreply.github.com>"] publish = ["crates-io"] description = "Finds the difference between two instances of any data structure. Supports derive on structs and enums." homepage = "https://github.com/distil/diffus" documentation = "https://docs.rs/diffus-derive" readme = "../README.md" keywords = ["algorithm", "diff", "difference", "data", "data-structure"] categories = ["algorithms", "data-structures"] license = "Apache-2.0" repository = "https://github.com/distil/diffus" [lib] proc-macro = true [dependencies.proc-macro2] version = "1.0" [dependencies.quote] version = "1.0" [dependencies.syn] version = "1.0" [features] serialize-impl = [] diffus-derive-0.10.0/Cargo.toml.orig000064400000000000000000000014150000000000000153030ustar 00000000000000[package] name = "diffus-derive" version = "0.10.0" authors = [ "Jim Holmström ", "Johan Gardell <736172+gardell@users.noreply.github.com>", ] description = "Finds the difference between two instances of any data structure. Supports derive on structs and enums." homepage = "https://github.com/distil/diffus" repository = "https://github.com/distil/diffus" documentation = "https://docs.rs/diffus-derive" readme = "../README.md" keywords = [ "algorithm", "diff", "difference", "data", "data-structure" ] categories = [ "algorithms", "data-structures" ] publish = [ "crates-io" ] license = "Apache-2.0" edition = "2018" [lib] proc-macro = true [dependencies] proc-macro2 = "1.0" quote = "1.0" syn = "1.0" [features] serialize-impl = [] diffus-derive-0.10.0/src/lib.rs000064400000000000000000000316500000000000000143230ustar 00000000000000extern crate proc_macro; use quote::{format_ident, quote}; type Output = proc_macro2::TokenStream; fn edit_fields(fields: &syn::Fields, lifetime: &syn::Lifetime) -> Output { let edit_fields = fields.iter().map(|field| match field { syn::Field { ident: Some(ident), ty, vis, .. } => quote! { #vis #ident: diffus::edit::Edit<#lifetime, #ty> }, syn::Field { ident: None, ty, vis, .. } => quote! { #vis diffus::edit::Edit<#lifetime, #ty> }, }); quote! { #(#edit_fields),* } } fn field_ident(enumerated_field: (usize, &syn::Field), prefix: &str) -> syn::Ident { match enumerated_field { ( _, syn::Field { ident: Some(ident), .. }, ) => format_ident!("{}{}", prefix, ident), (i, syn::Field { ident: None, .. }) => { format_ident!("{}{}", prefix, unnamed_field_ident(i)) } } } fn field_idents(fields: &syn::Fields, prefix: &str) -> Output { let field_idents = fields .iter() .enumerate() .map(|enumerated_field| field_ident(enumerated_field, prefix)); quote! { #(#field_idents),* } } fn renamed_field_ident(enumerated_field: (usize, &syn::Field), prefix: &str) -> Output { match enumerated_field { ( _, syn::Field { ident: Some(ident), .. }, ) => { let new_ident = format_ident!("{}{}", prefix, ident); quote! { #ident: #new_ident } } (_, syn::Field { ident: None, .. }) => unreachable!(), } } fn renamed_field_idents(fields: &syn::Fields, prefix: &str) -> Output { let field_idents = fields .iter() .enumerate() .map(|enumerated_field| renamed_field_ident(enumerated_field, prefix)); quote! { #(#field_idents),* } } fn matches_all_copy(fields: &syn::Fields) -> Output { let edit_fields_copy = fields.iter().enumerate().map(|_| { quote! { diffus::edit::Edit::Copy(_) } }); quote! { ( #(#edit_fields_copy),* ) => diffus::edit::Edit::Copy(self) } } fn field_diffs(fields: &syn::Fields) -> Output { let field_diffs = fields.iter().enumerate().map(|(index, field)| { let field_name = match field { syn::Field { ident: Some(ident), .. } => quote! { #ident }, syn::Field { ident: None, .. } => { let ident = unnamed_field_name(index); quote! { #ident } } }; quote! { diffus::Diffable::diff(&self.#field_name, &other.#field_name) } }); quote! { #(#field_diffs),* } } fn unnamed_field_ident(i: usize) -> syn::Ident { format_ident!("x{}", i as u32) } fn unnamed_field_name(i: usize) -> syn::Lit { syn::parse_str(&format!("{}", i as u32)).unwrap() } fn input_lifetime(generics: &syn::Generics) -> Option<&syn::Lifetime> { let mut lifetimes = generics.params.iter().filter_map(|generic_param| { if let syn::GenericParam::Lifetime(syn::LifetimeDef { lifetime, .. }) = generic_param { Some(lifetime) } else { None } }); let lifetime = lifetimes.next(); assert!( lifetimes.next().is_none(), "Multiple lifetimes not supported yet" ); lifetime } #[proc_macro_derive(Diffus)] pub fn derive_diffus(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input: syn::DeriveInput = syn::parse2(proc_macro2::TokenStream::from(input)).unwrap(); let ident = &input.ident; let vis = &input.vis; let where_clause = &input.generics.where_clause; let edited_ident = syn::parse_str::(&format!("Edited{}", ident)).unwrap(); let data_lifetime = input_lifetime(&input.generics); let default_lifetime = syn::parse_str::("'diffus_a").unwrap(); let impl_lifetime = data_lifetime.unwrap_or(&default_lifetime); #[cfg(feature = "serialize-impl")] let derive_serialize = Some(quote! { #[derive(serde::Serialize)] }); #[cfg(not(feature = "serialize-impl"))] let derive_serialize: Option = None; proc_macro::TokenStream::from(match input.data { syn::Data::Enum(syn::DataEnum { variants, .. }) => { let edit_variants = variants.iter().map(|syn::Variant { ident, fields, .. }| { let edit_fields = edit_fields(&fields, &impl_lifetime); match fields { syn::Fields::Named(syn::FieldsNamed { .. }) => { quote! { #ident { #edit_fields } } } syn::Fields::Unnamed(syn::FieldsUnnamed { .. }) => { quote! { #ident ( #edit_fields ) } } syn::Fields::Unit => { quote! { #ident } } } }); let has_non_unit_variant = variants.iter().any(|syn::Variant { fields, .. }| { if let syn::Fields::Unit = fields { false } else { true } }); let unit_enum_impl_lifetime = if has_non_unit_variant { Some(impl_lifetime.clone()) } else { None }; let variants_matches = variants.iter().map(|syn::Variant { ident: variant_ident, fields, .. }| { let field_diffs = fields.iter().enumerate().map(|(i, field)| { let self_field_ident = field_ident((i, field), "self_"); let other_field_ident = field_ident((i, field), "other_"); quote! { #self_field_ident . diff(& #other_field_ident ) } }); let field_diffs = quote! { #(#field_diffs),* }; let matches_all_copy = matches_all_copy(&fields); let just_field_idents = field_idents(&fields, ""); let self_field_idents = field_idents(&fields, "self_"); let other_field_idents = field_idents(&fields, "other_"); match fields { syn::Fields::Named(syn::FieldsNamed { .. }) => { let self_field_idents = renamed_field_idents(&fields, "self_"); let other_field_idents = renamed_field_idents(&fields, "other_"); quote! { ( #ident::#variant_ident { #self_field_idents }, #ident::#variant_ident { #other_field_idents } ) => { match ( #field_diffs ) { #matches_all_copy, ( #just_field_idents ) => { diffus::edit::Edit::Change( diffus::edit::enm::Edit::AssociatedChanged( #edited_ident::#variant_ident { #just_field_idents } ) ) } } } } }, syn::Fields::Unnamed(syn::FieldsUnnamed { .. }) => { quote! { ( #ident::#variant_ident( #self_field_idents ), #ident::#variant_ident( #other_field_idents ) ) => { match ( #field_diffs ) { #matches_all_copy, ( #just_field_idents ) => { diffus::edit::Edit::Change( diffus::edit::enm::Edit::AssociatedChanged( #edited_ident::#variant_ident ( #just_field_idents ) ) ) } } } } }, syn::Fields::Unit => { quote! { ( #ident::#variant_ident, #ident::#variant_ident ) => { diffus::edit::Edit::Copy(self) } } }, } }); quote! { #derive_serialize #vis enum #edited_ident <#unit_enum_impl_lifetime> where #where_clause { #(#edit_variants),* } impl<#impl_lifetime> diffus::Diffable<#impl_lifetime> for #ident <#data_lifetime> where #where_clause { type Diff = diffus::edit::enm::Edit<#impl_lifetime, Self, #edited_ident <#unit_enum_impl_lifetime>>; fn diff(&#impl_lifetime self, other: &#impl_lifetime Self) -> diffus::edit::Edit<#impl_lifetime, Self> { match (self, other) { #(#variants_matches,)* (self_variant, other_variant) => diffus::edit::Edit::Change(diffus::edit::enm::Edit::VariantChanged( self_variant, other_variant )), } } } } } syn::Data::Struct(syn::DataStruct { fields, .. }) => { let edit_fields = edit_fields(&fields, &impl_lifetime); let field_diffs = field_diffs(&fields); let field_idents = field_idents(&fields, ""); let matches_all_copy = matches_all_copy(&fields); match fields { syn::Fields::Named(_) => { quote! { #derive_serialize #vis struct #edited_ident<#impl_lifetime> where #where_clause { #edit_fields } impl<#impl_lifetime> diffus::Diffable<#impl_lifetime> for #ident <#data_lifetime> where #where_clause { type Diff = #edited_ident<#impl_lifetime>; fn diff(&#impl_lifetime self, other: &#impl_lifetime Self) -> diffus::edit::Edit<#impl_lifetime, Self> { match ( #field_diffs ) { #matches_all_copy, ( #field_idents ) => diffus::edit::Edit::Change( #edited_ident { #field_idents } ) } } } } } syn::Fields::Unnamed(_) => { quote! { #derive_serialize #vis struct #edited_ident<#impl_lifetime> ( #edit_fields ) where #where_clause; impl<#impl_lifetime> diffus::Diffable<#impl_lifetime> for #ident <#data_lifetime> where #where_clause { type Diff = #edited_ident<#impl_lifetime>; fn diff(&#impl_lifetime self, other: &#impl_lifetime Self) -> diffus::edit::Edit<#impl_lifetime, Self> { match ( #field_diffs ) { #matches_all_copy, ( #field_idents ) => diffus::edit::Edit::Change( #edited_ident ( #field_idents ) ) } } } } } syn::Fields::Unit => { quote! { #derive_serialize #vis struct #edited_ident< > where #where_clause; impl<#impl_lifetime> diffus::Diffable<#impl_lifetime> for #ident< > where #where_clause { type Diff = #edited_ident; fn diff(&#impl_lifetime self, other: &#impl_lifetime Self) -> diffus::edit::Edit<#impl_lifetime, Self> { diffus::edit::Edit::Copy(self) } } } } } } syn::Data::Union(_) => panic!("union type not supported yet"), }) }