rune_macros/
from_value.rsuse crate::context::{Context, Tokens};
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned};
use syn::spanned::Spanned as _;
struct Expander<'cx> {
cx: &'cx Context,
tokens: Tokens,
}
impl Expander<'_> {
fn expand_struct(
&mut self,
input: &syn::DeriveInput,
st: &syn::DataStruct,
) -> Result<TokenStream, ()> {
let ident = &input.ident;
let Tokens {
value,
type_value,
from_value,
result,
tuple,
runtime_error,
..
} = &self.tokens;
let (expanded, expected) = match &st.fields {
syn::Fields::Unit => {
let expanded = quote! {
#type_value::Unit => {
#result::Ok(Self)
}
#type_value::EmptyStruct(..) => {
#result::Ok(Self)
}
};
(expanded, &self.tokens.owned_tuple)
}
syn::Fields::Unnamed(f) => {
let expanded = self.expand_unnamed(f)?;
let expanded = quote! {
#type_value::Unit => {
let tuple = #tuple::new(&[]);
#result::Ok(Self(#expanded))
}
#type_value::Tuple(tuple) => {
#result::Ok(Self(#expanded))
}
#type_value::TupleStruct(tuple) => {
#result::Ok(Self(#expanded))
}
};
(expanded, &self.tokens.owned_tuple)
}
syn::Fields::Named(f) => {
let expanded = self.expand_named(f)?;
let expanded = quote! {
#type_value::Object(object) => {
#result::Ok(Self { #expanded })
}
#type_value::Struct(object) => {
#result::Ok(Self { #expanded })
}
};
(expanded, &self.tokens.object)
}
};
Ok(quote! {
#[automatically_derived]
impl #from_value for #ident {
fn from_value(value: #value) -> #result<Self, #runtime_error> {
match #value::as_type_value(&value)? {
#expanded
actual => {
#result::Err(#runtime_error::expected::<#expected>(#type_value::type_info(&actual)))
}
}
}
}
})
}
fn expand_enum(
&mut self,
input: &syn::DeriveInput,
en: &syn::DataEnum,
) -> Result<TokenStream, ()> {
let mut unit_matches = Vec::new();
let mut unnamed_matches = Vec::new();
let mut named_matches = Vec::new();
let ident = &input.ident;
let Tokens {
type_value,
from_value,
value,
result,
runtime_error,
..
} = &self.tokens;
for variant in &en.variants {
let ident = &variant.ident;
let lit_str = syn::LitStr::new(&ident.to_string(), variant.span());
match &variant.fields {
syn::Fields::Unit => {
unit_matches.push(quote! {
#lit_str => #result::Ok(Self::#ident)
});
}
syn::Fields::Unnamed(named) => {
let expanded = self.expand_unnamed(named)?;
unnamed_matches.push(quote! {
#lit_str => #result::Ok(Self::#ident ( #expanded ))
});
}
syn::Fields::Named(named) => {
let expanded = self.expand_named(named)?;
named_matches.push(quote! {
#lit_str => #result::Ok(Self::#ident { #expanded })
});
}
}
}
let missing = quote! {
name => {
return #result::Err(#runtime_error::__rune_macros__missing_variant(name)?);
}
};
let variant = quote! {
#type_value::EmptyStruct(data) => {
let Some(name) = data.rtti().item().base_name() else {
return #result::Err(#runtime_error::__rune_macros__missing_variant_name());
};
match name {
#(#unit_matches,)* #missing,
}
}
#type_value::TupleStruct(tuple) => {
let Some(name) = tuple.rtti().item().base_name() else {
return #result::Err(#runtime_error::__rune_macros__missing_variant_name());
};
match name {
#(#unnamed_matches,)* #missing,
}
}
#type_value::Struct(object) => {
let Some(name) = object.rtti().item().base_name() else {
return #result::Err(#runtime_error::__rune_macros__missing_variant_name());
};
match name {
#(#named_matches,)* #missing,
}
}
};
Ok(quote! {
#[automatically_derived]
impl #from_value for #ident {
fn from_value(value: #value) -> #result<Self, #runtime_error> {
match #value::as_type_value(&value)? {
#variant,
actual => {
#result::Err(#runtime_error::__rune_macros__expected_variant(#type_value::type_info(&actual)))
}
}
}
}
})
}
fn field_ident<'a>(&self, field: &'a syn::Field) -> Result<&'a syn::Ident, ()> {
match &field.ident {
Some(ident) => Ok(ident),
None => {
self.cx.error(syn::Error::new_spanned(
field,
"unnamed fields are not supported",
));
Err(())
}
}
}
fn expand_unnamed(&self, unnamed: &syn::FieldsUnnamed) -> Result<TokenStream, ()> {
let mut from_values = Vec::new();
let Tokens {
from_value,
result,
type_name,
try_clone,
runtime_error,
..
} = &self.tokens;
for (index, field) in unnamed.unnamed.iter().enumerate() {
let _ = self.cx.field_attrs(&field.attrs);
from_values.push(quote! {
match tuple.get(#index) {
Some(value) => {
let value = #try_clone::try_clone(value)?;
#from_value::from_value(value)?
}
None => {
return #result::Err(#runtime_error::__rune_macros__missing_tuple_index(#type_name::<Self>(), #index));
}
}
});
}
Ok(quote_spanned!(unnamed.span() => #(#from_values),*))
}
fn expand_named(&self, named: &syn::FieldsNamed) -> Result<TokenStream, ()> {
let mut from_values = Vec::new();
for field in &named.named {
let ident = self.field_ident(field)?;
let _ = self.cx.field_attrs(&field.attrs);
let name = &syn::LitStr::new(&ident.to_string(), ident.span());
let Tokens {
from_value,
result,
runtime_error,
type_name,
..
} = &self.tokens;
from_values.push(quote_spanned! {
field.span() =>
#ident: match object.get(#name) {
Some(value) => #from_value::from_value(value.clone())?,
None => {
return #result::Err(#runtime_error::__rune_macros__missing_struct_field(#type_name::<Self>(), #name));
}
}
});
}
Ok(quote!(#(#from_values),*))
}
}
pub(super) fn expand(cx: &Context, input: &syn::DeriveInput) -> Result<TokenStream, ()> {
let attr = cx.type_attrs(&input.attrs);
let tokens = cx.tokens_with_module(attr.module.as_ref());
let mut expander = Expander { cx, tokens };
match &input.data {
syn::Data::Struct(st) => expander.expand_struct(input, st),
syn::Data::Enum(en) => expander.expand_enum(input, en),
syn::Data::Union(un) => {
expander.cx.error(syn::Error::new_spanned(
un.union_token,
"not supported on unions",
));
Err(())
}
}
}