procmeta_core/expand/
parser.rs1use 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}