Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/rhaiscript/rhai
Browse files Browse the repository at this point in the history
  • Loading branch information
schungx committed Jan 25, 2024
2 parents 310bf8a + 9a82471 commit 6824d94
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 1 deletion.
85 changes: 85 additions & 0 deletions codegen/src/custom_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;
use syn::DeriveInput;

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

let accessors = match input.data {
syn::Data::Struct(ref data) => match data.fields {
syn::Fields::Named(ref fields) => {
let iter = fields.named.iter().map(|field| {
let mut get_fn = None;
let mut set_fn = None;
let mut readonly = false;
for attr in field.attrs.iter() {
if attr.path().is_ident("get") {
get_fn = Some(
attr.parse_args()
.unwrap_or_else(syn::Error::into_compile_error),
);
} else if attr.path().is_ident("set") {
set_fn = Some(
attr.parse_args()
.unwrap_or_else(syn::Error::into_compile_error),
);
} else if attr.path().is_ident("readonly") {
readonly = true;
}
}

generate_accessor_fns(&field.ident.as_ref().unwrap(), get_fn, set_fn, readonly)
});
quote! {#(#iter)*}
}
syn::Fields::Unnamed(_) => {
syn::Error::new(Span::call_site(), "tuple structs are not yet implemented")
.into_compile_error()
}
syn::Fields::Unit => quote! {},
},
syn::Data::Enum(_) => {
syn::Error::new(Span::call_site(), "enums are not yet implemented").into_compile_error()
}
syn::Data::Union(_) => {
syn::Error::new(Span::call_site(), "unions are not supported").into_compile_error()
}
};

quote! {
impl ::rhai::CustomType for #name {
fn build(mut builder: ::rhai::TypeBuilder<'_, Self>) {
#accessors;
}
}
}
}

fn generate_accessor_fns(
field: &Ident,
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});

if readonly {
quote! {
builder.with_get("#field", #get);
}
} else {
quote! {
builder.with_get_set(
"#field",
#get,
#set,
);
}
}
}
10 changes: 9 additions & 1 deletion codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,10 @@
//!
use quote::quote;
use syn::{parse_macro_input, spanned::Spanned};
use syn::{parse_macro_input, spanned::Spanned, DeriveInput};

mod attrs;
mod custom_type;
mod function;
mod module;
mod register;
Expand Down Expand Up @@ -408,3 +409,10 @@ pub fn set_exported_global_fn(args: proc_macro::TokenStream) -> proc_macro::Toke
Err(e) => e.to_compile_error().into(),
}
}

#[proc_macro_derive(CustomType, attributes(get, set, readonly))]
pub fn derive_custom_type(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let expanded = custom_type::derive_custom_type_impl(input);
expanded.into()
}
15 changes: 15 additions & 0 deletions codegen/tests/test_derive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use rhai_codegen::CustomType;

// Sanity check to make sure everything compiles
#[derive(Clone, CustomType)]
pub struct Foo {
#[get(get_bar)]
bar: i32,
#[readonly]
baz: String,
qux: Vec<i32>,
}

fn get_bar(_this: &mut Foo) -> i32 {
42
}

0 comments on commit 6824d94

Please sign in to comment.