parse_more_macros/
lib.rs

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