iced_anim_derive/
lib.rs

1//! Derive macro for the `Animate` trait in `iced_anim`.
2//!
3//! This makes it easy to animate your own custom structs. Ensure each field in your struct
4//! already implements the `Animate` trait to derive it automatically. See the documentation
5//! for `iced_anim` for usage and more details.
6extern crate proc_macro;
7extern crate syn;
8#[macro_use]
9extern crate quote;
10
11use proc_macro::TokenStream;
12use syn::{parse_macro_input, Data, DeriveInput, Fields};
13
14/// Derive macro generating an impl of the trait `Animate`.
15#[proc_macro_derive(Animate)]
16pub fn animate_derive(input: TokenStream) -> TokenStream {
17    // Parse the input tokens into a syntax tree
18    let input = parse_macro_input!(input as DeriveInput);
19
20    // Extract the identifier and data from the input
21    let name = input.ident;
22    let data = input.data;
23
24    // TODO: Support other types of data structures
25    let Data::Struct(data_struct) = data else {
26        panic!("Animate can only be derived for structs");
27    };
28
29    let Fields::Named(fields) = data_struct.fields else {
30        panic!("Animate can only be derived for structs with named fields");
31    };
32
33    let component_fields = fields.named.iter().map(|f| {
34        let ty = &f.ty;
35        quote! {
36            total += <#ty as ::iced_anim::Animate>::components();
37        }
38    });
39
40    let update_fields = fields.named.iter().map(|f| {
41        let name = &f.ident;
42        quote! {
43            ::iced_anim::Animate::update(&mut self.#name, components);
44        }
45    });
46
47    let distance_fields = fields.named.iter().map(|f| {
48        let name = &f.ident;
49        quote! {
50            distances.push(::iced_anim::Animate::distance_to(&self.#name, &end.#name));
51        }
52    });
53
54    let lerp_fields = fields.named.iter().map(|f| {
55        let name = &f.ident;
56        quote! {
57            ::iced_anim::Animate::lerp(&mut self.#name, &start.#name, &end.#name, progress);
58        }
59    });
60
61    let impl_gen = quote! {
62        impl ::iced_anim::Animate for #name {
63            fn components() -> usize {
64                let mut total = 0;
65                #(#component_fields)*
66                total
67            }
68
69            fn update(&mut self, components: &mut impl Iterator<Item = ::core::primitive::f32>) {
70                #(#update_fields)*
71            }
72
73            fn distance_to(&self, end: &Self) -> ::std::vec::Vec<::core::primitive::f32> {
74                let mut distances = ::std::vec::Vec::with_capacity(Self::components());
75                #(#distance_fields)*
76                distances.concat()
77            }
78
79            fn lerp(&mut self, start: &Self, end: &Self, progress: ::core::primitive::f32) {
80                #(#lerp_fields)*
81            }
82        }
83    };
84
85    TokenStream::from(impl_gen)
86}