Skip to content

Commit 74a144d

Browse files
committed
rune: Add new support for third-party constant values
1 parent 64aedd5 commit 74a144d

32 files changed

+1361
-501
lines changed

crates/rune-macros/src/const_value.rs

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
use core::fmt;
2+
3+
use proc_macro2::{Span, TokenStream};
4+
use quote::{quote, ToTokens};
5+
use syn::parse::{Parse, ParseStream};
6+
use syn::spanned::Spanned;
7+
use syn::DeriveInput;
8+
9+
use crate::context::{Context, Tokens};
10+
11+
/// An internal call to the macro.
12+
pub(super) struct Derive {
13+
input: DeriveInput,
14+
}
15+
16+
impl Parse for Derive {
17+
fn parse(input: ParseStream) -> syn::Result<Self> {
18+
Ok(Self {
19+
input: input.parse()?,
20+
})
21+
}
22+
}
23+
24+
pub(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+
34+
impl 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)).into_result()?
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+
132+
impl<T> ConstBuilder<T>
133+
where
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+
#result::Ok(#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+
}

crates/rune-macros/src/context.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ impl FieldAttrs {
4444
}
4545
}
4646

47+
/// Parsed #[const_value(..)] field attributes.
48+
#[derive(Default)]
49+
pub(crate) struct ConstValueFieldAttrs {
50+
/// Define a custom parsing method.
51+
pub(crate) with: Option<syn::Path>,
52+
}
53+
4754
/// The parsing implementations to build.
4855
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4956
pub(crate) enum ParseKind {
@@ -91,6 +98,13 @@ pub(crate) struct TypeAttr {
9198
pub(crate) from_value_params: Option<syn::punctuated::Punctuated<syn::Type, Token![,]>>,
9299
}
93100

101+
/// Parsed #[const_value(..)] field attributes.
102+
#[derive(Default)]
103+
pub(crate) struct ConstValueTypeAttr {
104+
/// `#[const_value(module = <path>)]`.
105+
pub(crate) module: Option<syn::Path>,
106+
}
107+
94108
/// Parsed variant attributes.
95109
#[derive(Default)]
96110
pub(crate) struct VariantAttrs {
@@ -178,6 +192,44 @@ impl Context {
178192
Ok(ident)
179193
}
180194

195+
pub(crate) fn const_value_field_attrs(
196+
&self,
197+
input: &[syn::Attribute],
198+
) -> Result<ConstValueFieldAttrs, ()> {
199+
let mut error = false;
200+
let mut attr = ConstValueFieldAttrs::default();
201+
202+
for a in input {
203+
if a.path() != CONST_VALUE {
204+
continue;
205+
}
206+
207+
let result = a.parse_nested_meta(|meta| {
208+
if meta.path.is_ident("with") {
209+
meta.input.parse::<Token![=]>()?;
210+
attr.with = Some(meta.input.parse::<syn::Path>()?);
211+
return Ok(());
212+
}
213+
214+
return Err(syn::Error::new_spanned(
215+
&meta.path,
216+
"Unsupported field attribute",
217+
));
218+
});
219+
220+
if let Err(e) = result {
221+
error = true;
222+
self.error(e);
223+
};
224+
}
225+
226+
if error {
227+
return Err(());
228+
}
229+
230+
Ok(attr)
231+
}
232+
181233
/// Parse field attributes.
182234
pub(crate) fn field_attrs(&self, input: &[syn::Attribute]) -> Result<FieldAttrs, ()> {
183235
macro_rules! generate_assign {
@@ -417,6 +469,49 @@ impl Context {
417469
Ok(attr)
418470
}
419471

472+
pub(crate) fn const_value_type_attrs(
473+
&self,
474+
input: &[syn::Attribute],
475+
) -> Result<ConstValueTypeAttr, ()> {
476+
let mut error = false;
477+
let mut attr = ConstValueTypeAttr::default();
478+
479+
for a in input {
480+
if a.path() != CONST_VALUE {
481+
continue;
482+
}
483+
484+
let result = a.parse_nested_meta(|meta| {
485+
if meta.path == MODULE || meta.path == CRATE {
486+
// Parse `#[rune(crate [= <path>])]`
487+
if meta.input.parse::<Option<Token![=]>>()?.is_some() {
488+
attr.module = Some(parse_path_compat(meta.input)?);
489+
} else {
490+
attr.module = Some(syn::parse_quote!(crate));
491+
}
492+
493+
return Ok(());
494+
}
495+
496+
return Err(syn::Error::new_spanned(
497+
&meta.path,
498+
"Unsupported type attribute",
499+
));
500+
});
501+
502+
if let Err(e) = result {
503+
error = true;
504+
self.error(e);
505+
};
506+
}
507+
508+
if error {
509+
return Err(());
510+
}
511+
512+
Ok(attr)
513+
}
514+
420515
/// Parse field attributes.
421516
pub(crate) fn type_attrs(&self, input: &[syn::Attribute]) -> Result<TypeAttr, ()> {
422517
let mut error = false;
@@ -614,10 +709,14 @@ impl Context {
614709
Tokens {
615710
alloc: path(m, ["alloc"]),
616711
any_t: path(m, ["Any"]),
712+
arc: path(m, ["__private", "Arc"]),
617713
box_: path(m, ["__private", "Box"]),
618714
compile_error: path(m, ["compile", "Error"]),
715+
const_construct_t: path(m, ["runtime", "ConstConstruct"]),
716+
const_value: path(m, ["runtime", "ConstValue"]),
619717
context_error: path(m, ["compile", "ContextError"]),
620718
double_ended_iterator: path(&core, ["iter", "DoubleEndedIterator"]),
719+
from_const_value_t: path(m, ["runtime", "FromConstValue"]),
621720
from_value: path(m, ["runtime", "FromValue"]),
622721
hash: path(m, ["Hash"]),
623722
id: path(m, ["parse", "Id"]),
@@ -644,10 +743,12 @@ impl Context {
644743
raw_value_guard: path(m, ["runtime", "RawValueGuard"]),
645744
ref_: path(m, ["runtime", "Ref"]),
646745
result: path(&core, ["result", "Result"]),
746+
runtime_error: path(m, ["runtime", "RuntimeError"]),
647747
span: path(m, ["ast", "Span"]),
648748
spanned: path(m, ["ast", "Spanned"]),
649749
static_type_mod: path(m, ["runtime", "static_type"]),
650750
string: path(m, ["alloc", "String"]),
751+
to_const_value_t: path(m, ["runtime", "ToConstValue"]),
651752
to_tokens: path(m, ["macros", "ToTokens"]),
652753
to_value: path(m, ["runtime", "ToValue"]),
653754
token_stream: path(m, ["macros", "TokenStream"]),
@@ -705,10 +806,14 @@ fn path<const N: usize>(base: &syn::Path, path: [&'static str; N]) -> syn::Path
705806
pub(crate) struct Tokens {
706807
pub(crate) alloc: syn::Path,
707808
pub(crate) any_t: syn::Path,
809+
pub(crate) arc: syn::Path,
708810
pub(crate) box_: syn::Path,
709811
pub(crate) compile_error: syn::Path,
812+
pub(crate) const_construct_t: syn::Path,
813+
pub(crate) const_value: syn::Path,
710814
pub(crate) context_error: syn::Path,
711815
pub(crate) double_ended_iterator: syn::Path,
816+
pub(crate) from_const_value_t: syn::Path,
712817
pub(crate) from_value: syn::Path,
713818
pub(crate) hash: syn::Path,
714819
pub(crate) id: syn::Path,
@@ -735,10 +840,12 @@ pub(crate) struct Tokens {
735840
pub(crate) raw_value_guard: syn::Path,
736841
pub(crate) ref_: syn::Path,
737842
pub(crate) result: syn::Path,
843+
pub(crate) runtime_error: syn::Path,
738844
pub(crate) span: syn::Path,
739845
pub(crate) spanned: syn::Path,
740846
pub(crate) static_type_mod: syn::Path,
741847
pub(crate) string: syn::Path,
848+
pub(crate) to_const_value_t: syn::Path,
742849
pub(crate) to_tokens: syn::Path,
743850
pub(crate) to_value: syn::Path,
744851
pub(crate) token_stream: syn::Path,

crates/rune-macros/src/internals.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::fmt;
66
pub struct Symbol(&'static str);
77

88
pub const RUNE: Symbol = Symbol("rune");
9+
pub const CONST_VALUE: Symbol = Symbol("const_value");
910
pub const ID: Symbol = Symbol("id");
1011
pub const SKIP: Symbol = Symbol("skip");
1112
pub const ITER: Symbol = Symbol("iter");

0 commit comments

Comments
 (0)