procmeta_core/expand/
parser.rs

1use std::collections::HashMap;
2
3use heck::ToSnakeCase;
4use proc_macro2::{Ident, Span, TokenStream};
5use quote::quote;
6use syn::{Attribute, Data, DeriveInput, FieldsNamed, Generics, LitStr, Type};
7
8use crate::util::{type_add_colon2, type_is_option};
9
10pub fn find_attr_name(attrs: &Vec<Attribute>) -> Option<String> {
11    for attr in attrs {
12        let name = attr.meta.require_list().unwrap();
13        if name.path.is_ident("name") {
14            let name: LitStr = name.parse_args().unwrap();
15            return Some(name.value());
16        }
17    }
18    None
19}
20
21pub fn find_attr_converter(attrs: &Vec<Attribute>) -> Option<Type> {
22    for attr in attrs {
23        let ty = attr.meta.require_list().unwrap();
24        if ty.path.is_ident("converter") {
25            let ty: Type = ty.parse_args().unwrap();
26            return Some(ty);
27        }
28    }
29    None
30}
31
32pub struct FieldsNamedParser {
33    pub path_ident: Ident,
34    pub let_def_token: TokenStream,
35    pub assign_token: TokenStream,
36    pub if_field_token: TokenStream,
37}
38
39impl FieldsNamedParser {
40    pub fn new(named: FieldsNamed) -> Self {
41        let path_ident = Ident::new("__syn_path", Span::call_site());
42        let mut let_def_token = quote!();
43        let mut assign_token = quote!();
44        let mut if_field_token = quote!();
45
46        for named_item in named.named {
47            let converter_ty = find_attr_converter(&named_item.attrs);
48            let named_ident = named_item.ident;
49            let named_ident_str = named_ident.as_ref().map(|t| t.to_string());
50            let mut named_ty = named_item.ty;
51            let is_option = type_is_option(&named_ty);
52            type_add_colon2(&mut named_ty);
53
54            let parse_token = match converter_ty {
55                Some(c_ty) => {
56                    quote!(#c_ty :: convert(&stream)?)
57                }
58                None => {
59                    quote!(#named_ty :: try_from_expr(stream.parse()?)?)
60                }
61            };
62
63            if is_option {
64                let_def_token = quote! {
65                    #let_def_token
66                    let mut #named_ident: #named_ty = None;
67                };
68                if_field_token = quote! {
69                    #if_field_token
70                    if #path_ident .is_ident(#named_ident_str) {
71                        #named_ident = #parse_token;
72                        if stream.is_empty() {
73                            break;
74                        }
75                        let _comma: syn::Token![,] = stream.parse()?;
76                        continue;
77                    }
78                };
79                assign_token = quote! {
80                    #assign_token
81                    #named_ident,
82                };
83            } else {
84                let_def_token = quote! {
85                    #let_def_token
86                    let mut #named_ident: Option<#named_ty> = None;
87                };
88                if_field_token = quote! {
89                    #if_field_token
90                    if #path_ident .is_ident(#named_ident_str) {
91                        #named_ident = Some(#parse_token);
92                        if stream.is_empty() {
93                            break;
94                        }
95                        let _comma: syn::Token![,] = stream.parse()?;
96                        continue;
97                    }
98                };
99                let message = format!("missing`{}`", named_ident_str.unwrap_or_default());
100                assign_token = quote! {
101                    #assign_token
102                    #named_ident: #named_ident .ok_or_else(|| {
103                        syn::Error::new(stream.span(), #message)
104                    })?,
105                };
106            }
107        }
108        Self {
109            path_ident,
110            let_def_token,
111            assign_token,
112            if_field_token,
113        }
114    }
115
116    pub fn impl_syn_parse(&self, ty: &Ident) -> TokenStream {
117        let path_ident = &self.path_ident;
118        let let_def_token = &self.let_def_token;
119        let if_field_token = &self.if_field_token;
120        let assign_token = &self.assign_token;
121        quote! {
122            impl syn::parse::Parse for #ty {
123                fn parse(stream: ParseStream) -> Result<Self> {
124                    #let_def_token
125                    while !stream.is_empty() {
126                        let #path_ident: Path = stream.parse()?;
127                        let _eq_token: Token![=] = stream.parse()?;
128                        #if_field_token
129                    }
130                    Ok(Self { #assign_token })
131                }
132            }
133        }
134    }
135
136    pub fn impl_meta_parse(&self, ty: &Ident, generics: &Generics) -> TokenStream {
137        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
138        quote! {
139            impl #impl_generics MetaParser for #ty #ty_generics #where_clause {
140
141                fn parse(meta: &Meta) -> syn::Result<Self> {
142                    let list = meta.require_list()?;
143                    list.parse_args_with(|stream: ParseStream| -> syn::Result<Self> {
144                        <Self as syn::parse::Parse> :: parse(stream)
145                    })
146                }
147            }
148        }
149    }
150}
151
152pub fn expand(input: DeriveInput) -> TokenStream {
153    let ty = input.ident;
154    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
155    match input.data {
156        Data::Struct(data) => match data.fields {
157            syn::Fields::Named(named) => {
158                let parser = FieldsNamedParser::new(named);
159                let impl_syn_parse = parser.impl_syn_parse(&ty);
160                let impl_meta_parse = parser.impl_meta_parse(&ty, &input.generics);
161                quote! {
162                    #impl_syn_parse
163
164                    #impl_meta_parse
165                }
166            },
167            syn::Fields::Unnamed(_) => unimplemented!(),
168            syn::Fields::Unit => unimplemented!(),
169        },
170        Data::Enum(data) => {
171            let mut result_token = quote!();
172            let mut group_map: HashMap<String, TokenStream> = HashMap::new();
173            for var_item in data.variants {
174                let var_ident = var_item.ident;
175                let attr_name = find_attr_name(&var_item.attrs)
176                    .unwrap_or(var_ident.to_string().to_snake_case());
177                let return_token;
178                match var_item.fields {
179                    syn::Fields::Named(named) => {
180                        let fields_named_parser = FieldsNamedParser::new(named);
181                        let let_def_token = fields_named_parser.let_def_token;
182                        let if_field_token = fields_named_parser.if_field_token;
183                        let assign_token = fields_named_parser.assign_token;
184                        let path_ident = fields_named_parser.path_ident;
185                        return_token = quote! {
186                            let result = meta.require_list();
187                            if let Ok(list) = result {
188                                let result = list.parse_args_with(|stream: ParseStream| -> syn::Result<Self #ty_generics> {
189                                    #let_def_token
190                                    loop {
191                                        if stream.is_empty() {
192                                            break;
193                                        }
194                                        let #path_ident : Path = stream.parse()?;
195                                        let _eq_token: Token![=] = stream.parse()?;
196                                        #if_field_token
197                                    }
198                                    Ok(Self :: #var_ident {
199                                        #assign_token
200                                    })
201                                });
202                                if let Ok(result) = result {
203                                    return Ok(result);
204                                }
205                            }
206                        };
207                    }
208                    syn::Fields::Unnamed(unamed) => {
209                        let mut let_def_token = quote!();
210                        let mut assign_token = quote!();
211                        let mut if_field_token = quote!();
212                        let mut index = 0;
213                        for unamed_item in unamed.unnamed {
214                            index += 1;
215                            let mut unamed_ty = unamed_item.ty;
216                            let converter_ty = find_attr_converter(&unamed_item.attrs);
217                            type_add_colon2(&mut unamed_ty);
218                            let unamed_ident =
219                                Ident::new(format!("uname_{index}").as_str(), Span::call_site());
220                            let_def_token = quote! {
221                                #let_def_token
222                                let mut #unamed_ident: Option<#unamed_ty> = None;
223                            };
224                            let parse_token = match converter_ty {
225                                Some(ty) => {
226                                    quote!(#ty ::convert(&stream)?)
227                                }
228                                None => {
229                                    quote!(stream.parse()?)
230                                }
231                            };
232                            if_field_token = quote! {
233                                #if_field_token
234                                if index == #index {
235                                    #unamed_ident = Some(#parse_token);
236                                }
237                            };
238                            assign_token = quote! {
239                                #assign_token
240                                #unamed_ident .ok_or_else(|| {
241                                    syn::Error::new(stream.span(), "Expected list")
242                                })?,
243                            };
244                        }
245                        return_token = quote! {
246                            let result = meta.require_list();
247                            if let Ok(list) = result {
248                                let result = list.parse_args_with(|stream: ParseStream| -> syn::Result<Self #ty_generics> {
249                                    #let_def_token
250                                    let mut index = 0;
251                                    loop {
252                                        if stream.is_empty() {
253                                            break;
254                                        }
255                                        index += 1;
256                                        #if_field_token
257                                        if stream.is_empty() {
258                                            break;
259                                        }
260                                        let _comma: syn::Token![,] = stream.parse()?;
261                                    }
262                                    Ok(Self :: #var_ident(#assign_token) )
263                                });
264                                if let Ok(result) = result {
265                                    return Ok(result);
266                                }
267                            }
268                        };
269                    }
270                    syn::Fields::Unit => {
271                        return_token = quote! {
272                            let result = meta.require_path_only();
273                            if result.is_ok() {
274                                return Ok(Self :: #var_ident);
275                            }
276                        };
277                    }
278                }
279                let taked_return_token = group_map.get(&attr_name);
280                match taked_return_token {
281                    Some(taked_return_token) => {
282                        group_map.insert(
283                            attr_name,
284                            quote! {
285                                #taked_return_token
286                                #return_token
287                            },
288                        );
289                    }
290                    None => {
291                        group_map.insert(attr_name, return_token);
292                    }
293                }
294            }
295            for (attr_name, return_token) in group_map {
296                result_token = quote! {
297                    #result_token
298                    if __syn_meta_path.is_ident(#attr_name) {
299                        #return_token;
300                    }
301                };
302            }
303            quote! {
304                impl #impl_generics MetaParser for #ty #ty_generics #where_clause {
305
306                    fn parse(meta: &Meta) -> syn::Result<Self> {
307                        let __syn_meta_path = meta.path();
308                        #result_token
309                        Err(Error::new(Span::call_site(), "unrecognized writing method"))
310                    }
311                }
312
313                impl syn::parse::Parse for #ty #ty_generics #where_clause {
314                    fn parse(stream: syn::parse::ParseStream) -> syn::Result<Self> {
315                        let meta: Meta = stream.parse()?;
316                        MetaParser::parse(&meta)
317                    }
318                }
319
320                impl TryFrom<&Attribute> for #ty #ty_generics #where_clause {
321                    type Error = syn::Error;
322
323                    fn try_from(attr: &Attribute) -> syn::Result<Self> {
324                        <Self as MetaParser>::parse(&(attr.meta))
325                    }
326                }
327
328            }
329        }
330        Data::Union(_) => unimplemented!(),
331    }
332}