beans_derive/
lib.rs

1use proc_macro2::TokenStream;
2use quote::{quote, quote_spanned};
3use syn::{
4    parse_macro_input, parse_quote, spanned::Spanned, Data, DeriveInput, Error, Fields,
5    GenericParam, Generics, Result,
6};
7
8#[proc_macro_derive(Readable, attributes())]
9pub fn derive_readable(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
10    let input = parse_macro_input!(input as DeriveInput);
11    let name = &input.ident;
12    let generics = add_trait_bounds(input.generics.clone());
13    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
14    let result = compute_self(&input).unwrap_or_else(|err| err.to_compile_error());
15    let expanded = quote! {
16    impl #impl_generics ::beans::typed::Readable for #name #ty_generics #where_clause {
17        fn read(ast: ::beans::parser::AST) -> Self {
18        let mut node = ::beans::node!(ast);
19        #result
20        }
21    }
22    };
23
24    proc_macro::TokenStream::from(expanded)
25}
26
27fn add_trait_bounds(mut generics: Generics) -> Generics {
28    for param in generics.params.iter_mut() {
29        if let GenericParam::Type(ref mut type_param) = *param {
30            type_param
31                .bounds
32                .push(parse_quote!(::beans::typed::Readable));
33        }
34    }
35    generics
36}
37
38fn compute_self(node: &DeriveInput) -> Result<TokenStream> {
39    Ok(match &node.data {
40        Data::Struct(data) => match data.fields {
41            Fields::Named(ref fields) => {
42                let recurse = fields.named.iter().map(|f| {
43                    let name = &f.ident;
44                    quote_spanned! {
45                    f.span() =>
46                        #name: ::beans::typed::Readable::read(
47                        ::beans::get!(node => #name)
48                        )
49                    }
50                });
51                quote! {
52                    Self {
53                    #(#recurse),*
54                    }
55                }
56            }
57            Fields::Unnamed(_) => todo!(),
58            Fields::Unit => {
59                quote! {
60                    Self {}
61                }
62            }
63        },
64        Data::Enum(data) => {
65            let recurse = data.variants.iter().map(|variant| {
66                let name = &variant.ident;
67                let constructor = match variant.fields {
68                    Fields::Named(ref fields) => {
69                        let recurse = fields.named.iter().map(|f| {
70                            let name = &f.ident;
71                            quote_spanned! {
72                            f.span() =>
73                                #name: ::beans::typed::Readable::read(
74                                ::beans::get!(node => #name)
75                                )
76                            }
77                        });
78                        quote!({#(#recurse),*})
79                    }
80                    Fields::Unnamed(_) => todo!(),
81                    Fields::Unit => quote!({}),
82                };
83                quote_spanned! {
84                    variant.span() =>
85                    Self::#name #constructor
86                }
87            });
88            quote! {
89            ::beans::match_variant! {(node) {
90                #(#recurse),*
91            }}
92            }
93        }
94        Data::Union(_) => {
95            return Err(Error::new_spanned(
96                node,
97                "Union as AST trees are not supported",
98            ));
99        }
100    })
101}