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