teleform_derive/
lib.rs

1//! Provides derive macros for `teleform`.
2use std::collections::HashSet;
3
4use quote::quote;
5use syn::{Data, DataStruct, DeriveInput, Fields, FieldsNamed, Index, TypeTuple};
6
7struct Composite {
8    function_body: proc_macro2::TokenStream,
9    where_constraints: Vec<proc_macro2::TokenStream>,
10}
11
12fn get_composite(input: &DeriveInput) -> syn::Result<Composite> {
13    let name = &input.ident;
14    let fields = match &input.data {
15        Data::Struct(DataStruct {
16            fields: Fields::Named(FieldsNamed { named, .. }),
17            ..
18        }) => named,
19        _ => {
20            return Err(syn::Error::new(
21                name.span(),
22                "deriving TeleSync only supports structs with named fields".to_string(),
23            ));
24        }
25    };
26
27    let where_constraints: Vec<_> = fields
28        .iter()
29        .map(|field| &field.ty)
30        .collect::<HashSet<_>>()
31        .into_iter()
32        .map(|ty| {
33            quote! {
34                #ty: tele::HasDependencies
35            }
36        })
37        .collect();
38    let composites: Vec<_> = fields
39        .iter()
40        .map(|field| {
41            // UNWRAP: safe because we only support structs (which all have named fields)
42            let ident = field.ident.clone().unwrap();
43            quote! {
44                .merge(self.#ident.dependencies())
45            }
46        })
47        .collect();
48    let function_body = quote! {
49        tele::Dependencies::default()
50            #(#composites)*
51    };
52    Ok(Composite {
53        where_constraints,
54        function_body,
55    })
56}
57
58#[proc_macro_derive(HasDependencies)]
59pub fn derive_has_dependencies(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
60    let input: DeriveInput = syn::parse_macro_input!(input);
61    let name = &input.ident;
62
63    let Composite {
64        function_body: composite,
65        where_constraints,
66    } = match get_composite(&input) {
67        Ok(c) => c,
68        Err(e) => return e.into_compile_error().into(),
69    };
70    let output = quote! {
71        impl tele::HasDependencies for #name
72        where
73            #(#where_constraints),*
74        {
75            fn dependencies(&self) -> tele::Dependencies {
76                #composite
77            }
78        }
79    };
80    output.into()
81}
82
83#[proc_macro]
84pub fn impl_has_dependencies_tuples(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
85    let tuple: TypeTuple = syn::parse_macro_input!(input);
86    let tys = tuple.elems.iter().collect::<Vec<_>>();
87    let deps = tys
88        .iter()
89        .enumerate()
90        .map(|(i, _)| {
91            let ndx = Index::from(i);
92            quote! {
93                .merge(self.#ndx.dependencies())
94            }
95        })
96        .collect::<Vec<_>>();
97    let output = quote! {
98        impl<#(#tys),*> tele::HasDependencies for #tuple
99        where
100            #(#tys: tele::HasDependencies),*,
101        {
102            fn dependencies(&self) -> tele::Dependencies {
103                tele::Dependencies::default()
104                    #(#deps)*
105
106            }
107        }
108    };
109    output.into()
110}