parse_more_macros/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::Span;
3use quote::{format_ident, quote};
4use syn::{parse_quote, spanned::Spanned, Attribute, ItemEnum, ItemStruct};
5
6/// Auto-implements the parse_more::ParseMore trait on an item.
7///
8/// # Example
9/// ```rust, ignore
10/// use parse_more::{parse_more, filler};
11/// use syn::{Ident, Expr, Token};
12///
13/// #[parse_more]
14/// struct Foo {
15///     name: Ident,
16///     #[filler(Token![=>])]
17///     values: (Expr, Expr),
18/// }
19/// ```
20#[proc_macro_attribute]
21pub fn parse_more(_args: TokenStream, input: TokenStream) -> TokenStream {
22    if let Ok(mut struct_item) = syn::parse::<ItemStruct>(input.clone()) {
23        let struct_ident = &struct_item.ident;
24
25        let mut named_generics = struct_item.generics.clone();
26        named_generics.params.iter_mut().for_each(|param| match param {
27            syn::GenericParam::Type(type_param) => type_param.default = None,
28            syn::GenericParam::Const(const_param) => const_param.default = None,
29            _ => {}
30        });
31        let mut generics = named_generics.clone();
32        generics.params.iter_mut().for_each(|param| match param {
33            syn::GenericParam::Type(type_param) => {
34                type_param.bounds.push(syn::TypeParamBound::Trait(parse_quote!(parse_more::ParseMore)));
35            },
36            _ => {}
37        });
38
39        let mut parsed = vec![];
40        let mut field_idents = vec![];
41
42        let edit_attributes = |attrs: &mut Vec<Attribute>, parsed: &mut Vec<_>| {
43            // Remove filler attributes and parse the corresponding item.
44            attrs.iter().cloned().enumerate().filter_map(|(i, attribute)| {
45                if attribute.path().segments.last().is_some_and(|arg | arg.ident == "filler") {
46                    let path = attribute.path();
47                    let path_struct = format_ident!("__AvoidImportWarning{}", i);
48                    match attribute.parse_args::<syn::Type>() {
49                        Err(e) => Some(Err(e)),
50                        Ok(ty) => {
51                            parsed.push(quote! {
52                                input.parse::<parse_more::ParseMoreWrapper<#ty>>()?;
53                                #[#path]
54                                struct #path_struct;
55                            });
56                            None
57                        }
58                    }
59                } else {
60                    Some(Ok(attribute))
61                }
62            }).collect::<Result<Vec<_>, _>>()
63        };
64
65        for field in &mut struct_item.fields {
66            let field_ident = &field.ident;
67            let field_type = &field.ty;
68
69            // Remove filler attributes and parse the corresponding item.
70            field.attrs = match edit_attributes(&mut field.attrs, &mut parsed) {
71                Ok(attrs) => attrs,
72                Err(e) => return e.into_compile_error().into(),
73            };
74
75            // Add the current field to fields initialization and parse it.
76            field_idents.push(field_ident.clone());
77            parsed.push(quote! {
78                let #field_ident = input.parse::<parse_more::ParseMoreWrapper<#field_type>>()?.0;
79            });
80        }
81
82        quote! {
83            #struct_item
84            impl #generics parse_more::ParseMore for #struct_ident #named_generics {
85                fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
86                    #(#parsed)*
87                    Ok(Self {
88                        #(#field_idents),*
89                    })
90                }
91            }
92        }
93    } else if let Ok(enum_item) = syn::parse::<ItemEnum>(input) {
94        let enum_ident = &enum_item.ident;
95
96        let mut named_generics = enum_item.generics.clone();
97        named_generics.params.iter_mut().for_each(|param| match param {
98            syn::GenericParam::Type(type_param) => type_param.default = None,
99            syn::GenericParam::Const(const_param) => const_param.default = None,
100            _ => {}
101        });
102        let mut generics = named_generics.clone();
103        generics.params.iter_mut().for_each(|param| match param {
104            syn::GenericParam::Type(type_param) => {
105                type_param.bounds.push(syn::TypeParamBound::Trait(parse_quote!(parse_more::ParseMore)));
106            },
107            _ => {}
108        });
109
110        let mut parsed = vec![];
111
112        for (i, variant) in enum_item.variants.iter().enumerate() {
113            let variant_ident = &variant.ident;
114            // If the variant fields are not of len 1, we don't know how to parse it (Using Concat, Tuple, Either) ?
115            // So we raise an error.
116            if variant.fields.len() != 1 {
117                return syn::Error::new(variant.fields.span(), "ParseMore derive macro require that enum variants contains exactly one type.").into_compile_error().into();
118            }
119            let variant_type = &variant.fields.iter().next().unwrap().ty;
120
121            // If it's the first iteration, err isn't already initialized.
122            let error = if i == 0 {
123                quote! {
124                    err = e
125                }
126            } else {
127                quote! {
128                    err.combine(e)
129                }
130            };
131            // Try parse the current variant, return it if success (by parsing it again since we parsed it in a fork of the input,
132            // to avoid advancing the cursor in case of a well formed start and then a failure).
133            parsed.push(quote! {
134                match input.fork().parse::<parse_more::ParseMoreWrapper<#variant_type>>() {
135                    Ok(_) => return Ok(Self::#variant_ident(input.parse::<parse_more::ParseMoreWrapper<#variant_type>>().unwrap().0)),
136                    Err(e) => #error,
137                }
138            });
139        }
140
141        quote! {
142            #enum_item
143            impl #generics parse_more::ParseMore for #enum_ident #named_generics {
144                fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
145                    let mut err;
146                    #(#parsed)*
147                    Err(err)
148                }
149            }
150        }
151    } else {
152        syn::Error::new(
153            Span::call_site(),
154            "This macro attribute must be applied on a struct or an enum",
155        )
156        .into_compile_error()
157    }
158    .into()
159}
160
161/// Parse an additionnal type between two struct fields.
162///
163/// # Example
164/// ```rust, ignore
165/// use parse_more::{parse_more, filler};
166/// use syn::{Ident, Expr, Token};
167///
168/// #[parse_more]
169/// struct Foo {
170///     name: Ident,
171///     #[filler(Token![=>])]
172///     values: (Expr, Expr),
173/// }
174/// ```
175#[proc_macro_attribute]
176pub fn filler(_args: TokenStream, _input: TokenStream) -> TokenStream {
177    TokenStream::new()
178}