token_parser_derive/
lib.rs1#![deny(missing_docs)]
2
3use quote::quote;
10use syn::{
11 Data, DeriveInput, Fields, GenericParam, Generics, TypeParamBound, parse_macro_input,
12 parse_quote,
13};
14
15#[proc_macro_derive(Parsable)]
21pub fn derive_default_parsable(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
22 derive_parsable(input, false)
23}
24
25#[proc_macro_derive(SymbolParsable)]
33pub fn derive_symbol_parsable(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
34 derive_parsable(input, true)
35}
36
37fn add_trait_bounds(mut generics: Generics, bound: TypeParamBound) -> Generics {
38 for param in &mut generics.params {
39 if let GenericParam::Type(ref mut type_param) = *param {
40 type_param.bounds.push(bound.clone());
41 }
42 }
43 generics
44}
45
46fn derive_parsable(input: proc_macro::TokenStream, symbol: bool) -> proc_macro::TokenStream {
47 let input = parse_macro_input!(input as DeriveInput);
48 let name = input.ident;
49
50 let trait_bound = if symbol {
51 parse_quote!(std::str::FromStr)
52 } else {
53 parse_quote!(token_parser::Parsable)
54 };
55
56 let generics = add_trait_bounds(input.generics.clone(), trait_bound);
57 let (_, ty_generics, where_clause) = generics.split_for_impl();
58
59 let mut generics = input.generics;
60 generics.params.push(parse_quote!(C: token_parser::Context));
61 let (impl_generics, _, _) = generics.split_for_impl();
62
63 let Data::Struct(data) = input.data else {
64 panic!("Deriving parser is only available for structs");
65 };
66
67 match data.fields {
68 Fields::Unnamed(fields) => {
69 let expression = if symbol {
70 quote! {
71 name.parse()
72 .map_err(|_| token_parser::Error::StringParsing)?
73 }
74 } else {
75 quote!(parser.parse_next(context)?)
76 };
77
78 let expressions = fields.unnamed.into_iter().map(|_| &expression);
79
80 let method = if symbol {
81 quote! {
82 fn parse_symbol(name: Box<str>, context: &C) -> token_parser::Result<Self>
83 }
84 } else {
85 quote! {
86 fn parse_list(parser: &mut token_parser::Parser, context: &C) -> token_parser::Result<Self>
87 }
88 };
89
90 let expanded = quote! {
91 impl #impl_generics token_parser::Parsable<C> for #name #ty_generics #where_clause {
92 #method
93 {
94 Ok(#name(#( #expressions, )*))
95 }
96 }
97 };
98
99 proc_macro::TokenStream::from(expanded)
100 }
101 _ => panic!("Deriving parser is only available for tuple structs"),
102 }
103}