dipa_derive/
lib.rs

1use crate::dipa_attribute::{maybe_parse_raw_dipa_attribute, DipaAttrs};
2use crate::multi_field_utils::{
3    fields_named_to_vec_fields, fields_unnamed_to_vec_fields, ParsedFields,
4};
5use crate::multi_variant_enum::generate_multi_variant_enum_impl;
6use crate::parsed_enum::{EnumVariant, EnumVariantFields, ParsedEnum};
7use crate::parsed_struct::ParsedStruct;
8use crate::single_field_struct::generate_single_field_struct_impl;
9use crate::single_variant_enum::{
10    generate_single_variant_enum_single_struct_field_impl,
11    generate_single_variant_enum_single_tuple_field_impl,
12};
13use crate::zst_impl::create_zst_impl;
14use proc_macro::TokenStream;
15use quote::quote;
16use syn::__private::TokenStream2;
17use syn::spanned::Spanned;
18use syn::{parse_macro_input, Data, DeriveInput, Fields};
19use syn::{Error as SynError, Result as SynResult};
20
21#[macro_use]
22extern crate quote;
23
24#[macro_use]
25extern crate syn;
26
27mod multi_variant_enum;
28mod single_field_struct;
29mod single_variant_enum;
30mod zst_impl;
31
32mod dipa_attribute;
33
34mod multi_field_utils;
35mod parsed_enum;
36mod parsed_struct;
37
38#[cfg(test)]
39mod test_utils;
40
41/// #[derive(DiffPatch)]
42// cargo test -p dipa-derive      # Unit tests of the macro's implementation
43// cargo test -p dipa-derive-test # Testing real usage of the macro
44#[proc_macro_derive(DiffPatch, attributes(dipa))]
45pub fn derive_diff_patch(input: TokenStream) -> TokenStream {
46    let input = parse_macro_input!(input as DeriveInput);
47
48    let dipa_attrs = match maybe_parse_raw_dipa_attribute(input.attrs) {
49        Some(attrib) => {
50            let attrib_tokens = attrib.tokens.into();
51
52            Some(parse_macro_input!(attrib_tokens as DipaAttrs))
53        }
54        None => None,
55    }
56    .unwrap_or(DipaAttrs::default());
57
58    let enum_or_struct_name = input.ident;
59
60    let zero_sized_diff = create_zst_impl(&enum_or_struct_name);
61
62    // Generate:
63    // impl<'p, Other> Diffable<'s, 'e, Other> for MyType { ... }
64    // impl Patchable<Patch> for MyType { ... }
65    let dipa_impl = match input.data {
66        Data::Struct(struct_data) => {
67            let fields = match &struct_data.fields {
68                Fields::Named(named_fields) => ParsedFields {
69                    fields: fields_named_to_vec_fields(named_fields),
70                    span: named_fields.span(),
71                },
72                Fields::Unnamed(unnamed_fields) => ParsedFields {
73                    fields: fields_unnamed_to_vec_fields(unnamed_fields),
74                    span: unnamed_fields.span(),
75                },
76                Fields::Unit => ParsedFields {
77                    fields: vec![],
78                    span: enum_or_struct_name.span(),
79                },
80            };
81            let parsed_struct = ParsedStruct {
82                // FIXME: Remove clone once we move the logic below into generate_dipa_impl()
83                name: enum_or_struct_name.clone(),
84                fields,
85            };
86
87            if let Err(err) = parsed_struct.validate_struct_container_attributes(&dipa_attrs) {
88                return err.into();
89            }
90
91            // TODO: Move this logic into ParsedStruct.generate_dipa_impl()
92            let struct_dipa_impl = match struct_data.fields {
93                // struct Foo { field_a: type1, field_b: type2, ... }
94                Fields::Named(fields) => {
95                    if fields.named.len() == 0 {
96                        zero_sized_diff
97                    } else if fields.named.len() == 1 {
98                        let field = &fields.named[0];
99                        let field_name = field.ident.as_ref().unwrap();
100
101                        generate_single_field_struct_impl(
102                            &enum_or_struct_name,
103                            quote_spanned! {field.span() => #field_name},
104                            &field.ty,
105                        )
106                    } else {
107                        parsed_struct.generate_multi_field_struct_impl(&dipa_attrs)
108                    }
109                }
110                // struct Foo(type1, type2);
111                Fields::Unnamed(fields) => {
112                    if fields.unnamed.len() == 0 {
113                        zero_sized_diff
114                    } else if fields.unnamed.len() == 1 {
115                        generate_single_field_struct_impl(
116                            &enum_or_struct_name,
117                            quote_spanned! {fields.unnamed[0].span() => 0},
118                            &fields.unnamed[0].ty,
119                        )
120                    } else {
121                        parsed_struct.generate_multi_field_struct_impl(&dipa_attrs)
122                    }
123                }
124                // struct Foo;
125                Fields::Unit => zero_sized_diff,
126            };
127
128            struct_dipa_impl
129        }
130        Data::Enum(enum_data) => {
131            let variants = enum_data
132                .variants
133                .iter()
134                .map(|v| {
135                    let fields = match &v.fields {
136                        Fields::Named(named_fields) => EnumVariantFields::Struct(ParsedFields {
137                            fields: fields_named_to_vec_fields(named_fields),
138                            span: named_fields.span(),
139                        }),
140                        Fields::Unnamed(unnamed_fields) => EnumVariantFields::Tuple(ParsedFields {
141                            fields: fields_unnamed_to_vec_fields(unnamed_fields),
142                            span: unnamed_fields.span(),
143                        }),
144                        Fields::Unit => EnumVariantFields::Unit,
145                    };
146
147                    EnumVariant {
148                        name: v.ident.clone(),
149                        fields,
150                    }
151                })
152                .collect();
153            let parsed_enum = ParsedEnum {
154                name: enum_or_struct_name.clone(),
155                variants,
156            };
157
158            if enum_data.variants.len() == 0 {
159                zero_sized_diff
160            } else if enum_data.variants.len() == 1 {
161                let variant = &enum_data.variants[0];
162
163                let fields = &variant.fields;
164
165                if fields.len() == 0 {
166                    zero_sized_diff
167                } else {
168                    match &fields {
169                        Fields::Named(fields) => {
170                            if fields.named.len() == 1 {
171                                let field = &fields.named[0];
172                                let field_name = field.ident.as_ref().unwrap();
173
174                                generate_single_variant_enum_single_struct_field_impl(
175                                    enum_or_struct_name,
176                                    &variant.ident,
177                                    quote_spanned! {field.span() => #field_name},
178                                    &field.ty,
179                                )
180                            } else {
181                                parsed_enum
182                                    .generate_single_variant_multi_field_dipa_impl(&dipa_attrs)
183                            }
184                        }
185                        Fields::Unnamed(fields) => {
186                            if fields.unnamed.len() == 1 {
187                                let field = &fields.unnamed[0];
188
189                                generate_single_variant_enum_single_tuple_field_impl(
190                                    enum_or_struct_name,
191                                    &variant.ident,
192                                    &field.ty,
193                                )
194                            } else {
195                                parsed_enum
196                                    .generate_single_variant_multi_field_dipa_impl(&dipa_attrs)
197                            }
198                        }
199                        Fields::Unit => {
200                            unimplemented!()
201                        }
202                    }
203                }
204            } else {
205                generate_multi_variant_enum_impl(
206                    enum_or_struct_name,
207                    enum_data.variants,
208                    dipa_attrs,
209                )
210            }
211        }
212        Data::Union(_) => unimplemented!(),
213    };
214
215    let expanded = quote! {
216    #dipa_impl
217    };
218
219    TokenStream::from(expanded)
220}
221
222fn impl_dipa(
223    enum_or_struct_name: &syn::Ident,
224    delta_type: TokenStream2,
225    delta_owned_type: TokenStream2,
226    create_delta_inner: TokenStream2,
227    apply_patch_inner: TokenStream2,
228) -> TokenStream2 {
229    let tokens = quote! {
230     impl<'s, 'e> dipa::Diffable<'s, 'e, #enum_or_struct_name> for #enum_or_struct_name {
231        type Delta = #delta_type;
232
233        type DeltaOwned = #delta_owned_type;
234
235        fn create_delta_towards (&'s self, end_state: &'e #enum_or_struct_name)
236          -> dipa::CreatedDelta<Self::Delta> {
237            #create_delta_inner
238        }
239     }
240
241     impl<'s, 'e> dipa::Patchable<#delta_owned_type> for #enum_or_struct_name {
242        fn apply_patch (&mut self, patch: #delta_owned_type) {
243            #apply_patch_inner
244        }
245     }
246    };
247
248    tokens
249}