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}