rune_macros/
const_value.rsuse core::fmt;
use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned;
use syn::DeriveInput;
use crate::context::{Context, Tokens};
pub(super) struct Derive {
input: DeriveInput,
}
impl Parse for Derive {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Self {
input: input.parse()?,
})
}
}
pub(super) struct ConstBuilder<T> {
ident: T,
tokens: Tokens,
body: TokenStream,
variables: Vec<syn::Ident>,
members: Vec<syn::Member>,
from_const_fields: Vec<TokenStream>,
from_value_fields: Vec<TokenStream>,
}
impl Derive {
pub(super) fn into_builder(self, cx: &Context) -> Result<ConstBuilder<syn::Ident>, ()> {
let attr = cx.const_value_type_attrs(&self.input.attrs);
let tokens = cx.tokens_with_module(attr.module.as_ref());
let body;
let Tokens {
const_value,
from_const_value_t,
to_const_value_t,
type_hash_t,
from_value,
value,
..
} = &tokens;
let mut variables = Vec::new();
let mut members = Vec::new();
let mut from_const_fields = Vec::new();
let mut from_value_fields = Vec::new();
match self.input.data {
syn::Data::Struct(data) => {
let mut fields = Vec::new();
for (index, field) in data.fields.iter().enumerate() {
let attr = cx.const_value_field_attrs(&field.attrs);
let member = match &field.ident {
Some(ident) => syn::Member::Named(ident.clone()),
None => syn::Member::Unnamed(syn::Index::from(index)),
};
let ty = &field.ty;
let var = syn::Ident::new(&format!("v{index}"), Span::call_site());
if let Some(path) = &attr.with {
let to_const_value: syn::Path =
syn::parse_quote_spanned!(path.span() => #path::to_const_value);
let from_const_value: syn::Path =
syn::parse_quote_spanned!(path.span() => #path::from_const_value);
let from_value: syn::Path =
syn::parse_quote_spanned!(path.span() => #path::from_value);
fields.push(quote!(#to_const_value(self.#member)?));
from_const_fields.push(quote!(#from_const_value(#var)?));
from_value_fields.push(quote!(#from_value(#value::take(#var))?));
} else {
fields.push(quote! {
<#ty as #to_const_value_t>::to_const_value(self.#member)?
});
from_const_fields.push(quote! {
<#ty as #from_const_value_t>::from_const_value(#var)?
});
from_value_fields.push(quote! {
<#ty as #from_value>::from_value(#value::take(#var))?
});
}
variables.push(var);
members.push(member);
}
body = quote! {
#const_value::for_struct(<Self as #type_hash_t>::HASH, [#(#fields),*])
};
}
syn::Data::Enum(..) => {
cx.error(syn::Error::new(
Span::call_site(),
"ToConstValue: enums are not supported",
));
return Err(());
}
syn::Data::Union(..) => {
cx.error(syn::Error::new(
Span::call_site(),
"ToConstValue: unions are not supported",
));
return Err(());
}
}
Ok(ConstBuilder {
ident: self.input.ident,
tokens,
body,
variables,
members,
from_const_fields,
from_value_fields,
})
}
}
impl<T> ConstBuilder<T>
where
T: ToTokens + fmt::Display,
{
pub(super) fn expand(self) -> TokenStream {
let Tokens {
arc,
const_construct_t,
const_value,
option,
result,
runtime_error,
to_const_value_t,
value,
..
} = &self.tokens;
let ident = self.ident;
let construct = syn::Ident::new(&format!("{ident}Construct"), Span::call_site());
let body = self.body;
let members = &self.members;
let variables = &self.variables;
let from_const_fields = &self.from_const_fields;
let from_value_fields = &self.from_value_fields;
let expected = self.members.len();
quote! {
#[automatically_derived]
impl #to_const_value_t for #ident {
#[inline]
fn to_const_value(self) -> #result<#const_value, #runtime_error> {
#body
}
#[inline]
fn construct() -> #option<#arc<dyn #const_construct_t>> {
struct #construct;
impl #const_construct_t for #construct {
#[inline]
fn const_construct(&self, values: &[#const_value]) -> #result<#value, #runtime_error> {
let [#(#variables),*] = values else {
return #result::Err(#runtime_error::bad_argument_count(values.len(), #expected));
};
let value = #ident {
#(#members: #from_const_fields,)*
};
#result::Ok(#value::new(value)?)
}
#[inline]
fn runtime_construct(&self, values: &mut [#value]) -> #result<#value, #runtime_error> {
let [#(#variables),*] = values else {
return #result::Err(#runtime_error::bad_argument_count(values.len(), #expected));
};
let value = #ident {
#(#members: #from_value_fields,)*
};
#result::Ok(#value::new(value)?)
}
}
#option::Some(#arc::new(#construct))
}
}
}
}
}