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#[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 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 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 let struct_dipa_impl = match struct_data.fields {
93 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 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 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}