Skip to content

Commit

Permalink
Merge pull request #821 from schungx/master
Browse files Browse the repository at this point in the history
Add support for tuple structs.
  • Loading branch information
schungx committed Jan 26, 2024
2 parents 05b624b + 1f3abc8 commit 6845a07
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 73 deletions.
111 changes: 49 additions & 62 deletions codegen/src/custom_type.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
use proc_macro2::{Ident, Span, TokenStream};
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::DeriveInput;
use syn::{DeriveInput, Fields};

pub fn derive_custom_type_impl(input: DeriveInput) -> TokenStream {
let name = input.ident;

let accessors = match input.data {
// struct Foo;
syn::Data::Struct(syn::DataStruct {
fields: syn::Fields::Named(ref fields),
fields: Fields::Unit,
..
}) => {
let iter = fields.named.iter().map(|field| {
}) => quote! {},

// struct Foo { ... }
syn::Data::Struct(syn::DataStruct { fields, .. }) => {
let fields = match fields {
Fields::Named(ref f) => f.named.iter(),
Fields::Unnamed(ref f) => f.unnamed.iter(),
Fields::Unit => unreachable!(),
};

let iter = fields.enumerate().map(|(i, field)| {
let mut name = None;
let mut get_fn = None;
let mut set_fn = None;
Expand All @@ -19,27 +29,8 @@ pub fn derive_custom_type_impl(input: DeriveInput) -> TokenStream {

for attr in field.attrs.iter() {
if attr.path().is_ident("rhai_custom_type_skip") {
if get_fn.is_some() || set_fn.is_some() || name.is_some() {
return syn::Error::new(
Span::call_site(),
"cannot use 'rhai_custom_type_skip' with other attributes",
)
.into_compile_error();
}

skip = true;
continue;
}

if skip {
return syn::Error::new(
Span::call_site(),
"cannot use 'rhai_custom_type_skip' with other attributes",
)
.into_compile_error();
}

if attr.path().is_ident("rhai_custom_type_name") {
} else if attr.path().is_ident("rhai_custom_type_name") {
name = Some(
attr.parse_args()
.unwrap_or_else(syn::Error::into_compile_error),
Expand Down Expand Up @@ -73,33 +64,35 @@ pub fn derive_custom_type_impl(input: DeriveInput) -> TokenStream {
}
}

if !skip {
generate_accessor_fns(
&field.ident.as_ref().unwrap(),
name,
get_fn,
set_fn,
readonly,
if skip && (get_fn.is_some() || set_fn.is_some() || name.is_some() || readonly) {
return syn::Error::new(
Span::call_site(),
"cannot use 'rhai_custom_type_skip' with other attributes",
)
.into_compile_error();
}

if !skip {
let field_name = if let Some(ref field_name) = field.ident {
quote! { #field_name }
} else {
if name.is_none() {
let map_name = format!("field{i}");
name = Some(quote! { #map_name });
}
let index = proc_macro2::Literal::usize_unsuffixed(i);
quote! { #index }
};

generate_accessor_fns(field_name, name, get_fn, set_fn, readonly)
} else {
quote! {}
}
});

quote! {#(#iter)*}
quote! { #(#iter ;)* }
}

syn::Data::Struct(syn::DataStruct {
fields: syn::Fields::Unnamed(_),
..
}) => syn::Error::new(Span::call_site(), "tuple structs are not yet implemented")
.into_compile_error(),

syn::Data::Struct(syn::DataStruct {
fields: syn::Fields::Unit,
..
}) => quote! {},

syn::Data::Enum(_) => {
syn::Error::new(Span::call_site(), "enums are not yet implemented").into_compile_error()
}
Expand All @@ -118,31 +111,25 @@ pub fn derive_custom_type_impl(input: DeriveInput) -> TokenStream {
}

fn generate_accessor_fns(
field: &Ident,
field: TokenStream,
name: Option<TokenStream>,
get: Option<TokenStream>,
set: Option<TokenStream>,
readonly: bool,
) -> proc_macro2::TokenStream {
let get = get
.map(|func| quote! {#func})
.unwrap_or_else(|| quote! {|obj: &mut Self| obj.#field.clone()});

let set = set
.map(|func| quote! {#func})
.unwrap_or_else(|| quote! {|obj: &mut Self, val| obj.#field = val});

let name = name
.map(|field| quote! { #field })
.unwrap_or_else(|| quote! { stringify!(#field) });
let get = get.map_or_else(
|| quote! { |obj: &mut Self| obj.#field.clone() },
|func| quote! { #func },
);
let set = set.map_or_else(
|| quote! { |obj: &mut Self, val| obj.#field = val },
|func| quote! { #func },
);
let name = name.map_or_else(|| quote! { stringify!(#field) }, |expr| quote! { #expr });

if readonly {
quote! {
builder.with_get(#name, #get);
}
quote! { builder.with_get(#name, #get) }
} else {
quote! {
builder.with_get_set(#name, #get, #set);
}
quote! { builder.with_get_set(#name, #get, #set) }
}
}
24 changes: 20 additions & 4 deletions codegen/tests/test_derive.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
use rhai::{CustomType, TypeBuilder, INT};
use rhai::{CustomType, TypeBuilder, FLOAT, INT};

// Sanity check to make sure everything compiles

#[derive(Clone, CustomType)]
pub struct Bar(
#[cfg(not(feature = "no_float"))]
#[rhai_custom_type_skip]
FLOAT,
INT,
#[rhai_custom_type_name("boo")]
#[rhai_custom_type_readonly]
String,
Vec<INT>,
);

#[derive(Clone, CustomType)]
pub struct Foo {
#[rhai_custom_type_skip]
_dummy: INT,
#[rhai_custom_type_get(get_bar)]
bar: INT,
pub bar: INT,
#[rhai_custom_type_name("boo")]
#[rhai_custom_type_readonly]
baz: String,
pub(crate) baz: String,
#[rhai_custom_type_set(Self::set_qux)]
qux: Vec<INT>,
pub qux: Vec<INT>,
}

impl Foo {
Expand All @@ -23,3 +36,6 @@ impl Foo {
fn get_bar(_this: &mut Foo) -> INT {
42
}

#[test]
fn test() {}
1 change: 1 addition & 0 deletions src/packages/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,7 @@ pub mod decimal_functions {
#[rhai_fn(return_raw)]
pub fn power(x: Decimal, y: Decimal) -> RhaiResultOf<Decimal> {
// Raising to a very large power can take exponential time, so limit it to 1 million.
// TODO: Remove this limit when `rust-decimal` is updated with the fix.
if std::convert::TryInto::<u32>::try_into(y.round()).map_or(true, |v| v > 1000000) {
return Err(make_err(format!("Exponential overflow: {x} ** {y}")));
}
Expand Down
13 changes: 6 additions & 7 deletions tests/build_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,17 +159,16 @@ fn test_build_type() {

#[test]
fn test_build_type_macro() {
#[derive(Debug, Clone, Eq, PartialEq)] // <- necessary for any custom type
#[derive(CustomType)] // <- auto-implement 'CustomType'
#[derive(Debug, Clone, Eq, PartialEq, CustomType)]
struct Foo {
#[rhai_custom_type_skip]
dummy: i64, // <- skip this field
#[rhai_custom_type_readonly] // <- only auto-implement getters
dummy: i64,
#[rhai_custom_type_readonly]
bar: i64,
#[rhai_custom_type_name("emphasize")]
baz: bool, // <- auto-implement getter/setter for 'baz'
#[rhai_custom_type_set(Self::set_hello)] // <- call custom setter for 'hello'
hello: String, // <- auto-implement getter for 'hello'
baz: bool,
#[rhai_custom_type_set(Self::set_hello)]
hello: String,
}

impl Foo {
Expand Down

0 comments on commit 6845a07

Please sign in to comment.