anim_derive/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use proc_macro2::Span;
5use proc_macro_crate::{crate_name, FoundCrate};
6use proc_quote::quote;
7use syn::parse_macro_input;
8use syn::DeriveInput;
9use syn::{Data, DataStruct, Fields, Ident, Type};
10
11/// the macro derives `anim::Animatable` for you automatically.
12#[proc_macro_derive(Animatable, attributes(tag))]
13pub fn animatable_derive(input: TokenStream) -> TokenStream {
14    let input = parse_macro_input!(input as DeriveInput);
15    expand_derive(input)
16        .unwrap_or_else(syn::Error::into_compile_error)
17        .into()
18}
19
20fn expand_derive(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
21    let anim = get_crate()?;
22    let fields = get_fields(input.data)
23        .unwrap()
24        .iter()
25        .map(|(field_name, _)| {
26            Ok(quote! {
27                res.#field_name = #anim::Animatable::animate(&self.#field_name,&to.#field_name, time);
28            })
29        })
30        .collect::<syn::Result<proc_macro2::TokenStream>>()?;
31    let st_name = input.ident;
32
33    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
34    Ok(quote! {
35        impl  #impl_generics #anim::Animatable for #st_name #ty_generics #where_clause
36         {
37            #[inline]
38            fn animate(&self, to: &Self, time: f64) -> Self{
39                let mut res = self.clone();
40                #fields
41                res
42            }
43        }
44    })
45}
46
47fn get_crate() -> syn::Result<Ident> {
48    let anim = match crate_name("anim") {
49        Ok(found) => match found {
50            FoundCrate::Itself => Ident::new("crate", Span::call_site()),
51            FoundCrate::Name(name) => Ident::new(&name, Span::call_site()),
52        },
53        Err(_) => Ident::new("crate", Span::call_site()),
54    };
55    Ok(anim)
56}
57
58fn get_fields(data: Data) -> syn::Result<Vec<(Ident, Type)>> {
59    let fields = match data {
60        Data::Struct(DataStruct {
61            fields: Fields::Named(fields),
62            ..
63        }) => fields.named,
64        _ => panic!("this derive macro only works on structs with named fields"),
65    };
66    let items = fields
67        .into_iter()
68        .map(|f| {
69            let field_name = f.ident.unwrap();
70            let ty = f.ty;
71            (field_name, ty)
72        })
73        .collect();
74
75    Ok(items)
76}