token_parser_derive/
lib.rs1#![deny(missing_docs)]
2
3use proc_macro2::TokenStream;
15use quote::quote;
16use syn::{
17 Data, DataStruct, DeriveInput, Fields, FieldsNamed, FieldsUnnamed, GenericParam, Generics,
18 Ident, TypeParamBound, parse_macro_input, parse_quote,
19};
20
21#[proc_macro_derive(Parsable)]
27pub fn derive_default_parsable(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
28 derive_parsable(input, false)
29}
30
31#[proc_macro_derive(SymbolParsable)]
39pub fn derive_symbol_parsable(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
40 derive_parsable(input, true)
41}
42
43fn add_trait_bounds(mut generics: Generics, bound: TypeParamBound) -> Generics {
44 for param in &mut generics.params {
45 if let GenericParam::Type(type_param) = param {
46 type_param.bounds.push(bound.clone());
47 }
48 }
49 generics
50}
51
52fn derive_parsable(input: proc_macro::TokenStream, symbol: bool) -> proc_macro::TokenStream {
53 let input = parse_macro_input!(input as DeriveInput);
54 let name = input.ident;
55
56 let trait_bound = if symbol {
57 parse_quote!(::std::str::FromStr)
58 } else {
59 parse_quote!(::token_parser::Parsable<C>)
60 };
61
62 let (_, ty_generics, _) = input.generics.split_for_impl();
63
64 let mut generics = add_trait_bounds(input.generics.clone(), trait_bound);
65 generics
66 .params
67 .push(parse_quote!(C: ::token_parser::Context));
68 let (impl_generics, _, where_clause) = generics.split_for_impl();
69
70 let body = match input.data {
71 Data::Struct(data) => struct_body(&name, data, symbol),
72 Data::Enum(_) => panic!("Deriving Parsable is not yet supported for enums"),
73 Data::Union(_) => panic!("Deriving Parsable is not supported for unions"),
74 };
75
76 let expanded = quote! {
77 impl #impl_generics ::token_parser::Parsable<C> for #name #ty_generics #where_clause {
78 #body
79 }
80 };
81
82 proc_macro::TokenStream::from(expanded)
83}
84
85fn struct_body(name: &Ident, data: DataStruct, symbol: bool) -> TokenStream {
86 if symbol {
87 let Fields::Unnamed(fields) = data.fields else {
88 panic!("SymbolParsable requires a tuple struct");
89 };
90 return symbol_tuple(name, &fields);
91 }
92 match data.fields {
93 Fields::Unnamed(fields) => parse_list_tuple(name, &fields),
94 Fields::Named(fields) => parse_list_named(name, &fields),
95 Fields::Unit => quote! {
96 fn parse_list(_parser: &mut ::token_parser::Parser, _context: &C) -> ::token_parser::Result<Self> {
97 Ok(Self)
98 }
99 },
100 }
101}
102
103fn parse_list_tuple(name: &Ident, fields: &FieldsUnnamed) -> TokenStream {
104 let exprs = fields.unnamed.iter().enumerate().map(|(position, _)| {
105 let description = format!("field {position} of `{name}`");
106 quote! {
107 parser
108 .parse_next(context)
109 .map_err(|error| error.context(#description))?
110 }
111 });
112 quote! {
113 fn parse_list(parser: &mut ::token_parser::Parser, context: &C) -> ::token_parser::Result<Self> {
114 Ok(Self( #( #exprs, )* ))
115 }
116 }
117}
118
119fn parse_list_named(name: &Ident, fields: &FieldsNamed) -> TokenStream {
120 let names: Vec<&Ident> = fields
121 .named
122 .iter()
123 .map(|f| f.ident.as_ref().unwrap())
124 .collect();
125 let labels: Vec<String> = names.iter().map(|n| Ident::to_string(n)).collect();
126
127 let arms = names.iter().zip(labels.iter()).map(|(field, label)| {
128 let description = format!("field `{label}` of `{name}`");
129 quote! {
130 #label => {
131 result.#field = sub
132 .parse_next(context)
133 .map_err(|error| error.context(#description))?;
134 }
135 }
136 });
137
138 let type_description = format!("`{name}`");
139 quote! {
140 fn parse_list(parser: &mut ::token_parser::Parser, context: &C) -> ::token_parser::Result<Self> {
141 let mut result = <Self as ::std::default::Default>::default();
142 for element in parser {
143 let mut sub = element?;
144 let field_name: Box<str> = sub.parse_next(context)?;
145 match field_name.as_ref() {
146 #( #arms )*
147 _ => {
148 return Err(::token_parser::Error::from(
149 ::token_parser::ErrorKind::UnknownField(field_name),
150 )
151 .at(sub.span())
152 .context(#type_description));
153 }
154 }
155 }
156 Ok(result)
157 }
158 }
159}
160
161fn symbol_tuple(name: &Ident, fields: &FieldsUnnamed) -> TokenStream {
162 let exprs = fields.unnamed.iter().map(|field| {
163 let field_type = &field.ty;
164 quote! {
165 name.parse().map_err(|_| ::token_parser::ErrorKind::StringParsing(stringify!(#field_type)))?
166 }
167 });
168 quote! {
169 fn parse_symbol(name: Box<str>, _span: ::token_parser::Span, _context: &C) -> ::token_parser::Result<Self> {
170 Ok(#name( #( #exprs, )* ))
171 }
172 }
173}