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