rune_macros/
const_value.rs
1use core::fmt;
2
3use proc_macro2::{Span, TokenStream};
4use quote::{quote, ToTokens};
5use syn::parse::{Parse, ParseStream};
6use syn::spanned::Spanned;
7use syn::DeriveInput;
8
9use crate::context::{Context, Tokens};
10
11pub(super) struct Derive {
13 input: DeriveInput,
14}
15
16impl Parse for Derive {
17 fn parse(input: ParseStream) -> syn::Result<Self> {
18 Ok(Self {
19 input: input.parse()?,
20 })
21 }
22}
23
24pub(super) struct ConstBuilder<T> {
25 ident: T,
26 tokens: Tokens,
27 body: TokenStream,
28 variables: Vec<syn::Ident>,
29 members: Vec<syn::Member>,
30 from_const_fields: Vec<TokenStream>,
31 from_value_fields: Vec<TokenStream>,
32}
33
34impl Derive {
35 pub(super) fn into_builder(self, cx: &Context) -> Result<ConstBuilder<syn::Ident>, ()> {
36 let attr = cx.const_value_type_attrs(&self.input.attrs);
37 let tokens = cx.tokens_with_module(attr.module.as_ref());
38 let body;
39
40 let Tokens {
41 const_value,
42 from_const_value_t,
43 to_const_value_t,
44 type_hash_t,
45 from_value,
46 value,
47 ..
48 } = &tokens;
49
50 let mut variables = Vec::new();
51 let mut members = Vec::new();
52 let mut from_const_fields = Vec::new();
53 let mut from_value_fields = Vec::new();
54
55 match self.input.data {
56 syn::Data::Struct(data) => {
57 let mut fields = Vec::new();
58
59 for (index, field) in data.fields.iter().enumerate() {
60 let attr = cx.const_value_field_attrs(&field.attrs);
61
62 let member = match &field.ident {
63 Some(ident) => syn::Member::Named(ident.clone()),
64 None => syn::Member::Unnamed(syn::Index::from(index)),
65 };
66
67 let ty = &field.ty;
68
69 let var = syn::Ident::new(&format!("v{index}"), Span::call_site());
70
71 if let Some(path) = &attr.with {
72 let to_const_value: syn::Path =
73 syn::parse_quote_spanned!(path.span() => #path::to_const_value);
74 let from_const_value: syn::Path =
75 syn::parse_quote_spanned!(path.span() => #path::from_const_value);
76 let from_value: syn::Path =
77 syn::parse_quote_spanned!(path.span() => #path::from_value);
78
79 fields.push(quote!(#to_const_value(self.#member)?));
80 from_const_fields.push(quote!(#from_const_value(#var)?));
81 from_value_fields.push(quote!(#from_value(#value::take(#var))?));
82 } else {
83 fields.push(quote! {
84 <#ty as #to_const_value_t>::to_const_value(self.#member)?
85 });
86
87 from_const_fields.push(quote! {
88 <#ty as #from_const_value_t>::from_const_value(#var)?
89 });
90
91 from_value_fields.push(quote! {
92 <#ty as #from_value>::from_value(#value::take(#var))?
93 });
94 }
95
96 variables.push(var);
97 members.push(member);
98 }
99
100 body = quote! {
101 #const_value::for_struct(<Self as #type_hash_t>::HASH, [#(#fields),*])
102 };
103 }
104 syn::Data::Enum(..) => {
105 cx.error(syn::Error::new(
106 Span::call_site(),
107 "ToConstValue: enums are not supported",
108 ));
109 return Err(());
110 }
111 syn::Data::Union(..) => {
112 cx.error(syn::Error::new(
113 Span::call_site(),
114 "ToConstValue: unions are not supported",
115 ));
116 return Err(());
117 }
118 }
119
120 Ok(ConstBuilder {
121 ident: self.input.ident,
122 tokens,
123 body,
124 variables,
125 members,
126 from_const_fields,
127 from_value_fields,
128 })
129 }
130}
131
132impl<T> ConstBuilder<T>
133where
134 T: ToTokens + fmt::Display,
135{
136 pub(super) fn expand(self) -> TokenStream {
137 let Tokens {
138 arc,
139 const_construct_t,
140 const_value,
141 option,
142 result,
143 runtime_error,
144 to_const_value_t,
145 value,
146 ..
147 } = &self.tokens;
148
149 let ident = self.ident;
150 let construct = syn::Ident::new(&format!("{ident}Construct"), Span::call_site());
151 let body = self.body;
152 let members = &self.members;
153 let variables = &self.variables;
154 let from_const_fields = &self.from_const_fields;
155 let from_value_fields = &self.from_value_fields;
156
157 let expected = self.members.len();
158
159 quote! {
160 #[automatically_derived]
161 impl #to_const_value_t for #ident {
162 #[inline]
163 fn to_const_value(self) -> #result<#const_value, #runtime_error> {
164 #body
165 }
166
167 #[inline]
168 fn construct() -> #option<#arc<dyn #const_construct_t>> {
169 struct #construct;
170
171 impl #const_construct_t for #construct {
172 #[inline]
173 fn const_construct(&self, values: &[#const_value]) -> #result<#value, #runtime_error> {
174 let [#(#variables),*] = values else {
175 return #result::Err(#runtime_error::bad_argument_count(values.len(), #expected));
176 };
177
178 let value = #ident {
179 #(#members: #from_const_fields,)*
180 };
181
182 #result::Ok(#value::new(value)?)
183 }
184
185 #[inline]
186 fn runtime_construct(&self, values: &mut [#value]) -> #result<#value, #runtime_error> {
187 let [#(#variables),*] = values else {
188 return #result::Err(#runtime_error::bad_argument_count(values.len(), #expected));
189 };
190
191 let value = #ident {
192 #(#members: #from_value_fields,)*
193 };
194
195 #result::Ok(#value::new(value)?)
196 }
197 }
198
199 #option::Some(#arc::new(#construct))
200 }
201 }
202 }
203 }
204}