zng_var_proc_macros/
transitionable.rs

1use syn::{spanned::Spanned, *};
2
3pub fn expand(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
4    let input = parse_macro_input!(input as DeriveInput);
5
6    let mut errors = crate::util::Errors::default();
7    let mut lerp_block = quote!();
8
9    let crate_ = quote!(::zng_var);
10    let path = quote!(#crate_::animation::Transitionable);
11
12    // params: self, to, step
13    match input.data {
14        Data::Struct(s) => match s.fields {
15            Fields::Named(f) => {
16                let f = f.named.iter().map(|f| {
17                    let ident = &f.ident;
18                    quote_spanned! {f.span()=>
19                        #ident: #path::lerp(self.#ident, &to.#ident, step)
20                    }
21                });
22                lerp_block = quote! {
23                    {
24                        Self {
25                            #(#f,)*
26                        }
27                    }
28                }
29            }
30            Fields::Unnamed(f) => {
31                let f = f.unnamed.iter().enumerate().map(|(i, f)| {
32                    let index = Index::from(i);
33                    quote_spanned! {f.span()=>
34                        #path::lerp(self.#index, &to.#index, step)
35                    }
36                });
37                lerp_block = quote! {
38                    {
39                        Self(#(#f),*)
40                    }
41                }
42            }
43            Fields::Unit => {
44                lerp_block = quote! {
45                    {
46                        let _ = (to, step);
47                        self
48                    }
49                }
50            }
51        },
52        Data::Enum(e) => {
53            errors.push("cannot derive `Transitionable` for enums", e.enum_token.span);
54        }
55        Data::Union(u) => {
56            errors.push("cannot derive `Transitionable` for unions", u.union_token.span);
57        }
58    }
59
60    let out = if errors.is_empty() {
61        let ident = input.ident;
62
63        let mut generics = input.generics;
64        for param in &mut generics.params {
65            if let GenericParam::Type(ref mut type_param) = *param {
66                type_param.bounds.push(parse_quote!(#path));
67            }
68        }
69        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
70
71        quote! {
72            impl #impl_generics #path for #ident #ty_generics #where_clause {
73                fn lerp(self, to: &Self, step: #crate_::animation::easing::EasingStep) -> Self #lerp_block
74            }
75        }
76    } else {
77        quote! {
78            #errors
79        }
80    };
81
82    out.into()
83}