rune_macros/
from_value.rs

1use crate::context::{Context, Tokens};
2use proc_macro2::TokenStream;
3use quote::{quote, quote_spanned};
4use syn::spanned::Spanned as _;
5
6struct Expander<'cx> {
7    cx: &'cx Context,
8    tokens: Tokens,
9}
10
11impl Expander<'_> {
12    /// Expand on a struct.
13    fn expand_struct(
14        &mut self,
15        input: &syn::DeriveInput,
16        st: &syn::DataStruct,
17    ) -> Result<TokenStream, ()> {
18        let ident = &input.ident;
19
20        let Tokens {
21            value,
22            type_value,
23            from_value,
24            result,
25            tuple,
26            runtime_error,
27            ..
28        } = &self.tokens;
29
30        let (expanded, expected) = match &st.fields {
31            syn::Fields::Unit => {
32                let expanded = quote! {
33                    #type_value::Unit => {
34                        #result::Ok(Self)
35                    }
36                    #type_value::EmptyStruct(..) => {
37                        #result::Ok(Self)
38                    }
39                };
40
41                (expanded, &self.tokens.owned_tuple)
42            }
43            syn::Fields::Unnamed(f) => {
44                let expanded = self.expand_unnamed(f)?;
45
46                let expanded = quote! {
47                    #type_value::Unit => {
48                        let tuple = #tuple::new(&[]);
49                        #result::Ok(Self(#expanded))
50                    }
51                    #type_value::Tuple(tuple) => {
52                        #result::Ok(Self(#expanded))
53                    }
54                    #type_value::TupleStruct(tuple) => {
55                        #result::Ok(Self(#expanded))
56                    }
57                };
58
59                (expanded, &self.tokens.owned_tuple)
60            }
61            syn::Fields::Named(f) => {
62                let expanded = self.expand_named(f)?;
63
64                let expanded = quote! {
65                    #type_value::Object(object) => {
66                        #result::Ok(Self { #expanded })
67                    }
68                    #type_value::Struct(object) => {
69                        #result::Ok(Self { #expanded })
70                    }
71                };
72
73                (expanded, &self.tokens.object)
74            }
75        };
76
77        Ok(quote! {
78            #[automatically_derived]
79            impl #from_value for #ident {
80                fn from_value(value: #value) -> #result<Self, #runtime_error> {
81                    match #value::as_type_value(&value)? {
82                        #expanded
83                        actual => {
84                            #result::Err(#runtime_error::expected::<#expected>(#type_value::type_info(&actual)))
85                        }
86                    }
87                }
88            }
89        })
90    }
91
92    /// Expand on a struct.
93    fn expand_enum(
94        &mut self,
95        input: &syn::DeriveInput,
96        en: &syn::DataEnum,
97    ) -> Result<TokenStream, ()> {
98        let mut unit_matches = Vec::new();
99        let mut unnamed_matches = Vec::new();
100        let mut named_matches = Vec::new();
101
102        let ident = &input.ident;
103
104        let Tokens {
105            type_value,
106            from_value,
107            value,
108            result,
109            runtime_error,
110            ..
111        } = &self.tokens;
112
113        for variant in &en.variants {
114            let ident = &variant.ident;
115            let lit_str = syn::LitStr::new(&ident.to_string(), variant.span());
116
117            match &variant.fields {
118                syn::Fields::Unit => {
119                    unit_matches.push(quote! {
120                        #lit_str => #result::Ok(Self::#ident)
121                    });
122                }
123                syn::Fields::Unnamed(named) => {
124                    let expanded = self.expand_unnamed(named)?;
125
126                    unnamed_matches.push(quote! {
127                        #lit_str => #result::Ok(Self::#ident ( #expanded ))
128                    });
129                }
130                syn::Fields::Named(named) => {
131                    let expanded = self.expand_named(named)?;
132
133                    named_matches.push(quote! {
134                        #lit_str => #result::Ok(Self::#ident { #expanded })
135                    });
136                }
137            }
138        }
139
140        let missing = quote! {
141            name => {
142                return #result::Err(#runtime_error::__rune_macros__missing_variant(name)?);
143            }
144        };
145
146        let variant = quote! {
147            #type_value::EmptyStruct(data) => {
148                let Some(name) = data.rtti().item().base_name() else {
149                    return #result::Err(#runtime_error::__rune_macros__missing_variant_name());
150                };
151
152                match name {
153                    #(#unit_matches,)* #missing,
154                }
155            }
156            #type_value::TupleStruct(tuple) => {
157                let Some(name) = tuple.rtti().item().base_name() else {
158                    return #result::Err(#runtime_error::__rune_macros__missing_variant_name());
159                };
160
161                match name {
162                    #(#unnamed_matches,)* #missing,
163                }
164            }
165            #type_value::Struct(object) => {
166                let Some(name) = object.rtti().item().base_name() else {
167                    return #result::Err(#runtime_error::__rune_macros__missing_variant_name());
168                };
169
170                match name {
171                    #(#named_matches,)* #missing,
172                }
173            }
174        };
175
176        Ok(quote! {
177            #[automatically_derived]
178            impl #from_value for #ident {
179                fn from_value(value: #value) -> #result<Self, #runtime_error> {
180                    match #value::as_type_value(&value)? {
181                        #variant,
182                        actual => {
183                            #result::Err(#runtime_error::__rune_macros__expected_variant(#type_value::type_info(&actual)))
184                        }
185                    }
186                }
187            }
188        })
189    }
190
191    /// Get a field identifier.
192    fn field_ident<'a>(&self, field: &'a syn::Field) -> Result<&'a syn::Ident, ()> {
193        match &field.ident {
194            Some(ident) => Ok(ident),
195            None => {
196                self.cx.error(syn::Error::new_spanned(
197                    field,
198                    "unnamed fields are not supported",
199                ));
200                Err(())
201            }
202        }
203    }
204
205    /// Expand unnamed fields.
206    fn expand_unnamed(&self, unnamed: &syn::FieldsUnnamed) -> Result<TokenStream, ()> {
207        let mut from_values = Vec::new();
208
209        let Tokens {
210            from_value,
211            result,
212            type_name,
213            try_clone,
214            runtime_error,
215            ..
216        } = &self.tokens;
217
218        for (index, field) in unnamed.unnamed.iter().enumerate() {
219            let _ = self.cx.field_attrs(&field.attrs);
220
221            from_values.push(quote! {
222                match tuple.get(#index) {
223                    Some(value) => {
224                        let value = #try_clone::try_clone(value)?;
225                        #from_value::from_value(value)?
226                    }
227                    None => {
228                        return #result::Err(#runtime_error::__rune_macros__missing_tuple_index(#type_name::<Self>(), #index));
229                    }
230                }
231            });
232        }
233
234        Ok(quote_spanned!(unnamed.span() => #(#from_values),*))
235    }
236
237    /// Expand named fields.
238    fn expand_named(&self, named: &syn::FieldsNamed) -> Result<TokenStream, ()> {
239        let mut from_values = Vec::new();
240
241        for field in &named.named {
242            let ident = self.field_ident(field)?;
243            let _ = self.cx.field_attrs(&field.attrs);
244
245            let name = &syn::LitStr::new(&ident.to_string(), ident.span());
246
247            let Tokens {
248                from_value,
249                result,
250                runtime_error,
251                type_name,
252                ..
253            } = &self.tokens;
254
255            from_values.push(quote_spanned! {
256                field.span() =>
257                #ident: match object.get(#name) {
258                    Some(value) => #from_value::from_value(value.clone())?,
259                    None => {
260                        return #result::Err(#runtime_error::__rune_macros__missing_struct_field(#type_name::<Self>(), #name));
261                    }
262                }
263            });
264        }
265
266        Ok(quote!(#(#from_values),*))
267    }
268}
269
270pub(super) fn expand(cx: &Context, input: &syn::DeriveInput) -> Result<TokenStream, ()> {
271    let attr = cx.type_attrs(&input.attrs);
272    let tokens = cx.tokens_with_module(attr.module.as_ref());
273
274    let mut expander = Expander { cx, tokens };
275
276    match &input.data {
277        syn::Data::Struct(st) => expander.expand_struct(input, st),
278        syn::Data::Enum(en) => expander.expand_enum(input, en),
279        syn::Data::Union(un) => {
280            expander.cx.error(syn::Error::new_spanned(
281                un.union_token,
282                "not supported on unions",
283            ));
284
285            Err(())
286        }
287    }
288}