1extern 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::{
15 fmt::{self, Display},
16 iter::FromIterator,
17 str::FromStr,
18};
19use syn::{
20 parse_macro_input, punctuated::Punctuated, Attribute, Data, DeriveInput, Expr, ExprLit, Fields,
21 Ident, Lit, Meta, MetaList, MetaNameValue, Path, PredicateType, Token, TraitBound,
22 TraitBoundModifiers, Type, TypeParamBound, WherePredicate,
23};
24
25#[derive(Copy, Clone, Debug, Eq, PartialEq)]
28enum FieldType {
29 Ordered,
31 Unordered,
35 UnorderedDelta,
39 Scalar,
41 Delta,
43}
44
45const VALID_FIELD_TYPES: &str =
46 "\"ordered\", \"unordered\", \"unordered-delta\", \"delta\", or \"scalar\"";
47
48type Field = (String, Type, FieldType, String);
52
53type ParsedField = (String, Type, ParsedAttrs);
56
57type ParsedAttrs = Result<(Option<FieldType>, String), FieldTypeError>;
60
61#[proc_macro_derive(Delta, attributes(delta_struct))]
119#[proc_macro_error]
120pub fn derive_delta(input: TokenStream) -> TokenStream {
121 let DeriveInput {
122 attrs,
123 vis,
124 ident,
125 mut generics,
126 data,
127 } = parse_macro_input!(input as DeriveInput);
128 let (default_field_type, delta_leader) =
129 match get_fieldtype_from_attrs(attrs.into_iter(), "default") {
130 Ok((v, delta_leader)) => (v.unwrap_or(FieldType::Scalar), delta_leader),
131 Err(e) => {
132 abort_call_site!(
133 "delta_struct(default = ...) for {} is not an accepted value, expected {}. {}",
134 ident,
135 VALID_FIELD_TYPES,
136 e,
137 );
138 }
139 };
140
141 let delta_leader = match proc_macro2::TokenStream::from_str(&delta_leader) {
142 Ok(v) => v,
143 Err(e) => {
144 abort_call_site!("error parsing delta leader as token stream {}", e);
145 }
146 };
147 let delta_ident = format_ident!("{}Delta", ident);
148 let og_where_clause = generics.where_clause.clone();
154 let ty_generics_only = generics.split_for_impl().1.to_token_stream();
155
156 let Generated {
157 delta_type,
158 output_ty,
159 delta_body,
160 apply_body,
161 } = match data {
162 Data::Struct(strukt) => struct_impl(
163 &ident,
164 &vis,
165 &delta_ident,
166 &delta_leader,
167 &generics,
168 &og_where_clause,
169 &ty_generics_only,
170 strukt.fields,
171 default_field_type,
172 ),
173 Data::Enum(enom) => enum_impl(
174 &ident,
175 &vis,
176 &delta_ident,
177 &delta_leader,
178 &generics,
179 &og_where_clause,
180 &ty_generics_only,
181 enom.variants.into_iter().collect(),
182 default_field_type,
183 ),
184 Data::Union(_) => {
185 abort_call_site!(
186 "delta_struct::Delta may only be derived for struct and enum types. {} is a union.",
187 ident
188 )
189 }
190 };
191 let partial_eq_types = generics
196 .type_params()
197 .map(|t| t.ident.clone())
198 .collect::<Vec<_>>();
199 let where_clause = generics.make_where_clause();
200 for ty in partial_eq_types {
201 let mut bounds = Punctuated::new();
202 let mut segments = Punctuated::new();
203 segments.push(Ident::new("std", Span::call_site()).into());
204 segments.push(Ident::new("cmp", Span::call_site()).into());
205 segments.push(Ident::new("PartialEq", Span::call_site()).into());
206 bounds.push(TypeParamBound::Trait(TraitBound {
207 paren_token: None,
208 modifiers: TraitBoundModifiers::default(),
209 lifetimes: None,
210 maybe: None,
211 path: Path {
212 leading_colon: Some(Token!(::)(Span::call_site())),
213 segments,
214 },
215 }));
216 where_clause
217 .predicates
218 .push(WherePredicate::Type(PredicateType {
219 attrs: Vec::new(),
220 lifetimes: None,
221 bounded_ty: Type::Verbatim(<Ident as Into<TokenTree>>::into(ty).into()),
222 colon_token: Token!(:)(Span::call_site()),
223 bounds,
224 }));
225 }
226 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
227 let delta_impl = quote! {
228 impl #impl_generics Delta for #ident #ty_generics #where_clause {
229 type Output = #output_ty;
232
233 fn delta(old: Self, new: Self) -> Option<Self::Output> {
234 #delta_body
235 }
236
237 #[allow(unreachable_patterns, unreachable_code)]
241 fn apply_delta(
242 &mut self,
243 delta: Self::Output,
244 ) -> ::std::result::Result<(), ::delta_struct::Mismatch> {
245 #apply_body
246 }
247 }
248 };
249 let output = quote! {
250 #delta_type
251
252 #delta_impl
253 };
254 TokenStream::from(output)
255}
256
257struct Generated {
260 delta_type: TokenStream2,
261 output_ty: TokenStream2,
262 delta_body: TokenStream2,
263 apply_body: TokenStream2,
264}
265
266fn read_fields(owner: &Ident, fields: Fields, default_field_type: FieldType) -> (bool, Vec<Field>) {
272 let (named, collected) = match fields {
273 Fields::Named(named) => (
274 true,
275 collect_results(
276 named.named.into_iter().map(|field| {
277 (
278 field.ident.unwrap().to_string(),
279 field.ty,
280 get_fieldtype_from_attrs(field.attrs.into_iter(), "field_type"),
281 )
282 }),
283 default_field_type,
284 ),
285 ),
286 Fields::Unnamed(unnamed) => (
287 false,
288 collect_results(
289 unnamed.unnamed.into_iter().enumerate().map(|(i, field)| {
290 (
291 i.to_string(),
292 field.ty,
293 get_fieldtype_from_attrs(field.attrs.into_iter(), "field_type"),
294 )
295 }),
296 default_field_type,
297 ),
298 ),
299 Fields::Unit => (false, Ok(vec![])),
300 };
301 match collected {
302 Ok(fields) => (named, fields),
303 Err(bad_fields) => {
304 let bad_fields = format!("{:?}", bad_fields);
305 abort_call_site!(
306 "delta_struct(field_type = ...) for fields in {}: {} are not valid values. Expected {}.",
307 owner,
308 bad_fields,
309 VALID_FIELD_TYPES
310 )
311 }
312 }
313}
314
315#[allow(clippy::too_many_arguments)] fn struct_impl(
319 ident: &Ident,
320 vis: &syn::Visibility,
321 delta_ident: &Ident,
322 delta_leader: &TokenStream2,
323 generics: &syn::Generics,
324 og_where_clause: &Option<syn::WhereClause>,
325 ty_generics: &TokenStream2,
326 fields: Fields,
327 default_field_type: FieldType,
328) -> Generated {
329 let (named, fields) = read_fields(ident, fields, default_field_type);
330 let delta_fields = delta_fields(named, fields.iter().cloned());
331 let (compute_let, compute_fields) =
332 delta_compute_fields(named, Source::Whole, fields.iter().cloned());
333 let (apply_let, apply_actions) = delta_apply_fields(named, Source::Whole, fields.into_iter());
334
335 let (delta_type, compute_init, apply_pattern) = if named {
344 (
345 quote! {
346 #delta_leader
347 #vis struct #delta_ident #generics #og_where_clause {
348 #delta_fields
349 }
350 },
351 quote!(Self::Output { #compute_fields }),
352 quote!(Self::Output { #apply_let }),
353 )
354 } else {
355 (
356 quote! {
357 #delta_leader
358 #vis struct #delta_ident #generics (#delta_fields) #og_where_clause;
359 },
360 quote!(#delta_ident(#compute_fields)),
361 quote!(#delta_ident(#apply_let)),
362 )
363 };
364
365 Generated {
366 delta_type,
367 output_ty: quote!(#delta_ident #ty_generics),
368 delta_body: quote! {
369 let mut delta_is_some = false;
370 #compute_let
371 if delta_is_some {
372 Some(#compute_init)
373 } else {
374 None
375 }
376 },
377 apply_body: quote! {
381 let #apply_pattern = delta;
382 #apply_actions
383 Ok(())
384 },
385 }
386}
387
388#[allow(clippy::too_many_arguments)] fn enum_impl(
399 ident: &Ident,
400 vis: &syn::Visibility,
401 delta_ident: &Ident,
402 delta_leader: &TokenStream2,
403 generics: &syn::Generics,
404 og_where_clause: &Option<syn::WhereClause>,
405 ty_generics: &TokenStream2,
406 variants: Vec<syn::Variant>,
407 default_field_type: FieldType,
408) -> Generated {
409 if variants.is_empty() {
410 abort_call_site!(
411 "delta_struct::Delta cannot be derived for {}, which has no variants: an \
412 uninhabited type has no two values to differ.",
413 ident
414 )
415 }
416
417 let read: Vec<(Ident, bool, Vec<Field>)> = variants
418 .into_iter()
419 .map(|variant| {
420 let (named, fields) = read_fields(ident, variant.fields, default_field_type);
421 (variant.ident, named, fields)
422 })
423 .collect();
424
425 let mut delta_variants = Vec::new();
426 let mut diff_arms = Vec::new();
427 let mut apply_arms = Vec::new();
428
429 for (variant, named, fields) in &read {
430 if fields.is_empty() {
431 let pattern = variant_pattern("e!(Self), variant, *named, fields, Some("old"));
434 diff_arms.push(quote!((#pattern, Self::#variant) => None,));
435 continue;
436 }
437
438 let declared = delta_fields_inner(*named, false, fields.iter().cloned());
441 delta_variants.push(if *named {
442 quote!(#variant { #declared })
443 } else {
444 quote!(#variant(#declared))
445 });
446
447 let (compute_let, compute_fields) =
448 delta_compute_fields(*named, Source::Bound, fields.iter().cloned());
449 let old = variant_pattern("e!(Self), variant, *named, fields, Some("old"));
450 let new = variant_pattern("e!(Self), variant, *named, fields, Some("new"));
451 let init = if *named {
452 quote!(#delta_ident::#variant { #compute_fields })
453 } else {
454 quote!(#delta_ident::#variant(#compute_fields))
455 };
456 diff_arms.push(quote! {
457 (#old, #new) => {
458 let mut delta_is_some = false;
459 #compute_let
460 if delta_is_some {
461 Some(::delta_struct::EnumDelta::Delta(#init))
462 } else {
463 None
464 }
465 }
466 });
467
468 let (_, apply_actions) = delta_apply_fields(*named, Source::Bound, fields.iter().cloned());
469 let target = variant_pattern("e!(Self), variant, *named, fields, Some("self"));
470 let carried = variant_pattern("e!(#delta_ident), variant, *named, fields, None);
471 apply_arms.push(quote! {
472 (#target, #carried) => { #apply_actions }
473 });
474 }
475
476 let source_names = read
479 .iter()
480 .map(|(variant, ..)| quote!(Self::#variant { .. } => stringify!(#variant),));
481 let delta_names = read
482 .iter()
483 .filter(|(_, _, fields)| !fields.is_empty())
484 .map(|(variant, ..)| quote!(#delta_ident::#variant { .. } => stringify!(#variant),));
485
486 Generated {
487 delta_type: quote! {
488 #delta_leader
489 #vis enum #delta_ident #generics #og_where_clause {
490 #(#delta_variants,)*
491 }
492 },
493 output_ty: quote! {
494 ::delta_struct::EnumDelta<#ident #ty_generics, #delta_ident #ty_generics>
495 },
496 delta_body: quote! {
497 #[allow(unreachable_patterns)] match (old, new) {
499 #(#diff_arms)*
500 (_, new) => Some(::delta_struct::EnumDelta::Became(new)),
503 }
504 },
505 apply_body: quote! {
506 let delta = match delta {
507 ::delta_struct::EnumDelta::Became(new) => {
508 *self = new;
509 return Ok(());
510 }
511 ::delta_struct::EnumDelta::Delta(delta) => delta,
512 };
513 match (&mut *self, delta) {
514 #(#apply_arms)*
515 (found, mismatched) => {
516 return Err(::delta_struct::Mismatch {
517 type_name: stringify!(#ident),
518 expected: match mismatched { #(#delta_names)* },
519 found: match found { #(#source_names)* },
520 });
521 }
522 }
523 Ok(())
524 },
525 }
526}
527
528fn variant_pattern(
535 path: &TokenStream2,
536 variant: &Ident,
537 named: bool,
538 fields: &[Field],
539 prefix: Option<&str>,
540) -> TokenStream2 {
541 if fields.is_empty() {
542 return quote!(#path::#variant);
543 }
544 let bindings = fields
545 .iter()
546 .map(|(og_ident, ..)| {
547 let local = local_ident(named, og_ident);
548 match prefix {
549 Some(prefix) => format_ident!("{}_{}", prefix, local),
550 None => local,
551 }
552 })
553 .collect::<Vec<_>>();
554 if named {
555 let names = fields
556 .iter()
557 .map(|(og_ident, ..)| format_ident!("{}", og_ident));
558 quote!(#path::#variant { #(#names: #bindings),* })
559 } else {
560 quote!(#path::#variant( #(#bindings),* ))
561 }
562}
563
564fn delta_fields(named: bool, iter: impl Iterator<Item = Field>) -> proc_macro2::TokenStream {
572 delta_fields_inner(named, true, iter)
573}
574
575fn delta_fields_inner(
580 named: bool,
581 public: bool,
582 iter: impl Iterator<Item = Field>,
583) -> proc_macro2::TokenStream {
584 let vis = public.then(|| quote!(pub));
585 FromIterator::from_iter(iter.map(|(ident, ty, field_ty, field_leader)| {
586 let field_leader = proc_macro2::TokenStream::from_str(&field_leader).unwrap();
587 let declared_ty = match field_ty {
588 FieldType::Ordered => {
589 quote!(::delta_struct::SeqDelta<<#ty as ::std::iter::IntoIterator>::Item>)
590 }
591 FieldType::Unordered => {
592 quote!(<#ty as ::delta_struct::Unordered>::Delta)
596 }
597 FieldType::UnorderedDelta => {
598 let entry = quote!(<#ty as ::std::iter::IntoIterator>::Item);
602 let key = quote!(<#entry as ::delta_struct::MapEntry>::Key);
603 let value = quote!(<#entry as ::delta_struct::MapEntry>::Value);
604 quote!(::delta_struct::MapDelta<#key, #value, <#value as Delta>::Output>)
605 }
606 FieldType::Scalar => quote!(::delta_struct::ScalarDelta<#ty>),
607 FieldType::Delta => quote!(::std::option::Option<<#ty as Delta>::Output>),
608 };
609 if named {
610 let ident = format_ident!("{}", ident);
611 quote! {
612 #field_leader
613 #vis #ident: #declared_ty,
614 }
615 } else {
616 quote! {
617 #field_leader
618 #vis #declared_ty,
619 }
620 }
621 }))
622}
623
624fn delta_compute_fields(
631 named: bool,
632 source: Source,
633 iter: impl Iterator<Item = Field>,
634) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
635 iter.map(|(og_ident, ty, field_ty, _field_leader)| {
636 let ident = local_ident(named, &og_ident);
637 let (old, new) = source.sides(&og_ident, &ident);
638 let statements = match field_ty {
639 FieldType::Ordered | FieldType::UnorderedDelta => {
640 let module = collection_module(field_ty);
641 quote! {
642 let #ident = ::delta_struct::#module::diff(#old, #new);
643 delta_is_some = delta_is_some || !#ident.is_empty();
644 }
645 }
646 FieldType::Unordered => quote! {
651 let #ident = match <#ty as ::delta_struct::Unordered>::diff(#old, #new) {
652 Some(v) => {
653 delta_is_some = true;
654 v
655 }
656 None => ::std::default::Default::default(),
657 };
658 },
659 FieldType::Scalar => quote! {
660 let #ident = if #old != #new {
661 delta_is_some = true;
662 ::delta_struct::ScalarDelta::Changed(#new)
663 } else {
664 ::delta_struct::ScalarDelta::Unchanged
665 };
666 },
667 FieldType::Delta => quote! {
668 let #ident = Delta::delta(#old, #new);
669 delta_is_some = delta_is_some || #ident.is_some();
670 },
671 };
672 (statements, quote!(#ident,))
676 })
677 .unzip()
678}
679
680fn delta_apply_fields(
687 named: bool,
688 source: Source,
689 iter: impl Iterator<Item = Field>,
690) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
691 iter.map(|(og_ident, ty, field_ty, _field_leader)| {
692 let ident = local_ident(named, &og_ident);
693 let target = source.target(&og_ident, &ident);
694 let statements = match field_ty {
695 FieldType::Ordered | FieldType::UnorderedDelta => {
698 let module = collection_module(field_ty);
699 let question = (field_ty == FieldType::UnorderedDelta).then(|| quote!(?));
700 quote! {
701 ::delta_struct::#module::apply(&mut #target, #ident)#question;
702 }
703 }
704 FieldType::Unordered => quote! {
705 <#ty as ::delta_struct::Unordered>::apply(&mut #target, #ident);
706 },
707 FieldType::Scalar => quote! {
708 if let ::delta_struct::ScalarDelta::Changed(v) = #ident {
709 #target = v;
710 }
711 },
712 FieldType::Delta => quote! {
713 if let Some(v) = #ident {
714 #target.apply_delta(v)?;
715 }
716 },
717 };
718 (quote!(#ident,), statements)
721 })
722 .unzip()
723}
724
725#[derive(Copy, Clone, Debug, Eq, PartialEq)]
732enum Source {
733 Whole,
735 Bound,
737}
738
739impl Source {
740 fn sides(self, og_ident: &str, ident: &Ident) -> (TokenStream2, TokenStream2) {
742 match self {
743 Source::Whole => {
744 let og_ident = field_accessor(og_ident);
745 (quote!(old.#og_ident), quote!(new.#og_ident))
746 }
747 Source::Bound => {
748 let old = format_ident!("old_{}", ident);
749 let new = format_ident!("new_{}", ident);
750 (quote!(#old), quote!(#new))
751 }
752 }
753 }
754
755 fn target(self, og_ident: &str, ident: &Ident) -> TokenStream2 {
757 match self {
758 Source::Whole => {
759 let og_ident = field_accessor(og_ident);
760 quote!(self.#og_ident)
761 }
762 Source::Bound => {
763 let binding = format_ident!("self_{}", ident);
764 quote!((*#binding))
765 }
766 }
767 }
768}
769
770fn field_accessor(og_ident: &str) -> TokenStream2 {
773 FromStr::from_str(og_ident).unwrap()
774}
775
776fn local_ident(named: bool, og_ident: &str) -> Ident {
779 if named {
780 format_ident!("{}", og_ident)
781 } else {
782 format_ident!("field_{}", og_ident)
783 }
784}
785
786fn collection_module(field_ty: FieldType) -> Ident {
796 match field_ty {
797 FieldType::Ordered => format_ident!("seq"),
798 FieldType::UnorderedDelta => format_ident!("map"),
799 FieldType::Unordered | FieldType::Scalar | FieldType::Delta => {
800 unreachable!("{:?} does not have a fixed collection module", field_ty)
801 }
802 }
803}
804
805#[allow(clippy::manual_try_fold)] fn collect_results(
810 iter: impl Iterator<Item = ParsedField>,
811 default_field_type: FieldType,
812) -> Result<Vec<Field>, Vec<String>> {
813 iter.fold(Ok(vec![]), |v, i| match (v, i) {
814 (Ok(mut v), (ident, b, Ok((c, d)))) => {
815 v.push((ident, b, c.unwrap_or(default_field_type), d));
816 Ok(v)
817 }
818 (Ok(_), (ident, _, Err(_))) => Err(vec![ident]),
819 (Err(mut v), (ident, _, Err(_))) => {
820 v.push(ident);
821 Err(v)
822 }
823 (v @ Err(_), _) => v,
824 })
825}
826
827enum FieldTypeError {
828 Syn(syn::Error),
829 UnrecognizedJunkFound(Vec<Meta>),
832}
833
834impl From<syn::Error> for FieldTypeError {
835 fn from(value: syn::Error) -> Self {
836 FieldTypeError::Syn(value)
837 }
838}
839
840impl Display for FieldTypeError {
841 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
842 match self {
843 FieldTypeError::Syn(error) => write!(f, "{error}"),
844 FieldTypeError::UnrecognizedJunkFound(metas) => {
845 let metas = metas
846 .iter()
847 .map(|m| m.into_token_stream().to_string())
848 .collect::<Vec<_>>()
849 .join(" ");
850 write!(
851 f,
852 "expected a comma separated list of named values, got {metas}"
853 )
854 }
855 }
856 }
857}
858
859#[allow(clippy::manual_try_fold)] fn get_fieldtype_from_attrs(iter: impl Iterator<Item = Attribute>, attr_name: &str) -> ParsedAttrs {
869 for attr in iter {
870 if let Meta::List(MetaList { path, .. }) = &attr.meta {
871 let Path { segments, .. } = path;
872 if segments
873 .iter()
874 .map(|p| &p.ident)
875 .eq(["delta_struct"].iter().cloned())
876 {
877 let nested =
878 attr.parse_args_with(Punctuated::<Meta, Token!(,)>::parse_terminated)?;
879 let values: Result<Vec<_>, Vec<Meta>> = nested
880 .iter()
881 .map(|meta| match meta {
882 Meta::NameValue(MetaNameValue {
883 path,
884 value:
885 Expr::Lit(ExprLit {
886 lit: Lit::Str(s), ..
887 }),
888 ..
889 }) => Ok((path.get_ident().map(|i| i.to_string()), s.value())),
890 e => Err(e),
891 })
892 .fold(Ok(vec![]), |v, i| match (v, i) {
893 (Ok(mut v), Ok(i)) => {
894 v.push(i);
895 Ok(v)
896 }
897 (Ok(_), Err(e)) => Err(vec![e.clone()]),
898 (Err(mut v), Err(e)) => {
899 v.push(e.clone());
900 Err(v)
901 }
902 (v @ Err(_), _) => v,
903 });
904 let v = values.map_err(FieldTypeError::UnrecognizedJunkFound)?;
905 let mut field_type = None;
906 let mut delta_leader = String::new();
907 for i in v {
908 match i.0.as_deref() {
909 Some("delta_leader") => {
910 delta_leader = i.1;
911 }
912 a if Some(attr_name) == a => {
913 field_type = string_to_fieldtype(&i.1);
914 }
915 a => {
916 abort_call_site!("Unrecognized value {:?}", a);
917 }
918 }
919 }
920 return Ok((field_type, delta_leader));
921 }
922 }
923 }
924 Ok((None, String::new()))
925}
926
927fn string_to_fieldtype(s: &str) -> Option<FieldType> {
930 match s {
931 "ordered" => Some(FieldType::Ordered),
932 "unordered" => Some(FieldType::Unordered),
933 "unordered-delta" => Some(FieldType::UnorderedDelta),
934 "scalar" => Some(FieldType::Scalar),
935 "delta" => Some(FieldType::Delta),
936 _ => None,
937 }
938}
939
940#[proc_macro_derive(Fingerprint)]
961#[proc_macro_error]
962pub fn derive_fingerprint(input: TokenStream) -> TokenStream {
963 let DeriveInput {
964 ident,
965 mut generics,
966 data,
967 ..
968 } = parse_macro_input!(input as DeriveInput);
969
970 let body = match data {
971 Data::Struct(strukt) => {
972 fingerprint_calls(strukt.fields.iter().enumerate().map(|(i, field)| {
975 match &field.ident {
976 Some(ident) => quote!(self.#ident),
977 None => {
978 let index = syn::Index::from(i);
979 quote!(self.#index)
980 }
981 }
982 }))
983 }
984 Data::Enum(enom) => {
985 let arms = enom.variants.into_iter().enumerate().map(|(index, variant)| {
989 let variant_ident = variant.ident;
990 let bindings = binding_idents(&variant.fields);
991 let pattern = match &variant.fields {
992 Fields::Named(_) => quote!(Self::#variant_ident { #(#bindings),* }),
993 Fields::Unnamed(_) => quote!(Self::#variant_ident( #(#bindings),* )),
994 Fields::Unit => quote!(Self::#variant_ident),
995 };
996 let fields = fingerprint_calls(bindings.iter().map(|b| quote!(#b)));
997 let index = index as u32;
998 quote! {
999 #pattern => {
1000 ::delta_struct::Fingerprint::fingerprint(&#index, hasher);
1001 #fields
1002 }
1003 }
1004 });
1005 quote! {
1006 match self {
1007 #(#arms)*
1008 }
1009 }
1010 }
1011 _ => abort_call_site!(
1012 "delta_struct::Fingerprint may only be derived for struct and enum types. {} is neither.",
1013 ident
1014 ),
1015 };
1016
1017 let fingerprint_types = generics
1018 .type_params()
1019 .map(|t| t.ident.clone())
1020 .collect::<Vec<_>>();
1021 let where_clause = generics.make_where_clause();
1022 for ty in fingerprint_types {
1023 where_clause
1024 .predicates
1025 .push(syn::parse_quote!(#ty: ::delta_struct::Fingerprint));
1026 }
1027 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
1028
1029 TokenStream::from(quote! {
1030 impl #impl_generics ::delta_struct::Fingerprint for #ident #ty_generics #where_clause {
1031 fn fingerprint(&self, hasher: &mut ::delta_struct::fingerprint::Hasher) {
1032 #body
1033 }
1034 }
1035 })
1036}
1037
1038fn binding_idents(fields: &Fields) -> Vec<Ident> {
1041 fields
1042 .iter()
1043 .enumerate()
1044 .map(|(i, field)| match &field.ident {
1045 Some(ident) => ident.clone(),
1046 None => format_ident!("field_{}", i),
1047 })
1048 .collect()
1049}
1050
1051fn fingerprint_calls(
1056 exprs: impl Iterator<Item = proc_macro2::TokenStream>,
1057) -> proc_macro2::TokenStream {
1058 let calls = exprs.map(|expr| quote!(::delta_struct::Fingerprint::fingerprint(&#expr, hasher);));
1059 quote!(#(#calls)*)
1060}