1use proc_macro::TokenStream;
2use proc_macro2::{Span, TokenStream as TokenStream2};
3use quote::quote;
4use syn::{parse_macro_input, Data, DeriveInput};
5
6use darling::{FromAttributes, FromDeriveInput};
7
8#[derive(FromAttributes)]
10#[darling(attributes(deltapatcher))]
11struct MacroFieldConfig {
12 delta_ty: syn::Type,
15}
16
17#[derive(FromDeriveInput)]
19#[darling(attributes(deltapatcher))]
20struct MacroOverallConfig {
21 delta_name: Option<syn::Ident>,
22 delta_vis: Option<syn::Visibility>,
23 delta_derive: Option<Vec<syn::Ident>>,
24}
25
26impl MacroOverallConfig {
27 fn delta_name_or_default(&self, ty_name: &syn::Ident) -> syn::Ident {
28 self.delta_name
29 .clone()
30 .unwrap_or_else(|| syn::Ident::new(&format!("{ty_name}Delta"), Span::call_site()))
31 }
32}
33
34fn has_derive_diff(ast: &DeriveInput) -> bool {
36 ast.attrs.iter().any(|attr| {
37 if attr.path().is_ident("derive") {
38 let mut found_diff = false;
39 let _ = attr.parse_nested_meta(|meta| {
40 if meta.path.is_ident("Diff") {
41 found_diff = true;
42 }
43 Ok(())
44 });
45 found_diff
46 } else {
47 false
48 }
49 })
50}
51
52#[proc_macro_derive(Diff, attributes(deltapatcher))]
54pub fn derive_diff(input: TokenStream) -> TokenStream {
55 let ast = parse_macro_input!(input as DeriveInput);
56 expand_macro(ast, true)
57}
58
59#[proc_macro_derive(Patch, attributes(deltapatcher))]
61pub fn derive_patch(input: TokenStream) -> TokenStream {
62 let ast = parse_macro_input!(input as DeriveInput);
63
64 if has_derive_diff(&ast) {
67 return TokenStream::new();
68 }
69
70 expand_macro(ast, false)
71}
72
73fn expand_macro(ast: DeriveInput, generate_diff: bool) -> TokenStream {
74 let top_level_config = MacroOverallConfig::from_derive_input(&ast).unwrap();
75
76 match ast.data {
77 Data::Enum(e) => expand_enum(ast.ident, e, top_level_config, generate_diff),
78 Data::Struct(s) => expand_struct(ast.vis, ast.ident, s, top_level_config, generate_diff),
79 Data::Union(_) => Err(syn::Error::new(
80 Span::call_site(),
81 "Unions are not supported by deltapatcher derives",
82 )),
83 }
84 .unwrap_or_else(syn::Error::into_compile_error)
85 .into()
86}
87
88fn expand_enum(
89 ident: syn::Ident,
90 e: syn::DataEnum,
91 config: MacroOverallConfig,
92 generate_diff: bool,
93) -> syn::Result<TokenStream2> {
94 let delta_name = config.delta_name_or_default(&ident);
95 let delta_derive = config
96 .delta_derive
97 .map(|vals| quote! { #[derive(#(#vals),*)] })
98 .unwrap_or(quote! {});
99 let delta_doc = format!("Autogenerated delta for enum [`{ident}`]");
100
101 struct VariantInfo {
102 ident: syn::Ident,
103 fields: syn::Fields,
104 delta_types: Vec<syn::Type>,
105 }
106
107 let mut variants: Vec<VariantInfo> = Vec::with_capacity(e.variants.len());
108 for variant in &e.variants {
109 let mut delta_types = Vec::with_capacity(variant.fields.len());
110 for field in &variant.fields {
111 if let Ok(field_config) = MacroFieldConfig::from_attributes(&field.attrs) {
112 delta_types.push(field_config.delta_ty);
113 } else {
114 return Err(syn::Error::new_spanned(
115 field,
116 "Cannot infer delta type. Field must be annotated with #[deltapatcher(delta_ty = SomeDelta)].",
117 ));
118 }
119 }
120 variants.push(VariantInfo {
121 ident: variant.ident.clone(),
122 fields: variant.fields.clone(),
123 delta_types,
124 });
125 }
126
127 let delta_variant_defs = variants.iter().map(|v| {
129 let vname = &v.ident;
130 let dtypes = &v.delta_types;
131 match &v.fields {
132 syn::Fields::Named(n) => {
133 let names: Vec<_> = n.named.iter().map(|f| f.ident.clone().unwrap()).collect();
134 quote! { #vname { #(#names: #dtypes),* } }
135 }
136 syn::Fields::Unnamed(_) => quote! { #vname(#(#dtypes),*) },
137 syn::Fields::Unit => quote! { #vname },
138 }
139 });
140
141 let diff_impl = if generate_diff {
143 let diff_arms = variants.iter().map(|v| {
144 let vname = &v.ident;
145 let ftypes = v.fields.iter().map(|f| f.ty.clone()).collect::<Vec<_>>();
146 let dtypes = &v.delta_types;
147 match &v.fields {
148 syn::Fields::Named(n) => {
149 let names: Vec<_> = n.named.iter().map(|f| f.ident.clone().unwrap()).collect();
150 let s_names: Vec<_> = names
151 .iter()
152 .map(|n| syn::Ident::new(&format!("s_{n}"), Span::call_site()))
153 .collect();
154 let i_names: Vec<_> = names
155 .iter()
156 .map(|n| syn::Ident::new(&format!("i_{n}"), Span::call_site()))
157 .collect();
158 quote! {
159 (#ident::#vname { #(#names: #s_names),* }, #ident::#vname { #(#names: #i_names),* }) =>
160 #delta_name::#vname {
161 #(#names: <#ftypes as deltapatcher::Diff<#dtypes>>::diff(#s_names, #i_names)),*
162 }
163 }
164 }
165 syn::Fields::Unnamed(un) => {
166 let count = un.unnamed.len();
167 let s_ids: Vec<_> = (0..count)
168 .map(|i| syn::Ident::new(&format!("s_{i}"), Span::call_site()))
169 .collect();
170 let i_ids: Vec<_> = (0..count)
171 .map(|i| syn::Ident::new(&format!("i_{i}"), Span::call_site()))
172 .collect();
173 quote! {
174 (#ident::#vname(#(#s_ids),*), #ident::#vname(#(#i_ids),*)) =>
175 #delta_name::#vname(
176 #(<#ftypes as deltapatcher::Diff<#dtypes>>::diff(#s_ids, #i_ids)),*
177 )
178 }
179 }
180 syn::Fields::Unit => quote! {
181 (#ident::#vname, #ident::#vname) => #delta_name::#vname
182 },
183 }
184 });
185
186 quote! {
187 impl deltapatcher::Diff<#delta_name> for #ident {
188 fn diff(&self, initial: &Self) -> #delta_name {
189 match (self, initial) {
190 #(#diff_arms,)*
191 _ => #delta_name::Replace(self.clone()),
192 }
193 }
194 }
195 }
196 } else {
197 quote! {}
198 };
199
200 let patch_arms = variants.iter().map(|v| {
202 let vname = &v.ident;
203 let ftypes = v.fields.iter().map(|f| f.ty.clone()).collect::<Vec<_>>();
204 let dtypes = &v.delta_types;
205 match &v.fields {
206 syn::Fields::Named(n) => {
207 let names: Vec<_> = n.named.iter().map(|f| f.ident.clone().unwrap()).collect();
208 let d_names: Vec<_> = names
209 .iter()
210 .map(|n| syn::Ident::new(&format!("d_{n}"), Span::call_site()))
211 .collect();
212 quote! {
213 (#ident::#vname { #(#names),* }, #delta_name::#vname { #(#names: #d_names),* }) => {
214 #(<#ftypes as deltapatcher::Patch<#dtypes>>::patch(#names, #d_names);)*
215 }
216 }
217 }
218 syn::Fields::Unnamed(un) => {
219 let count = un.unnamed.len();
220 let s_ids: Vec<_> = (0..count)
221 .map(|i| syn::Ident::new(&format!("s_{i}"), Span::call_site()))
222 .collect();
223 let d_ids: Vec<_> = (0..count)
224 .map(|i| syn::Ident::new(&format!("d_{i}"), Span::call_site()))
225 .collect();
226 quote! {
227 (#ident::#vname(#(#s_ids),*), #delta_name::#vname(#(#d_ids),*)) => {
228 #(<#ftypes as deltapatcher::Patch<#dtypes>>::patch(#s_ids, #d_ids);)*
229 }
230 }
231 }
232 syn::Fields::Unit => quote! {
233 (#ident::#vname, #delta_name::#vname) => {}
234 },
235 }
236 });
237
238 let aggregate_arms = variants.iter().map(|v| {
240 let vname = &v.ident;
241 let dtypes = &v.delta_types;
242 match &v.fields {
243 syn::Fields::Named(n) => {
244 let names: Vec<_> = n.named.iter().map(|f| f.ident.clone().unwrap()).collect();
245 let d_names: Vec<_> = names
246 .iter()
247 .map(|n| syn::Ident::new(&format!("d_{n}"), Span::call_site()))
248 .collect();
249 quote! {
250 (Self::#vname { #(#names),* }, Self::#vname { #(#names: #d_names),* }) => {
251 #(<#dtypes as deltapatcher::delta::Delta>::aggregate(#names, #d_names);)*
252 }
253 }
254 }
255 syn::Fields::Unnamed(un) => {
256 let count = un.unnamed.len();
257 let s_ids: Vec<_> = (0..count)
258 .map(|i| syn::Ident::new(&format!("s_{i}"), Span::call_site()))
259 .collect();
260 let d_ids: Vec<_> = (0..count)
261 .map(|i| syn::Ident::new(&format!("d_{i}"), Span::call_site()))
262 .collect();
263 quote! {
264 (Self::#vname(#(#s_ids),*), Self::#vname(#(#d_ids),*)) => {
265 #(<#dtypes as deltapatcher::delta::Delta>::aggregate(#s_ids, #d_ids);)*
266 }
267 }
268 }
269 syn::Fields::Unit => quote! {
270 (Self::#vname, Self::#vname) => {}
271 },
272 }
273 });
274
275 let aggregate_owned_arms = variants.iter().map(|v| {
277 let vname = &v.ident;
278 let dtypes = &v.delta_types;
279 match &v.fields {
280 syn::Fields::Named(n) => {
281 let names: Vec<_> = n.named.iter().map(|f| f.ident.clone().unwrap()).collect();
282 let d_names: Vec<_> = names
283 .iter()
284 .map(|n| syn::Ident::new(&format!("d_{n}"), Span::call_site()))
285 .collect();
286 quote! {
287 (Self::#vname { #(#names),* }, Self::#vname { #(#names: #d_names),* }) => {
288 #(<#dtypes as deltapatcher::delta::Delta>::aggregate_owned(#names, #d_names);)*
289 }
290 }
291 }
292 syn::Fields::Unnamed(un) => {
293 let count = un.unnamed.len();
294 let s_ids: Vec<_> = (0..count)
295 .map(|i| syn::Ident::new(&format!("s_{i}"), Span::call_site()))
296 .collect();
297 let d_ids: Vec<_> = (0..count)
298 .map(|i| syn::Ident::new(&format!("d_{i}"), Span::call_site()))
299 .collect();
300 quote! {
301 (Self::#vname(#(#s_ids),*), Self::#vname(#(#d_ids),*)) => {
302 #(<#dtypes as deltapatcher::delta::Delta>::aggregate_owned(#s_ids, #d_ids);)*
303 }
304 }
305 }
306 syn::Fields::Unit => quote! {
307 (Self::#vname, Self::#vname) => {}
308 },
309 }
310 });
311
312 Ok(quote! {
313 #[doc = #delta_doc]
314 #delta_derive
315 #[allow(dead_code)]
316 enum #delta_name {
317 #(#delta_variant_defs,)*
318 Replace(#ident),
320 }
321
322 impl deltapatcher::delta::Delta for #delta_name {
323 fn aggregate(&mut self, next: &Self) {
324 match (self, next) {
325 (slf, Self::Replace(val)) => *slf = Self::Replace(val.clone()),
326 (Self::Replace(current), next) => {
327 <#ident as deltapatcher::Patch<#delta_name>>::patch(current, next);
328 },
329 #(#aggregate_arms,)*
330 _ => unreachable!("delta variants disagree without a Replace — this is a bug"),
331 }
332 }
333
334 fn aggregate_owned(&mut self, next: Self) {
335 match (self, next) {
336 (slf, Self::Replace(val)) => *slf = Self::Replace(val),
337 (Self::Replace(current), next) => {
338 <#ident as deltapatcher::Patch<#delta_name>>::patch(current, &next);
339 },
340 #(#aggregate_owned_arms,)*
341 _ => unreachable!("delta variants disagree without a Replace — this is a bug"),
342 }
343 }
344 }
345
346 impl deltapatcher::Patch<#delta_name> for #ident {
347 fn patch(&mut self, delta: &#delta_name) {
348 match (self, delta) {
349 #(#patch_arms,)*
350 (slf, #delta_name::Replace(val)) => *slf = val.clone(),
351 _ => unreachable!("mismatched non-Replace delta variant applied to wrong enum variant — this is a bug"),
352 }
353 }
354 }
355
356 #diff_impl
357 })
358}
359
360fn expand_struct(
361 vis: syn::Visibility,
362 ident: syn::Ident,
363 s: syn::DataStruct,
364 config: MacroOverallConfig,
365 generate_diff: bool,
366) -> syn::Result<TokenStream2> {
367 let mut delta_field_type = Vec::with_capacity(s.fields.len());
368 for field in &s.fields {
369 if let Ok(field_config) = MacroFieldConfig::from_attributes(&field.attrs) {
370 delta_field_type.push(field_config.delta_ty);
371 } else {
372 return Err(syn::Error::new_spanned(
373 field,
374 "Cannot infer delta type. Field must be annotated with #[deltapatcher(delta_ty = SomeDelta)].",
375 ));
376 }
377 }
378
379 let delta_name = config.delta_name_or_default(&ident);
380 let delta_derive = config
381 .delta_derive
382 .map(|vals| quote! { #[derive(#(#vals),*)] })
383 .unwrap_or(quote! {});
384 let delta_vis = config.delta_vis.unwrap_or(vis);
385 let delta_doc = format!("Autogenerated delta for struct [`{ident}`]");
386
387 Ok(match s.fields {
388 syn::Fields::Named(n) => {
389 let field_vis = n.named.iter().map(|field| field.vis.clone());
390 let field_name = n
391 .named
392 .iter()
393 .map(|f| f.ident.clone().unwrap())
394 .collect::<Vec<_>>();
395 let field_type = n.named.iter().map(|f| f.ty.clone()).collect::<Vec<_>>();
396
397 let diff_impl = if generate_diff {
398 quote! {
399 impl deltapatcher::Diff<#delta_name> for #ident {
400 fn diff(&self, initial: &Self) -> #delta_name {
401 #delta_name {
402 #(#field_name: <#field_type as deltapatcher::Diff<#delta_field_type>>::diff(&self.#field_name, &initial.#field_name)),*
403 }
404 }
405 }
406 }
407 } else { quote! {} };
408
409 quote! {
410 #[doc = #delta_doc]
411 #delta_derive
412 #delta_vis struct #delta_name {
413 #(#field_vis #field_name: #delta_field_type),*
414 }
415
416 impl deltapatcher::delta::Delta for #delta_name {
417 fn aggregate(&mut self, next: &Self) {
418 #(<#delta_field_type as deltapatcher::delta::Delta>::aggregate(&mut self.#field_name, &next.#field_name);)*
419 }
420
421 fn aggregate_owned(&mut self, next: Self) {
422 #(<#delta_field_type as deltapatcher::delta::Delta>::aggregate_owned(&mut self.#field_name, next.#field_name);)*
423 }
424 }
425
426 impl deltapatcher::Patch<#delta_name> for #ident {
427 fn patch(&mut self, delta: &#delta_name) {
428 #(<#field_type as deltapatcher::Patch<#delta_field_type>>::patch(&mut self.#field_name, &delta.#field_name);)*
429 }
430 }
431
432 #diff_impl
433 }
434 }
435 syn::Fields::Unnamed(un) => {
436 let field_vis = un.unnamed.iter().map(|field| field.vis.clone());
437 let field_idx = (0..un.unnamed.len()).collect::<Vec<_>>();
438 let field_type = un.unnamed.iter().map(|f| f.ty.clone()).collect::<Vec<_>>();
439
440 let diff_impl = if generate_diff {
441 quote! {
442 impl deltapatcher::Diff<#delta_name> for #ident {
443 fn diff(&self, initial: &Self) -> #delta_name {
444 #delta_name (#(<#field_type as deltapatcher::Diff<#delta_field_type>>::diff(&self.#field_idx, &initial.#field_idx)),*)
445 }
446 }
447 }
448 } else { quote! {} };
449
450 quote! {
451 #[doc = #delta_doc]
452 #delta_derive
453 #delta_vis struct #delta_name(#(#field_vis #delta_field_type),*);
454
455 impl deltapatcher::delta::Delta for #delta_name {
456 fn aggregate(&mut self, next: &Self) {
457 #(<#delta_field_type as deltapatcher::delta::Delta>::aggregate(&mut self.#field_idx, &next.#field_idx);)*
458 }
459
460 fn aggregate_owned(&mut self, next: Self) {
461 #(<#delta_field_type as deltapatcher::delta::Delta>::aggregate_owned(&mut self.#field_idx, next.#field_idx);)*
462 }
463 }
464
465 impl deltapatcher::Patch<#delta_name> for #ident {
466 fn patch(&mut self, delta: &#delta_name) {
467 #(<#field_type as deltapatcher::Patch<#delta_field_type>>::patch(&mut self.#field_idx, &delta.#field_idx);)*
468 }
469 }
470
471 #diff_impl
472 }
473 }
474 syn::Fields::Unit => {
475 let diff_impl = if generate_diff {
476 quote! {
477 impl deltapatcher::Diff<#delta_name> for #ident {
478 fn diff(&self, _initial: &Self) -> #delta_name { #delta_name }
479 }
480 }
481 } else { quote! {} };
482
483 quote! {
484 #[doc = #delta_doc]
485 #delta_derive
486 #delta_vis struct #delta_name;
487
488 impl deltapatcher::delta::Delta for #delta_name {
489 fn aggregate(&mut self, _next: &Self) {}
490 fn aggregate_owned(&mut self, _next: Self) {}
491 }
492
493 impl deltapatcher::Patch<#delta_name> for #ident {
494 fn patch(&mut self, _delta: &#delta_name) {}
495 }
496
497 #diff_impl
498 }
499 }
500 })
501}