rune_macros/
parse.rs

1use crate::{
2    add_trait_bounds,
3    context::{Context, ParseKind, Tokens},
4};
5use proc_macro2::TokenStream;
6use quote::{quote, quote_spanned};
7use syn::spanned::Spanned as _;
8
9/// Derive implementation of the Parse macro.
10pub struct Derive {
11    input: syn::DeriveInput,
12}
13
14impl syn::parse::Parse for Derive {
15    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
16        Ok(Self {
17            input: input.parse()?,
18        })
19    }
20}
21
22impl Derive {
23    pub(super) fn expand(self, cx: &Context) -> Result<TokenStream, ()> {
24        let attr = cx.type_attrs(&self.input.attrs);
25        let tokens = cx.tokens_with_module(attr.module.as_ref());
26
27        let mut expander = Expander { cx, tokens };
28
29        match &self.input.data {
30            syn::Data::Struct(st) => expander.expand_struct(&self.input, st),
31            syn::Data::Enum(en) => {
32                expander.cx.error(syn::Error::new_spanned(
33                    en.enum_token,
34                    "not supported on enums",
35                ));
36
37                Err(())
38            }
39            syn::Data::Union(un) => {
40                expander.cx.error(syn::Error::new_spanned(
41                    un.union_token,
42                    "not supported on unions",
43                ));
44
45                Err(())
46            }
47        }
48    }
49}
50
51struct Expander<'cx> {
52    cx: &'cx Context,
53    tokens: Tokens,
54}
55
56impl Expander<'_> {
57    /// Expand on a struct.
58    fn expand_struct(
59        &mut self,
60        input: &syn::DeriveInput,
61        st: &syn::DataStruct,
62    ) -> Result<TokenStream, ()> {
63        self.expand_struct_fields(input, &st.fields)
64    }
65
66    /// Expand field decoding.
67    fn expand_struct_fields(
68        &mut self,
69        input: &syn::DeriveInput,
70        fields: &syn::Fields,
71    ) -> Result<TokenStream, ()> {
72        match fields {
73            syn::Fields::Named(named) => self.expand_struct_named(input, named),
74            syn::Fields::Unnamed(..) => {
75                self.cx.error(syn::Error::new_spanned(
76                    fields,
77                    "Tuple structs are not supported",
78                ));
79                Err(())
80            }
81            syn::Fields::Unit => {
82                self.cx.error(syn::Error::new_spanned(
83                    fields,
84                    "Unit structs are not supported",
85                ));
86                Err(())
87            }
88        }
89    }
90
91    /// Expand named fields.
92    fn expand_struct_named(
93        &mut self,
94        input: &syn::DeriveInput,
95        named: &syn::FieldsNamed,
96    ) -> Result<TokenStream, ()> {
97        let ident = &input.ident;
98        let mut fields = Vec::new();
99
100        let mut meta_args = Vec::new();
101        let mut meta_parse = Vec::new();
102        let mut meta_fields = Vec::new();
103
104        let ty_attrs = self.cx.type_attrs(&input.attrs);
105        let mut skipped = 0;
106
107        for (i, field) in named.named.iter().enumerate() {
108            let field_attrs = self.cx.field_attrs(&field.attrs);
109            let ident = self.cx.field_ident(field)?;
110
111            if field_attrs.id.is_some() {
112                fields.push(quote_spanned! { field.span() => #ident: Default::default() });
113                skipped += 1;
114                continue;
115            }
116
117            let parse_impl = if let Some(parse_with) = field_attrs.parse_with {
118                quote_spanned!(field.span() => #parse_with(parser)?)
119            } else {
120                quote_spanned!(field.span() => parser.parse()?)
121            };
122
123            if field_attrs.meta.is_none() {
124                fields.push(quote_spanned! { field.span() => #ident: #parse_impl });
125                continue;
126            }
127
128            if i - skipped != meta_fields.len() {
129                self.cx.error(syn::Error::new_spanned(
130                    field,
131                    "The first sequence of fields may have `#[rune(meta)]`, \
132                        but field is outside of that sequence.",
133                ));
134                return Err(());
135            }
136
137            let ident = self.cx.field_ident(field)?;
138            let ty = &field.ty;
139            meta_args.push(quote_spanned!(field.span() => #ident: #ty));
140            meta_parse.push(quote_spanned!(field.span() => let #ident: #ty = #parse_impl));
141            fields.push(quote_spanned! { field.span() => #ident });
142            meta_fields.push(ident);
143        }
144
145        let parser_ident = &if fields.is_empty() {
146            quote!(_parser)
147        } else {
148            quote!(parser)
149        };
150
151        let parse = &self.tokens.parse;
152        let parser = &self.tokens.parser;
153        let compile_error = &self.tokens.compile_error;
154        let result = &self.tokens.result;
155
156        let mut generics = input.generics.clone();
157
158        add_trait_bounds(&mut generics, parse);
159
160        let (impl_generics, type_generics, where_generics) = generics.split_for_impl();
161
162        let inner = if let ParseKind::MetaOnly = ty_attrs.parse {
163            None
164        } else {
165            Some(quote_spanned! {
166                named.span() =>
167                #[automatically_derived]
168                impl #impl_generics #parse for #ident #type_generics #where_generics {
169                    fn parse(parser: &mut #parser<'_>) -> #result<Self, #compile_error> {
170                        #(#meta_parse;)*
171                        Self::parse_with_meta(parser, #(#meta_fields,)*)
172                     }
173                }
174            })
175        };
176
177        let output = if !meta_args.is_empty() {
178            quote_spanned! { named.span() =>
179                #[automatically_derived]
180                impl #ident {
181                    #[doc = "Parse #ident and attach the given meta"]
182                    pub fn parse_with_meta(#parser_ident: &mut #parser<'_>, #(#meta_args,)*)
183                        -> #result<Self, #compile_error>
184                    {
185                        Ok(Self {
186                            #(#fields,)*
187                        })
188                    }
189                }
190
191                #inner
192            }
193        } else {
194            quote_spanned! { named.span() =>
195                #[automatically_derived]
196                impl #impl_generics #parse for #ident #type_generics #where_generics {
197                    fn parse(#parser_ident: &mut #parser<'_>) -> #result<Self, #compile_error> {
198                        Ok(Self {
199                            #(#fields,)*
200                        })
201                    }
202                }
203            }
204        };
205
206        Ok(output)
207    }
208}