rune_macros/
to_value.rs

1use crate::context::{Context, Tokens};
2use proc_macro2::TokenStream;
3use quote::quote;
4
5struct Expander<'cx> {
6    cx: &'cx Context,
7    tokens: Tokens,
8}
9
10impl Expander<'_> {
11    /// Expand on a struct.
12    fn expand_struct(
13        &mut self,
14        input: &syn::DeriveInput,
15        st: &syn::DataStruct,
16    ) -> Result<TokenStream, ()> {
17        let inner = self.expand_fields(&st.fields)?;
18
19        let ident = &input.ident;
20
21        let Tokens {
22            value,
23            to_value,
24            result,
25            runtime_error,
26            ..
27        } = &self.tokens;
28
29        Ok(quote! {
30            #[automatically_derived]
31            impl #to_value for #ident {
32                fn to_value(self) -> #result<#value, #runtime_error> {
33                    #inner
34                }
35            }
36        })
37    }
38
39    /// Expand field decoding.
40    fn expand_fields(&mut self, fields: &syn::Fields) -> Result<TokenStream, ()> {
41        match fields {
42            syn::Fields::Unnamed(named) => self.expand_unnamed(named),
43            syn::Fields::Named(named) => self.expand_named(named),
44            syn::Fields::Unit => {
45                self.cx.error(syn::Error::new_spanned(
46                    fields,
47                    "unit structs are not supported",
48                ));
49                Err(())
50            }
51        }
52    }
53
54    /// Expand unnamed fields.
55    fn expand_unnamed(&mut self, unnamed: &syn::FieldsUnnamed) -> Result<TokenStream, ()> {
56        let mut to_values = Vec::new();
57
58        let Tokens {
59            to_value,
60            value,
61            owned_tuple,
62            result,
63            try_from,
64            vec,
65            ..
66        } = &self.tokens;
67
68        for (index, f) in unnamed.unnamed.iter().enumerate() {
69            _ = self.cx.field_attrs(&f.attrs);
70            let index = syn::Index::from(index);
71            to_values.push(quote!(#vec::try_push(&mut tuple, #to_value::to_value(self.#index)?)?));
72        }
73
74        let cap = unnamed.unnamed.len();
75
76        Ok(quote! {
77            let mut tuple = #vec::try_with_capacity(#cap)?;
78            #(#to_values;)*
79            let tuple = <#owned_tuple as #try_from<_>>::try_from(tuple)?;
80            #result::Ok(<#value as #try_from<_>>::try_from(tuple)?)
81        })
82    }
83
84    /// Expand named fields.
85    fn expand_named(&mut self, named: &syn::FieldsNamed) -> Result<TokenStream, ()> {
86        let Tokens {
87            to_value,
88            value,
89            object,
90            result,
91            string,
92            try_from,
93            ..
94        } = &self.tokens;
95
96        let mut to_values = Vec::new();
97
98        for f in &named.named {
99            let ident = self.cx.field_ident(f)?;
100            _ = self.cx.field_attrs(&f.attrs);
101
102            let name = syn::LitStr::new(&ident.to_string(), ident.span());
103
104            to_values.push(quote! {
105                object.insert(<#string as #try_from<_>>::try_from(#name)?, #to_value::to_value(self.#ident)?)?
106            });
107        }
108
109        Ok(quote! {
110            let mut object = <#object>::new();
111            #(#to_values;)*
112            #result::Ok(<#value as #try_from<_>>::try_from(object)?)
113        })
114    }
115}
116
117pub(super) fn expand(cx: &Context, input: &syn::DeriveInput) -> Result<TokenStream, ()> {
118    let attr = cx.type_attrs(&input.attrs);
119    let tokens = cx.tokens_with_module(attr.module.as_ref());
120
121    let mut expander = Expander { cx, tokens };
122
123    match &input.data {
124        syn::Data::Struct(st) => {
125            if let Ok(expanded) = expander.expand_struct(input, st) {
126                return Ok(expanded);
127            }
128        }
129        syn::Data::Enum(en) => {
130            expander.cx.error(syn::Error::new_spanned(
131                en.enum_token,
132                "not supported on enums",
133            ));
134        }
135        syn::Data::Union(un) => {
136            expander.cx.error(syn::Error::new_spanned(
137                un.union_token,
138                "not supported on unions",
139            ));
140        }
141    }
142
143    Err(())
144}