Skip to content

Commit

Permalink
Add method attribute
Browse files Browse the repository at this point in the history
Owner can be omitted.
  • Loading branch information
B-head committed Mar 9, 2022
1 parent 3724c36 commit 02538cb
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 23 deletions.
30 changes: 19 additions & 11 deletions gdnative-core/src/export/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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,)*
);
Expand Down Expand Up @@ -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)*
$(,)?
Expand All @@ -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)?)
Expand All @@ -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)*
$(,)?
Expand All @@ -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)?)
Expand All @@ -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)*
$(,)?
Expand All @@ -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)?)
Expand All @@ -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)*
$(,)?
Expand All @@ -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)?)
Expand Down
2 changes: 1 addition & 1 deletion gdnative-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ mod variant;
/// impl gdnative::export::NativeClassMethods for Foo {
/// fn register(builder: &ClassBuilder<Self>) {
/// 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();
/// }
Expand Down
69 changes: 58 additions & 11 deletions gdnative-derive/src/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,12 @@ pub(crate) struct ExportMethod {
pub(crate) sig: Signature,
pub(crate) export_args: ExportArgs,
pub(crate) optional_args: Option<usize>,
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<RpcMode>,
pub(crate) name_override: Option<String>,
pub(crate) is_deref_return: bool,
Expand All @@ -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;
Expand All @@ -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();
}
Expand All @@ -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 ,)
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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 {
Expand All @@ -344,39 +373,57 @@ 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.",
));
}
}
}

methods_to_export.push(ExportMethod {
sig: method.sig.clone(),
export_args,
optional_args,
exist_base_arg,
});
}

Expand Down

0 comments on commit 02538cb

Please sign in to comment.