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 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}