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