From 02538cb5de78480a063dd0573ad76fd450024eca Mon Sep 17 00:00:00 2001 From: B_head Date: Thu, 10 Mar 2022 02:30:00 +0900 Subject: [PATCH] Add method attribute Owner can be omitted. --- gdnative-core/src/export/macros.rs | 30 ++++++++----- gdnative-derive/src/lib.rs | 2 +- gdnative-derive/src/methods.rs | 69 +++++++++++++++++++++++++----- 3 files changed, 78 insertions(+), 23 deletions(-) diff --git a/gdnative-core/src/export/macros.rs b/gdnative-core/src/export/macros.rs index df11a9372..f6ef28a9c 100644 --- a/gdnative-core/src/export/macros.rs +++ b/gdnative-core/src/export/macros.rs @@ -29,6 +29,14 @@ macro_rules! deprecated_reference_return { () => {}; } +#[doc(hidden)] +#[macro_export] +macro_rules! godot_wrap_method_void { + ($ident:ident, $void:tt) => { + $ident + }; +} + #[doc(hidden)] #[macro_export] macro_rules! godot_wrap_method_inner { @@ -38,7 +46,7 @@ macro_rules! godot_wrap_method_inner { $map_method:ident, fn $method_name:ident( $self:ident - , $owner:ident : $owner_ty:ty + $(, #[base] $base:ident : $base_ty:ty)? $(, $pname:ident : $pty:ty)* $(, #[opt] $opt_pname:ident : $opt_pty:ty)* ) -> $retty:ty @@ -67,11 +75,11 @@ macro_rules! godot_wrap_method_inner { Args { $($pname,)* $($opt_pname,)* }: Args, ) -> $crate::core_types::Variant { this - .$map_method(|__rust_val, $owner| { + .$map_method(|__rust_val, __base| { #[allow(unused_unsafe)] unsafe { let ret = __rust_val.$method_name( - OwnerArg::from_safe_ref($owner), + $(OwnerArg::from_safe_ref($crate::godot_wrap_method_void!(__base,$base)),)? $($pname,)* $($opt_pname,)* ); @@ -118,7 +126,7 @@ macro_rules! godot_wrap_method { $is_deref_return:ident, fn $method_name:ident( &mut $self:ident - , $owner:ident : $owner_ty:ty + $(, #[base] $base:ident : $base_ty:ty)? $(, $pname:ident : $pty:ty)* $(, #[opt] $opt_pname:ident : $opt_pty:ty)* $(,)? @@ -130,7 +138,7 @@ macro_rules! godot_wrap_method { map_mut, fn $method_name( $self - , $owner : $owner_ty + $(, #[base] $base : $base_ty)? $(, $pname : $pty)* $(, #[opt] $opt_pname : $opt_pty)* ) -> godot_wrap_method_return_type!($($retty)?) @@ -142,7 +150,7 @@ macro_rules! godot_wrap_method { $is_deref_return:ident, fn $method_name:ident( & $self:ident - , $owner:ident : $owner_ty:ty + $(, #[base] $base:ident : $base_ty:ty)? $(, $pname:ident : $pty:ty)* $(, #[opt] $opt_pname:ident : $opt_pty:ty)* $(,)? @@ -154,7 +162,7 @@ macro_rules! godot_wrap_method { map, fn $method_name( $self - , $owner : $owner_ty + $(, #[base] $base : $base_ty)? $(, $pname : $pty)* $(, #[opt] $opt_pname : $opt_pty)* ) -> godot_wrap_method_return_type!($($retty)?) @@ -166,7 +174,7 @@ macro_rules! godot_wrap_method { $is_deref_return:ident, fn $method_name:ident( mut $self:ident - , $owner:ident : $owner_ty:ty + $(, #[base] $base:ident : $base_ty:ty)? $(, $pname:ident : $pty:ty)* $(, #[opt] $opt_pname:ident : $opt_pty:ty)* $(,)? @@ -178,7 +186,7 @@ macro_rules! godot_wrap_method { map_owned, fn $method_name( $self - , $owner : $owner_ty + $(, #[base] $base : $base_ty)? $(, $pname : $pty)* $(, #[opt] $opt_pname : $opt_pty)* ) -> godot_wrap_method_return_type!($($retty)?) @@ -190,7 +198,7 @@ macro_rules! godot_wrap_method { $is_deref_return:ident, fn $method_name:ident( $self:ident - , $owner:ident : $owner_ty:ty + $(, #[base] $base:ident : $base_ty:ty)? $(, $pname:ident : $pty:ty)* $(, #[opt] $opt_pname:ident : $opt_pty:ty)* $(,)? @@ -202,7 +210,7 @@ macro_rules! godot_wrap_method { map_owned, fn $method_name( $self - , $owner : $owner_ty + $(, #[base] $base : $base_ty)? $(, $pname : $pty)* $(, #[opt] $opt_pname : $opt_pty)* ) -> godot_wrap_method_return_type!($($retty)?) diff --git a/gdnative-derive/src/lib.rs b/gdnative-derive/src/lib.rs index e37c3c006..c3b2e3a7d 100644 --- a/gdnative-derive/src/lib.rs +++ b/gdnative-derive/src/lib.rs @@ -50,7 +50,7 @@ mod variant; /// impl gdnative::export::NativeClassMethods for Foo { /// fn register(builder: &ClassBuilder) { /// use gdnative::export::*; -/// builder.method("foo", gdnative::export::godot_wrap_method!(Foo, false, fn foo(&self, _owner: &Reference, bar: i64) -> i64)) +/// builder.method("foo", gdnative::export::godot_wrap_method!(Foo, false, fn foo(&self, #[base] _owner: &Reference, bar: i64) -> i64)) /// .with_rpc_mode(RpcMode::Disabled) /// .done_stateless(); /// } diff --git a/gdnative-derive/src/methods.rs b/gdnative-derive/src/methods.rs index 6671ac1d6..7f3eeffd2 100644 --- a/gdnative-derive/src/methods.rs +++ b/gdnative-derive/src/methods.rs @@ -60,10 +60,12 @@ pub(crate) struct ExportMethod { pub(crate) sig: Signature, pub(crate) export_args: ExportArgs, pub(crate) optional_args: Option, + pub(crate) exist_base_arg: bool, } #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)] pub(crate) struct ExportArgs { + pub(crate) is_old_syntax: bool, pub(crate) rpc_mode: Option, pub(crate) name_override: Option, pub(crate) is_deref_return: bool, @@ -80,7 +82,7 @@ pub(crate) fn derive_methods(item_impl: ItemImpl) -> TokenStream2 { let methods = export .methods .into_iter() - .map(|ExportMethod { sig, export_args , optional_args}| { + .map(|ExportMethod { sig, export_args , optional_args, exist_base_arg}| { let sig_span = sig.ident.span(); let name = sig.ident; @@ -93,10 +95,18 @@ pub(crate) fn derive_methods(item_impl: ItemImpl) -> TokenStream2 { let arg_count = sig.inputs.len(); - if arg_count < 2 { + if arg_count == 0 { return syn::Error::new( sig_span, - "exported methods must take self and owner as arguments", + "exported methods must take self as argument", + ) + .to_compile_error(); + } + + if export_args.is_old_syntax && !exist_base_arg { + return syn::Error::new( + sig_span, + "exported methods must take second argument", ) .to_compile_error(); } @@ -121,7 +131,10 @@ pub(crate) fn derive_methods(item_impl: ItemImpl) -> TokenStream2 { let args = sig.inputs.iter().enumerate().map(|(n, arg)| { let span = arg.span(); - if n < arg_count - optional_args { + if exist_base_arg && n == 1 { + quote_spanned!(span => #[base] #arg ,) + } + else if n < arg_count - optional_args { quote_spanned!(span => #arg ,) } else { quote_spanned!(span => #[opt] #arg ,) @@ -208,8 +221,19 @@ fn impl_gdnative_expose(ast: ItemImpl) -> (ItemImpl, ClassMethodExport) { .last() .map(|i| i.ident.to_string()); - if let Some("export") = last_seg.as_deref() { - let mut _export_args = export_args.get_or_insert_with(ExportArgs::default); + let (is_export, is_old_syntax) = if let Some("export") = last_seg.as_deref() + { + (true, true) + } else if let Some("method") = last_seg.as_deref() { + (true, false) + } else { + (false, false) + }; + + if is_export { + let mut _export_args = + export_args.get_or_insert_with(ExportArgs::default); + _export_args.is_old_syntax = is_old_syntax; use syn::{punctuated::Punctuated, Lit, Meta, NestedMeta}; let nested_meta_iter = match attr.parse_meta() { Err(err) => { @@ -290,7 +314,11 @@ fn impl_gdnative_expose(ast: ItemImpl) -> (ItemImpl, ClassMethodExport) { )); } Some(Lit::Str(str)) => { - if _export_args.name_override.replace(str.value()).is_some() { + if _export_args + .name_override + .replace(str.value()) + .is_some() + { errors.push(syn::Error::new( nested_meta.span(), "name was set more than once", @@ -336,6 +364,7 @@ fn impl_gdnative_expose(ast: ItemImpl) -> (ItemImpl, ClassMethodExport) { if let Some(export_args) = export_args.take() { let mut optional_args = None; + let mut exist_base_arg = false; for (n, arg) in method.sig.inputs.iter_mut().enumerate() { let attrs = match arg { @@ -344,32 +373,49 @@ fn impl_gdnative_expose(ast: ItemImpl) -> (ItemImpl, ClassMethodExport) { }; let mut is_optional = false; + let mut is_base = false; attrs.retain(|attr| { if attr.path.is_ident("opt") { is_optional = true; false + } else if attr.path.is_ident("base") { + is_base = true; + false } else { true } }); + // In the old syntax, the second argument is always the base argument. + if export_args.is_old_syntax && n == 1 { + is_base = true; + } + if is_optional { if n < 2 { errors.push(syn::Error::new( arg.span(), "self or owner cannot be optional", )); - continue; + } else { + *optional_args.get_or_insert(0) += 1; } - - *optional_args.get_or_insert(0) += 1; } else if optional_args.is_some() { errors.push(syn::Error::new( arg.span(), "cannot add required parameters after optional ones", )); - continue; + } + + if is_base { + exist_base_arg = true; + if n != 1 { + errors.push(syn::Error::new( + arg.span(), + "base must be the second argument.", + )); + } } } @@ -377,6 +423,7 @@ fn impl_gdnative_expose(ast: ItemImpl) -> (ItemImpl, ClassMethodExport) { sig: method.sig.clone(), export_args, optional_args, + exist_base_arg, }); }