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 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 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 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 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}