delta_struct_macros/lib.rs
1//! Procedural macro implementation behind the `delta-struct` crate.
2//!
3//! Use [`delta-struct`](https://docs.rs/delta-struct) rather than depending on
4//! this crate directly; it re-exports the [`Delta`] derive alongside the trait
5//! the derive generates an implementation of, and carries the user-facing
6//! documentation.
7
8extern crate proc_macro;
9
10use proc_macro::TokenStream;
11use proc_macro2::{Span, TokenStream as TokenStream2, TokenTree};
12use proc_macro_error::{abort_call_site, proc_macro_error};
13use quote::{format_ident, quote, ToTokens};
14use std::{iter::FromIterator, str::FromStr};
15use syn::{
16 parse_macro_input, punctuated::Punctuated, Attribute, Data, DeriveInput, Fields, Ident, Lit,
17 Meta, MetaList, MetaNameValue, NestedMeta, Path, PredicateType, Token, TraitBound,
18 TraitBoundModifier, Type, TypeParamBound, WherePredicate,
19};
20
21/// How a single field is diffed, and therefore how it is represented on the
22/// generated delta struct.
23#[derive(Copy, Clone, Debug, Eq, PartialEq)]
24enum FieldType {
25 /// A positional diff: the delta is a Myers edit script over the sequence.
26 Ordered,
27 /// A bag of items: the delta records additions and removals, not order.
28 /// Its shape is the collection's to choose, through `Unordered`, since a
29 /// map can name a departing entry by key where a set cannot.
30 Unordered,
31 /// A bag of key/value entries: like [`FieldType::Unordered`], except that
32 /// entries sharing a key are diffed with the value's own `Delta` rather
33 /// than recorded as a removal plus an addition.
34 UnorderedDelta,
35 /// Compared with `!=` and replaced wholesale.
36 Scalar,
37 /// Diffed recursively via the field type's own `Delta` implementation.
38 Delta,
39}
40
41const VALID_FIELD_TYPES: &str =
42 "\"ordered\", \"unordered\", \"unordered-delta\", \"delta\", or \"scalar\"";
43
44/// One field of the source struct, as the code generators want it: its name
45/// (or, for a tuple struct, its index), its declared type, how it is diffed,
46/// and the tokens to emit above the field it turns into.
47type Field = (String, Type, FieldType, String);
48
49/// One field as it comes back from attribute parsing, before the container's
50/// `default` has been used to fill in a missing `field_type`.
51type ParsedField = (String, Type, ParsedAttrs);
52
53/// The `(field type, delta_leader)` pair a single `#[delta_struct(...)]`
54/// yields, or the reason it could not be read.
55type ParsedAttrs = Result<(Option<FieldType>, String), FieldTypeError>;
56
57/// Derives `Delta`, generating a `{Self}Delta` struct that holds only the
58/// changed parts of a value plus the trait implementation that produces and
59/// applies one.
60///
61/// The generated type takes the visibility and generic parameters of the type
62/// it is derived on, and mirrors its shape: a tuple struct's delta is a tuple
63/// struct with its fields in the same positions, and an enum's is an enum with
64/// one variant per *diffable* source variant. A struct's delta fields are all
65/// `pub`.
66///
67/// For an enum, `Output` is `EnumDelta<Self, {Self}Delta>` rather than the
68/// bare companion, because a value can change variant as well as change within
69/// one — and changing variant is a replacement rather than a difference.
70///
71/// See the [`delta-struct`](https://docs.rs/delta-struct) crate documentation
72/// for the full picture, including trait bounds, serde usage, and limitations;
73/// what follows is the attribute reference.
74///
75/// # Container attributes
76///
77/// | Attribute | Effect |
78/// | --- | --- |
79/// | `default = "<field type>"` | Field type for fields that don't specify one. Defaults to `"scalar"`. |
80/// | `delta_leader = "<tokens>"` | Tokens emitted directly above the generated struct — derives, doc comments, anything. |
81///
82/// # Field attributes
83///
84/// | Attribute | Effect |
85/// | --- | --- |
86/// | `field_type = "<field type>"` | How this field is diffed. Overrides the container's `default`. |
87/// | `delta_leader = "<tokens>"` | Tokens emitted directly above the generated field. |
88///
89/// # Field types
90///
91/// Each maps one source field onto exactly one delta field.
92///
93/// | Value | Delta representation | Requires |
94/// | --- | --- | --- |
95/// | `"scalar"` | `Option<T>` | `T: PartialEq` |
96/// | `"unordered"` | `<T as Unordered>::Delta` — `BagDelta<Item>` for a set, `EntryDelta<Key, Value>` for a map | `T: Unordered` |
97/// | `"unordered-delta"` | `MapDelta<Key, Value, <Value as Delta>::Output>`, an `add`, a `remove`, and a `change` | `T: IntoIterator + Extend<Item> + TryIndexMut<Key, Output = Value> Item: MapEntry` (so `(K, V)`), `Value: Delta` |
98/// | `"ordered"` | `SeqDelta<Item>`, a Myers edit script | `T: IntoIterator + FromIterator<Item>`, `Item: Hash + Eq` |
99/// | `"delta"` | `Option<<T as Delta>::Output>` | `T: Delta` |
100///
101/// # Example
102///
103/// ```ignore
104/// use delta_struct::Delta;
105///
106/// #[derive(Delta)]
107/// #[delta_struct(delta_leader = "#[derive(Debug)]")]
108/// struct Device {
109/// #[delta_struct(field_type = "unordered")]
110/// services: std::collections::HashSet<String>,
111/// online: bool,
112/// }
113/// ```
114#[proc_macro_derive(Delta, attributes(delta_struct))]
115#[proc_macro_error]
116pub fn derive_delta(input: TokenStream) -> TokenStream {
117 let DeriveInput {
118 attrs,
119 vis,
120 ident,
121 mut generics,
122 data,
123 } = parse_macro_input!(input as DeriveInput);
124 let (default_field_type, delta_leader) =
125 match get_fieldtype_from_attrs(attrs.into_iter(), "default") {
126 Ok((v, delta_leader)) => (v.unwrap_or(FieldType::Scalar), delta_leader),
127 Err(_) => {
128 abort_call_site!(
129 "delta_struct(default = ...) for {} is not an accepted value, expected {}.",
130 ident,
131 VALID_FIELD_TYPES
132 );
133 }
134 };
135
136 let delta_leader = match proc_macro2::TokenStream::from_str(&delta_leader) {
137 Ok(v) => v,
138 Err(e) => {
139 abort_call_site!("error parsing delta leader as token stream {}", e);
140 }
141 };
142 let delta_ident = format_ident!("{}Delta", ident);
143 // The delta type repeats the source type's generics verbatim, bounds and
144 // all, since its fields can project through them — `<T as Delta>::Output`
145 // for a delta field, `<T as IntoIterator>::Item` for an unordered one. Grab
146 // the where clause before the `PartialEq` predicates below are pushed onto
147 // it; those are the impl's business, not the type's.
148 let og_where_clause = generics.where_clause.clone();
149 let ty_generics_only = generics.split_for_impl().1.to_token_stream();
150
151 let Generated {
152 delta_type,
153 output_ty,
154 delta_body,
155 apply_body,
156 } = match data {
157 Data::Struct(strukt) => struct_impl(
158 &ident,
159 &vis,
160 &delta_ident,
161 &delta_leader,
162 &generics,
163 &og_where_clause,
164 &ty_generics_only,
165 strukt.fields,
166 default_field_type,
167 ),
168 Data::Enum(enom) => enum_impl(
169 &ident,
170 &vis,
171 &delta_ident,
172 &delta_leader,
173 &generics,
174 &og_where_clause,
175 &ty_generics_only,
176 enom.variants.into_iter().collect(),
177 default_field_type,
178 ),
179 Data::Union(_) => {
180 abort_call_site!(
181 "delta_struct::Delta may only be derived for struct and enum types. {} is a union.",
182 ident
183 )
184 }
185 };
186 // Scalar and unordered fields compare values with `==`, so every type
187 // parameter picks up a `PartialEq` bound on the impl. This is broader than
188 // strictly necessary — a parameter used only by a `delta` field does not
189 // need it.
190 let partial_eq_types = generics
191 .type_params()
192 .map(|t| t.ident.clone())
193 .collect::<Vec<_>>();
194 let where_clause = generics.make_where_clause();
195 for ty in partial_eq_types {
196 let mut bounds = Punctuated::new();
197 let mut segments = Punctuated::new();
198 segments.push(Ident::new("std", Span::call_site()).into());
199 segments.push(Ident::new("cmp", Span::call_site()).into());
200 segments.push(Ident::new("PartialEq", Span::call_site()).into());
201 bounds.push(TypeParamBound::Trait(TraitBound {
202 paren_token: None,
203 modifier: TraitBoundModifier::None,
204 lifetimes: None,
205 path: Path {
206 leading_colon: Some(Token!(::)(Span::call_site())),
207 segments,
208 },
209 }));
210 where_clause
211 .predicates
212 .push(WherePredicate::Type(PredicateType {
213 lifetimes: None,
214 bounded_ty: Type::Verbatim(<Ident as Into<TokenTree>>::into(ty).into()),
215 colon_token: Token!(:)(Span::call_site()),
216 bounds,
217 }));
218 }
219 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
220 let delta_impl = quote! {
221 impl #impl_generics Delta for #ident #ty_generics #where_clause {
222 // `ty_generics` and not `generics`: the latter renders parameter
223 // bounds too, which are not allowed in a type position.
224 type Output = #output_ty;
225
226 fn delta(old: Self, new: Self) -> Option<Self::Output> {
227 #delta_body
228 }
229
230 // A one-variant enum has no mismatch to catch, and an enum of only
231 // unit variants has an uninhabited delta, which makes the tail of
232 // this unreachable. Both are fine; neither should warn the caller.
233 #[allow(unreachable_patterns, unreachable_code)]
234 fn apply_delta(
235 &mut self,
236 delta: Self::Output,
237 ) -> ::std::result::Result<(), ::delta_struct::Mismatch> {
238 #apply_body
239 }
240 }
241 };
242 let output = quote! {
243 #delta_type
244
245 #delta_impl
246 };
247 TokenStream::from(output)
248}
249
250/// The four pieces the struct and enum paths each produce: the delta type's
251/// declaration, the `Output` it becomes, and the two method bodies.
252struct Generated {
253 delta_type: TokenStream2,
254 output_ty: TokenStream2,
255 delta_body: TokenStream2,
256 apply_body: TokenStream2,
257}
258
259/// Reads one group of source fields into the shape the code generators want,
260/// resolving each against the container's default field type.
261///
262/// Returns `(named, fields)`, where `named` says whether the group is written
263/// with braces or with parentheses.
264fn read_fields(owner: &Ident, fields: Fields, default_field_type: FieldType) -> (bool, Vec<Field>) {
265 let (named, collected) = match fields {
266 Fields::Named(named) => (
267 true,
268 collect_results(
269 named.named.into_iter().map(|field| {
270 (
271 field.ident.unwrap().to_string(),
272 field.ty,
273 get_fieldtype_from_attrs(field.attrs.into_iter(), "field_type"),
274 )
275 }),
276 default_field_type,
277 ),
278 ),
279 Fields::Unnamed(unnamed) => (
280 false,
281 collect_results(
282 unnamed.unnamed.into_iter().enumerate().map(|(i, field)| {
283 (
284 i.to_string(),
285 field.ty,
286 get_fieldtype_from_attrs(field.attrs.into_iter(), "field_type"),
287 )
288 }),
289 default_field_type,
290 ),
291 ),
292 Fields::Unit => (false, Ok(vec![])),
293 };
294 match collected {
295 Ok(fields) => (named, fields),
296 Err(bad_fields) => {
297 let bad_fields = format!("{:?}", bad_fields);
298 abort_call_site!(
299 "delta_struct(field_type = ...) for fields in {}: {} are not valid values. Expected {}.",
300 owner,
301 bad_fields,
302 VALID_FIELD_TYPES
303 )
304 }
305 }
306}
307
308/// Generates the delta of a struct: a companion struct of the same shape, and
309/// two method bodies that walk its fields.
310#[allow(clippy::too_many_arguments)] // All of it is one type's description.
311fn struct_impl(
312 ident: &Ident,
313 vis: &syn::Visibility,
314 delta_ident: &Ident,
315 delta_leader: &TokenStream2,
316 generics: &syn::Generics,
317 og_where_clause: &Option<syn::WhereClause>,
318 ty_generics: &TokenStream2,
319 fields: Fields,
320 default_field_type: FieldType,
321) -> Generated {
322 let (named, fields) = read_fields(ident, fields, default_field_type);
323 let delta_fields = delta_fields(named, fields.iter().cloned());
324 let (compute_let, compute_fields) =
325 delta_compute_fields(named, Source::Whole, fields.iter().cloned());
326 let (apply_let, apply_actions) = delta_apply_fields(named, Source::Whole, fields.into_iter());
327
328 // A tuple struct's delta is a tuple struct too, which means the
329 // declaration, the initializer, and the destructuring pattern all have to
330 // switch from braces to parentheses together. Two things differ beyond the
331 // brackets: a tuple struct puts its `where` clause *after* the fields and
332 // ends in a semicolon, and its constructor lives in the value namespace,
333 // which `Self::Output` — an associated type — cannot reach, so the
334 // initializer and pattern name the struct itself and let inference supply
335 // its generics.
336 let (delta_type, compute_init, apply_pattern) = if named {
337 (
338 quote! {
339 #delta_leader
340 #vis struct #delta_ident #generics #og_where_clause {
341 #delta_fields
342 }
343 },
344 quote!(Self::Output { #compute_fields }),
345 quote!(Self::Output { #apply_let }),
346 )
347 } else {
348 (
349 quote! {
350 #delta_leader
351 #vis struct #delta_ident #generics (#delta_fields) #og_where_clause;
352 },
353 quote!(#delta_ident(#compute_fields)),
354 quote!(#delta_ident(#apply_let)),
355 )
356 };
357
358 Generated {
359 delta_type,
360 output_ty: quote!(#delta_ident #ty_generics),
361 delta_body: quote! {
362 let mut delta_is_some = false;
363 #compute_let
364 if delta_is_some {
365 Some(#compute_init)
366 } else {
367 None
368 }
369 },
370 // A struct's delta always fits, so this is the arm of `apply_delta`
371 // that can only ever be `Ok` — the `?`s inside come from fields whose
372 // own types are enums.
373 apply_body: quote! {
374 let #apply_pattern = delta;
375 #apply_actions
376 Ok(())
377 },
378 }
379}
380
381/// Generates the delta of an enum.
382///
383/// The companion enum carries one variant per *diffable* source variant — a
384/// field-less variant can never differ from itself, so giving it an arm would
385/// only create one nothing could construct. Changing variant is not a
386/// difference at all but a replacement, and that case lives in
387/// [`EnumDelta::Became`](::delta_struct::EnumDelta), a type in the runtime
388/// crate rather than an arm here, so it cannot collide with a variant the user
389/// wrote.
390#[allow(clippy::too_many_arguments)] // All of it is one type's description.
391fn enum_impl(
392 ident: &Ident,
393 vis: &syn::Visibility,
394 delta_ident: &Ident,
395 delta_leader: &TokenStream2,
396 generics: &syn::Generics,
397 og_where_clause: &Option<syn::WhereClause>,
398 ty_generics: &TokenStream2,
399 variants: Vec<syn::Variant>,
400 default_field_type: FieldType,
401) -> Generated {
402 if variants.is_empty() {
403 abort_call_site!(
404 "delta_struct::Delta cannot be derived for {}, which has no variants: an \
405 uninhabited type has no two values to differ.",
406 ident
407 )
408 }
409
410 let read: Vec<(Ident, bool, Vec<Field>)> = variants
411 .into_iter()
412 .map(|variant| {
413 let (named, fields) = read_fields(ident, variant.fields, default_field_type);
414 (variant.ident, named, fields)
415 })
416 .collect();
417
418 let mut delta_variants = Vec::new();
419 let mut diff_arms = Vec::new();
420 let mut apply_arms = Vec::new();
421
422 for (variant, named, fields) in &read {
423 if fields.is_empty() {
424 // Nothing to diff, and nothing to apply: two of these are equal by
425 // being the same variant.
426 let pattern = variant_pattern("e!(Self), variant, *named, fields, Some("old"));
427 diff_arms.push(quote!((#pattern, Self::#variant) => None,));
428 continue;
429 }
430
431 // Enum variant fields carry the enum's visibility, so unlike a struct's
432 // they must not be written `pub`.
433 let declared = delta_fields_inner(*named, false, fields.iter().cloned());
434 delta_variants.push(if *named {
435 quote!(#variant { #declared })
436 } else {
437 quote!(#variant(#declared))
438 });
439
440 let (compute_let, compute_fields) =
441 delta_compute_fields(*named, Source::Bound, fields.iter().cloned());
442 let old = variant_pattern("e!(Self), variant, *named, fields, Some("old"));
443 let new = variant_pattern("e!(Self), variant, *named, fields, Some("new"));
444 let init = if *named {
445 quote!(#delta_ident::#variant { #compute_fields })
446 } else {
447 quote!(#delta_ident::#variant(#compute_fields))
448 };
449 diff_arms.push(quote! {
450 (#old, #new) => {
451 let mut delta_is_some = false;
452 #compute_let
453 if delta_is_some {
454 Some(::delta_struct::EnumDelta::Delta(#init))
455 } else {
456 None
457 }
458 }
459 });
460
461 let (_, apply_actions) = delta_apply_fields(*named, Source::Bound, fields.iter().cloned());
462 let target = variant_pattern("e!(Self), variant, *named, fields, Some("self"));
463 let carried = variant_pattern("e!(#delta_ident), variant, *named, fields, None);
464 apply_arms.push(quote! {
465 (#target, #carried) => { #apply_actions }
466 });
467 }
468
469 // Both halves of a mismatch report a name, and both are found by matching
470 // — `{ .. }` fits every variant shape, so one arm per variant does it.
471 let source_names = read
472 .iter()
473 .map(|(variant, ..)| quote!(Self::#variant { .. } => stringify!(#variant),));
474 let delta_names = read
475 .iter()
476 .filter(|(_, _, fields)| !fields.is_empty())
477 .map(|(variant, ..)| quote!(#delta_ident::#variant { .. } => stringify!(#variant),));
478
479 Generated {
480 delta_type: quote! {
481 #delta_leader
482 #vis enum #delta_ident #generics #og_where_clause {
483 #(#delta_variants,)*
484 }
485 },
486 output_ty: quote! {
487 ::delta_struct::EnumDelta<#ident #ty_generics, #delta_ident #ty_generics>
488 },
489 delta_body: quote! {
490 #[allow(unreachable_patterns)] // A one-variant enum never `Became`.
491 match (old, new) {
492 #(#diff_arms)*
493 // Different variants: there is no difference to describe, only
494 // a replacement.
495 (_, new) => Some(::delta_struct::EnumDelta::Became(new)),
496 }
497 },
498 apply_body: quote! {
499 let delta = match delta {
500 ::delta_struct::EnumDelta::Became(new) => {
501 *self = new;
502 return Ok(());
503 }
504 ::delta_struct::EnumDelta::Delta(delta) => delta,
505 };
506 match (&mut *self, delta) {
507 #(#apply_arms)*
508 (found, mismatched) => {
509 return Err(::delta_struct::Mismatch {
510 type_name: stringify!(#ident),
511 expected: match mismatched { #(#delta_names)* },
512 found: match found { #(#source_names)* },
513 });
514 }
515 }
516 Ok(())
517 },
518 }
519}
520
521/// The pattern that takes one variant apart, binding each field to a local.
522///
523/// `prefix` distinguishes the several copies of a variant that appear in one
524/// match — `old_`, `new_`, `self_` — or is [`None`] for the delta being
525/// consumed, whose fields bind to the bare local names the generated field
526/// code already refers to.
527fn variant_pattern(
528 path: &TokenStream2,
529 variant: &Ident,
530 named: bool,
531 fields: &[Field],
532 prefix: Option<&str>,
533) -> TokenStream2 {
534 if fields.is_empty() {
535 return quote!(#path::#variant);
536 }
537 let bindings = fields
538 .iter()
539 .map(|(og_ident, ..)| {
540 let local = local_ident(named, og_ident);
541 match prefix {
542 Some(prefix) => format_ident!("{}_{}", prefix, local),
543 None => local,
544 }
545 })
546 .collect::<Vec<_>>();
547 if named {
548 let names = fields
549 .iter()
550 .map(|(og_ident, ..)| format_ident!("{}", og_ident));
551 quote!(#path::#variant { #(#names: #bindings),* })
552 } else {
553 quote!(#path::#variant( #(#bindings),* ))
554 }
555}
556
557/// Emits the field declarations of the generated delta struct.
558///
559/// Fields arrive as `(name, type, field type, delta_leader)`, where `name` is
560/// the source field's name or, for tuple structs, its index. `named` says
561/// which of the two it is, and so whether these declarations are about to be
562/// wrapped in braces or in parentheses: a tuple struct's delta is a tuple
563/// struct too, and its fields are positional rather than named.
564fn delta_fields(named: bool, iter: impl Iterator<Item = Field>) -> proc_macro2::TokenStream {
565 delta_fields_inner(named, true, iter)
566}
567
568/// The body of [`delta_fields`], with a say over `pub`.
569///
570/// A struct's delta fields are all `pub`; an enum variant's take the enum's
571/// visibility and may not be written `pub` at all.
572fn delta_fields_inner(
573 named: bool,
574 public: bool,
575 iter: impl Iterator<Item = Field>,
576) -> proc_macro2::TokenStream {
577 let vis = public.then(|| quote!(pub));
578 FromIterator::from_iter(iter.map(|(ident, ty, field_ty, field_leader)| {
579 let field_leader = proc_macro2::TokenStream::from_str(&field_leader).unwrap();
580 let declared_ty = match field_ty {
581 FieldType::Ordered => {
582 quote!(::delta_struct::SeqDelta<<#ty as ::std::iter::IntoIterator>::Item>)
583 }
584 FieldType::Unordered => {
585 // Unlike the other collection field types this does not name a
586 // delta type directly: a set's membership diff and a map's are
587 // different shapes, and `Unordered` is what picks between them.
588 quote!(<#ty as ::delta_struct::Unordered>::Delta)
589 }
590 FieldType::UnorderedDelta => {
591 // The field's own type names the collection, not its key and
592 // value; `MapEntry` is what projects those back out of the
593 // item type so the delta field can be spelled at all.
594 let entry = quote!(<#ty as ::std::iter::IntoIterator>::Item);
595 let key = quote!(<#entry as ::delta_struct::MapEntry>::Key);
596 let value = quote!(<#entry as ::delta_struct::MapEntry>::Value);
597 quote!(::delta_struct::MapDelta<#key, #value, <#value as Delta>::Output>)
598 }
599 FieldType::Scalar => quote!(::std::option::Option<#ty>),
600 FieldType::Delta => quote!(::std::option::Option<<#ty as Delta>::Output>),
601 };
602 if named {
603 let ident = format_ident!("{}", ident);
604 quote! {
605 #field_leader
606 #vis #ident: #declared_ty,
607 }
608 } else {
609 quote! {
610 #field_leader
611 #vis #declared_ty,
612 }
613 }
614 }))
615}
616
617/// Emits the body of `Delta::delta`, as `(statements, struct initializer)`.
618///
619/// The statements bind one local per generated field and set `delta_is_some`
620/// whenever they find a real change; the initializer then moves those locals
621/// into the delta struct. Fields arrive in the same shape as in
622/// [`delta_fields`].
623fn delta_compute_fields(
624 named: bool,
625 source: Source,
626 iter: impl Iterator<Item = Field>,
627) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
628 iter.map(|(og_ident, ty, field_ty, _field_leader)| {
629 let ident = local_ident(named, &og_ident);
630 let (old, new) = source.sides(&og_ident, &ident);
631 let statements = match field_ty {
632 FieldType::Ordered | FieldType::UnorderedDelta => {
633 let module = collection_module(field_ty);
634 quote! {
635 let #ident = ::delta_struct::#module::diff(#old, #new);
636 delta_is_some = delta_is_some || !#ident.is_empty();
637 }
638 }
639 // `Unordered::diff` reports "nothing changed" as `None` rather than
640 // as an empty delta, so this reads like the `Scalar` arm below
641 // rather than like the two collection modules above. The field
642 // still holds an empty delta, which is what `Default` supplies.
643 FieldType::Unordered => quote! {
644 let #ident = match <#ty as ::delta_struct::Unordered>::diff(#old, #new) {
645 Some(v) => {
646 delta_is_some = true;
647 v
648 }
649 None => ::std::default::Default::default(),
650 };
651 },
652 FieldType::Scalar => quote! {
653 let #ident = if #old != #new {
654 delta_is_some = true;
655 Some(#new)
656 } else {
657 None
658 };
659 },
660 FieldType::Delta => quote! {
661 let #ident = Delta::delta(#old, #new);
662 delta_is_some = delta_is_some || #ident.is_some();
663 },
664 };
665 // The locals are listed in declaration order, so this reads as a field
666 // shorthand inside braces and as a positional argument inside parens —
667 // whichever bracket the caller wraps it in.
668 (statements, quote!(#ident,))
669 })
670 .unzip()
671}
672
673/// Emits the body of `Delta::apply_delta`, as `(destructuring pattern,
674/// statements)`.
675///
676/// The pattern takes the delta struct apart into locals and the statements
677/// write each change back into `self`. Fields arrive in the same shape as in
678/// [`delta_fields`].
679fn delta_apply_fields(
680 named: bool,
681 source: Source,
682 iter: impl Iterator<Item = Field>,
683) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
684 iter.map(|(og_ident, ty, field_ty, _field_leader)| {
685 let ident = local_ident(named, &og_ident);
686 let target = source.target(&og_ident, &ident);
687 let statements = match field_ty {
688 // `map::apply` is the one collection helper that can fail, because
689 // it is the one that recurses into `apply_delta`.
690 FieldType::Ordered | FieldType::UnorderedDelta => {
691 let module = collection_module(field_ty);
692 let question = (field_ty == FieldType::UnorderedDelta).then(|| quote!(?));
693 quote! {
694 ::delta_struct::#module::apply(&mut #target, #ident)#question;
695 }
696 }
697 FieldType::Unordered => quote! {
698 <#ty as ::delta_struct::Unordered>::apply(&mut #target, #ident);
699 },
700 FieldType::Scalar => quote! {
701 if let Some(v) = #ident {
702 #target = v;
703 }
704 },
705 FieldType::Delta => quote! {
706 if let Some(v) = #ident {
707 #target.apply_delta(v)?;
708 }
709 },
710 };
711 // Binds one local per field, in declaration order — see the matching
712 // note in `delta_compute_fields` about braces versus parens.
713 (quote!(#ident,), statements)
714 })
715 .unzip()
716}
717
718/// How generated code reaches the two sides of a field.
719///
720/// A struct's impl holds whole values and reads through them. An enum's has
721/// taken its values apart in a match pattern, so the fields are already locals
722/// by the time the per-field code runs — and for `apply_delta` they are `&mut`
723/// locals, which is why [`Source::target`] dereferences them.
724#[derive(Copy, Clone, Debug, Eq, PartialEq)]
725enum Source {
726 /// Through the values themselves: `old.foo`, `new.foo`, `self.foo`.
727 Whole,
728 /// Through pattern bindings: `old_foo`, `new_foo`, `*self_foo`.
729 Bound,
730}
731
732impl Source {
733 /// The expressions naming a field's old and new values in `Delta::delta`.
734 fn sides(self, og_ident: &str, ident: &Ident) -> (TokenStream2, TokenStream2) {
735 match self {
736 Source::Whole => {
737 let og_ident = field_accessor(og_ident);
738 (quote!(old.#og_ident), quote!(new.#og_ident))
739 }
740 Source::Bound => {
741 let old = format_ident!("old_{}", ident);
742 let new = format_ident!("new_{}", ident);
743 (quote!(#old), quote!(#new))
744 }
745 }
746 }
747
748 /// The place expression a field is written back to in `apply_delta`.
749 fn target(self, og_ident: &str, ident: &Ident) -> TokenStream2 {
750 match self {
751 Source::Whole => {
752 let og_ident = field_accessor(og_ident);
753 quote!(self.#og_ident)
754 }
755 Source::Bound => {
756 let binding = format_ident!("self_{}", ident);
757 quote!((*#binding))
758 }
759 }
760 }
761}
762
763/// A source field's name as it is written after a `.` — its identifier, or the
764/// bare index of a tuple field.
765fn field_accessor(og_ident: &str) -> TokenStream2 {
766 FromStr::from_str(og_ident).unwrap()
767}
768
769/// The local a generated field binds to: its own name, or `field_0`,
770/// `field_1`, … where the source field is positional.
771fn local_ident(named: bool, og_ident: &str) -> Ident {
772 if named {
773 format_ident!("{}", og_ident)
774 } else {
775 format_ident!("field_{}", og_ident)
776 }
777}
778
779/// The runtime module backing a collection field type whose delta type is
780/// fixed by the field type alone.
781///
782/// These two differ in what their delta looks like but not in how the derive
783/// drives one: each module pairs a `diff` and an `apply` over a delta type
784/// that reports whether it is empty. `unordered` is not among them — its shape
785/// depends on the collection rather than the field type, so it goes through
786/// the `Unordered` trait instead. Panics for every field type the callers
787/// never pass.
788fn collection_module(field_ty: FieldType) -> Ident {
789 match field_ty {
790 FieldType::Ordered => format_ident!("seq"),
791 FieldType::UnorderedDelta => format_ident!("map"),
792 FieldType::Unordered | FieldType::Scalar | FieldType::Delta => {
793 unreachable!("{:?} does not have a fixed collection module", field_ty)
794 }
795 }
796}
797
798/// Resolves each field's parsed attributes against the container default,
799/// collecting *every* bad field rather than stopping at the first, so one
800/// compile reports them all.
801#[allow(clippy::manual_try_fold)] // Collects errors too
802fn collect_results(
803 iter: impl Iterator<Item = ParsedField>,
804 default_field_type: FieldType,
805) -> Result<Vec<Field>, Vec<String>> {
806 iter.fold(Ok(vec![]), |v, i| match (v, i) {
807 (Ok(mut v), (ident, b, Ok((c, d)))) => {
808 v.push((ident, b, c.unwrap_or(default_field_type), d));
809 Ok(v)
810 }
811 (Ok(_), (ident, _, Err(_))) => Err(vec![ident]),
812 (Err(mut v), (ident, _, Err(_))) => {
813 v.push(ident);
814 Err(v)
815 }
816 (v @ Err(_), _) => v,
817 })
818}
819
820enum FieldTypeError {
821 /// The `delta_struct(...)` attribute contained entries that were not
822 /// `name = "value"` pairs.
823 UnrecognizedJunkFound,
824}
825
826/// Reads a `#[delta_struct(...)]` attribute, returning
827/// `(field type, delta_leader)`.
828///
829/// `attr_name` is the key naming the field type in this position — `"default"`
830/// on a container, `"field_type"` on a field — because the two spellings mean
831/// the same thing at different scopes. The field type is `None` when the
832/// attribute is absent or names no field type, leaving the caller to fill in
833/// the default; `delta_leader` is empty when unspecified.
834#[allow(clippy::manual_try_fold)] // Collects errors too
835fn get_fieldtype_from_attrs(iter: impl Iterator<Item = Attribute>, attr_name: &str) -> ParsedAttrs {
836 for attr in iter {
837 if let Ok(Meta::List(MetaList { path, nested, .. })) = attr.parse_meta() {
838 let Path { segments, .. } = path;
839 if segments
840 .iter()
841 .map(|p| &p.ident)
842 .eq(["delta_struct"].iter().cloned())
843 {
844 let values: Result<Vec<_>, Vec<NestedMeta>> = nested
845 .iter()
846 .map(|nested_meta| match nested_meta {
847 NestedMeta::Meta(Meta::NameValue(MetaNameValue {
848 path,
849 lit: Lit::Str(s),
850 ..
851 })) => Ok((path.get_ident().map(|i| i.to_string()), s.value())),
852 e => Err(e),
853 })
854 .fold(Ok(vec![]), |v, i| match (v, i) {
855 (Ok(mut v), Ok(i)) => {
856 v.push(i);
857 Ok(v)
858 }
859 (Ok(_), Err(e)) => Err(vec![e.clone()]),
860 (Err(mut v), Err(e)) => {
861 v.push(e.clone());
862 Err(v)
863 }
864 (v @ Err(_), _) => v,
865 });
866 return match values {
867 Ok(v) => {
868 let mut field_type = None;
869 let mut delta_leader = String::new();
870 for i in v {
871 match i.0.as_deref() {
872 Some("delta_leader") => {
873 delta_leader = i.1;
874 }
875 a if Some(attr_name) == a => {
876 field_type = string_to_fieldtype(&i.1);
877 }
878 a => {
879 abort_call_site!("Unrecognized value {:?}", a);
880 }
881 }
882 }
883 Ok((field_type, delta_leader))
884 }
885 Err(_) => Err(FieldTypeError::UnrecognizedJunkFound),
886 };
887 }
888 }
889 }
890 Ok((None, String::new()))
891}
892
893/// Maps the attribute spelling of a field type to its variant, or `None` if it
894/// is not one of the recognized names.
895fn string_to_fieldtype(s: &str) -> Option<FieldType> {
896 match s {
897 "ordered" => Some(FieldType::Ordered),
898 "unordered" => Some(FieldType::Unordered),
899 "unordered-delta" => Some(FieldType::UnorderedDelta),
900 "scalar" => Some(FieldType::Scalar),
901 "delta" => Some(FieldType::Delta),
902 _ => None,
903 }
904}
905
906/// Derives `Fingerprint`, a stable content hash used to check that a delta is
907/// being applied to the state it was computed against.
908///
909/// Walks a struct's fields in declaration order, or an enum's variant index
910/// followed by that variant's fields. Every field type has to implement
911/// `Fingerprint` too, and every type parameter picks up a `Fingerprint` bound.
912///
913/// Unlike the `Delta` derive this needs nothing in scope — the generated code
914/// names `::delta_struct::Fingerprint` in full — and it accepts enums, which
915/// have a perfectly good content hash even though they have no obvious delta.
916///
917/// ```ignore
918/// use delta_struct::Fingerprint;
919///
920/// #[derive(Fingerprint)]
921/// struct Device {
922/// services: std::collections::HashSet<String>,
923/// online: bool,
924/// }
925/// ```
926#[proc_macro_derive(Fingerprint)]
927#[proc_macro_error]
928pub fn derive_fingerprint(input: TokenStream) -> TokenStream {
929 let DeriveInput {
930 ident,
931 mut generics,
932 data,
933 ..
934 } = parse_macro_input!(input as DeriveInput);
935
936 let body = match data {
937 Data::Struct(strukt) => {
938 // A struct's fields are reached through `self`, by name or by
939 // position.
940 fingerprint_calls(strukt.fields.iter().enumerate().map(|(i, field)| {
941 match &field.ident {
942 Some(ident) => quote!(self.#ident),
943 None => {
944 let index = syn::Index::from(i);
945 quote!(self.#index)
946 }
947 }
948 }))
949 }
950 Data::Enum(enom) => {
951 // A variant's fields are reached through the locals its pattern
952 // binds. The variant's index is folded in first, so two variants
953 // holding equal payloads still fingerprint differently.
954 let arms = enom.variants.into_iter().enumerate().map(|(index, variant)| {
955 let variant_ident = variant.ident;
956 let bindings = binding_idents(&variant.fields);
957 let pattern = match &variant.fields {
958 Fields::Named(_) => quote!(Self::#variant_ident { #(#bindings),* }),
959 Fields::Unnamed(_) => quote!(Self::#variant_ident( #(#bindings),* )),
960 Fields::Unit => quote!(Self::#variant_ident),
961 };
962 let fields = fingerprint_calls(bindings.iter().map(|b| quote!(#b)));
963 let index = index as u32;
964 quote! {
965 #pattern => {
966 ::delta_struct::Fingerprint::fingerprint(&#index, hasher);
967 #fields
968 }
969 }
970 });
971 quote! {
972 match self {
973 #(#arms)*
974 }
975 }
976 }
977 _ => abort_call_site!(
978 "delta_struct::Fingerprint may only be derived for struct and enum types. {} is neither.",
979 ident
980 ),
981 };
982
983 let fingerprint_types = generics
984 .type_params()
985 .map(|t| t.ident.clone())
986 .collect::<Vec<_>>();
987 let where_clause = generics.make_where_clause();
988 for ty in fingerprint_types {
989 where_clause
990 .predicates
991 .push(syn::parse_quote!(#ty: ::delta_struct::Fingerprint));
992 }
993 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
994
995 TokenStream::from(quote! {
996 impl #impl_generics ::delta_struct::Fingerprint for #ident #ty_generics #where_clause {
997 fn fingerprint(&self, hasher: &mut ::delta_struct::fingerprint::Hasher) {
998 #body
999 }
1000 }
1001 })
1002}
1003
1004/// The locals an enum variant's fields bind to in a match pattern: the field's
1005/// own name where it has one, and `field_0`, `field_1`, … where it does not.
1006fn binding_idents(fields: &Fields) -> Vec<Ident> {
1007 fields
1008 .iter()
1009 .enumerate()
1010 .map(|(i, field)| match &field.ident {
1011 Some(ident) => ident.clone(),
1012 None => format_ident!("field_{}", i),
1013 })
1014 .collect()
1015}
1016
1017/// Emits one `Fingerprint::fingerprint` call per expression, in order.
1018///
1019/// The expressions name the fields however the caller can reach them —
1020/// `self.foo` inside a struct, a pattern binding inside a match arm.
1021fn fingerprint_calls(
1022 exprs: impl Iterator<Item = proc_macro2::TokenStream>,
1023) -> proc_macro2::TokenStream {
1024 let calls = exprs.map(|expr| quote!(::delta_struct::Fingerprint::fingerprint(&#expr, hasher);));
1025 quote!(#(#calls)*)
1026}