Skip to content

Commit 958b274

Browse files
author
toasteater
committed
Allow parsing arguments lists into custom generic structs
This adds the `FromVarargs` trait and derive macro, which supports using generic structs as argument lists, with the same `#[opt]` syntax for optional arguments. This makes it a lot easier to actually make use of the `Method` interface outside `#[methods]`. This is the final step in simplifying `godot_wrap_method_inner`, which is now a lot easier to follow, and no longer outputs unsafe code into the user crate!
1 parent c464b49 commit 958b274

File tree

11 files changed

+592
-174
lines changed

11 files changed

+592
-174
lines changed

gdnative-core/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ approx = "0.3.2"
2323
euclid = "0.22.1"
2424
indexmap = "1.6.0"
2525
ahash = "0.4.5"
26-
thiserror = "1.0"
2726

2827
gdnative-impl-proc-macros = { path = "../impl/proc_macros", version = "=0.9.1" }
2928

gdnative-core/src/nativescript/init.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
//! For full examples, see [`examples`](https://github.com/godot-rust/godot-rust/tree/master/examples)
3232
//! in the godot-rust repository.
3333
34+
// Temporary for unsafe method registration
35+
#![allow(deprecated)]
36+
3437
use crate::*;
3538

3639
use std::ffi::CString;
@@ -48,7 +51,9 @@ use super::emplace;
4851
pub mod method;
4952
pub mod property;
5053

51-
pub use self::method::{Method, Varargs, RpcMode, ScriptMethod, MethodBuilder, ScriptMethodAttributes, ScriptMethodFn};
54+
pub use self::method::{
55+
Method, MethodBuilder, RpcMode, ScriptMethod, ScriptMethodAttributes, ScriptMethodFn, Varargs,
56+
};
5257
pub use self::property::{Export, ExportInfo, PropertyBuilder, Usage as PropertyUsage};
5358

5459
/// A handle that can register new classes to the engine during initialization.
@@ -223,6 +228,7 @@ pub struct ClassBuilder<C> {
223228

224229
impl<C: NativeClass> ClassBuilder<C> {
225230
#[inline]
231+
#[deprecated(note = "Unsafe registration is deprecated. Use `build_method` instead.")]
226232
pub fn add_method_advanced(&self, method: ScriptMethod) {
227233
let method_name = CString::new(method.name).unwrap();
228234

@@ -256,6 +262,7 @@ impl<C: NativeClass> ClassBuilder<C> {
256262
}
257263

258264
#[inline]
265+
#[deprecated(note = "Unsafe registration is deprecated. Use `build_method` instead.")]
259266
pub fn add_method_with_rpc_mode(&self, name: &str, method: ScriptMethodFn, rpc_mode: RpcMode) {
260267
self.add_method_advanced(ScriptMethod {
261268
name,
@@ -267,12 +274,32 @@ impl<C: NativeClass> ClassBuilder<C> {
267274
}
268275

269276
#[inline]
277+
#[deprecated(note = "Unsafe registration is deprecated. Use `build_method` instead.")]
270278
pub fn add_method(&self, name: &str, method: ScriptMethodFn) {
271279
self.add_method_with_rpc_mode(name, method, RpcMode::Disabled);
272280
}
273281

282+
/// Returns a `MethodBuilder` which can be used to add a method to the class being
283+
/// registered.
284+
///
285+
/// # Examples
286+
///
287+
/// Basic usage:
288+
///
289+
/// ```ignore
290+
/// // `Bar` is a stateful method implementing `Method<C>`
291+
///
292+
/// builder
293+
/// .build_method("foo", Bar { baz: 42 })
294+
/// .with_rpc_mode(RpcMode::RemoteSync)
295+
/// .done();
296+
/// ```
274297
#[inline]
275-
pub fn build_method<'a, F: Method<C>>(&'a self, name: &'a str, method: F) -> MethodBuilder<'a, C, F> {
298+
pub fn build_method<'a, F: Method<C>>(
299+
&'a self,
300+
name: &'a str,
301+
method: F,
302+
) -> MethodBuilder<'a, C, F> {
276303
MethodBuilder::new(self, name, method)
277304
}
278305

gdnative-core/src/nativescript/init/method.rs

Lines changed: 96 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
1+
//! Method registration
2+
3+
// Temporary for unsafe method registration
4+
#![allow(deprecated)]
5+
16
use std::fmt;
27
use std::marker::PhantomData;
38

4-
use crate::thread_access::Shared;
5-
use crate::nativescript::class::{NativeClass, RefInstance};
69
use crate::core_types::{FromVariant, FromVariantError, Variant};
10+
use crate::nativescript::class::{NativeClass, RefInstance};
711
use crate::object::{Ref, TRef};
12+
use crate::thread_access::Shared;
813

914
use super::ClassBuilder;
1015

16+
/// Builder type used to register a method on a `NativeClass`.
1117
pub struct MethodBuilder<'a, C, F> {
1218
class_builder: &'a super::ClassBuilder<C>,
1319
name: &'a str,
@@ -80,6 +86,9 @@ where
8086
}
8187
}
8288

89+
#[deprecated(
90+
note = "Unsafe registration is deprecated. Use the safe, higher-level `MethodBuilder` API instead."
91+
)]
8392
pub type ScriptMethodFn = unsafe extern "C" fn(
8493
*mut sys::godot_object,
8594
*mut libc::c_void,
@@ -105,10 +114,16 @@ impl Default for RpcMode {
105114
}
106115
}
107116

117+
#[deprecated(
118+
note = "Unsafe registration is deprecated. Use the safe, higher-level `MethodBuilder` API instead."
119+
)]
108120
pub struct ScriptMethodAttributes {
109121
pub rpc_mode: RpcMode,
110122
}
111123

124+
#[deprecated(
125+
note = "Unsafe registration is deprecated. Use the safe, higher-level `MethodBuilder` API instead."
126+
)]
112127
pub struct ScriptMethod<'l> {
113128
pub name: &'l str,
114129
pub method_ptr: Option<ScriptMethodFn>,
@@ -118,7 +133,7 @@ pub struct ScriptMethod<'l> {
118133
pub free_func: Option<unsafe extern "C" fn(*mut libc::c_void) -> ()>,
119134
}
120135

121-
/// Low-level trait for stateful, variadic methods that can be called on a native script type.
136+
/// Safe low-level trait for stateful, variadic methods that can be called on a native script type.
122137
pub trait Method<C: NativeClass>: Send + Sync + 'static {
123138
fn call(&self, this: RefInstance<'_, C, Shared>, args: Varargs<'_>) -> Variant;
124139
}
@@ -135,7 +150,50 @@ impl<C: NativeClass, F: Method<C> + Copy + Default> Method<C> for Stateless<F> {
135150
}
136151
}
137152

138-
/// Interface to a list of borrowed method arguments with a convenient interface
153+
/// Adapter for methods whose arguments are statically determined. If the arguments would fail to
154+
/// type check, the method will print the errors to Godot's debug console and return `null`.
155+
#[derive(Clone, Copy, Default, Debug)]
156+
pub struct StaticArgs<F> {
157+
f: F,
158+
}
159+
160+
impl<F> StaticArgs<F> {
161+
/// Wrap `f` in an adapter that implements `Method`.
162+
#[inline]
163+
pub fn new(f: F) -> Self {
164+
StaticArgs { f }
165+
}
166+
}
167+
168+
/// Trait for methods whose argument lists are known at compile time. Not to be confused with a
169+
/// "static method".
170+
pub trait StaticArgsMethod<C: NativeClass>: Send + Sync + 'static {
171+
type Args: FromVarargs;
172+
fn call(&self, this: RefInstance<'_, C, Shared>, args: Self::Args) -> Variant;
173+
}
174+
175+
impl<C: NativeClass, F: StaticArgsMethod<C>> Method<C> for StaticArgs<F> {
176+
#[inline]
177+
fn call(&self, this: RefInstance<'_, C, Shared>, mut args: Varargs<'_>) -> Variant {
178+
match args.read_many::<F::Args>() {
179+
Ok(parsed) => {
180+
if let Err(err) = args.done() {
181+
godot_error!("{}", err);
182+
return Variant::new();
183+
}
184+
F::call(&self.f, this, parsed)
185+
}
186+
Err(errors) => {
187+
for err in errors {
188+
godot_error!("{}", err);
189+
}
190+
Variant::new()
191+
}
192+
}
193+
}
194+
}
195+
196+
/// Safe interface to a list of borrowed method arguments with a convenient API
139197
/// for common operations with them. Can also be used as an iterator.
140198
pub struct Varargs<'a> {
141199
idx: usize,
@@ -166,6 +224,13 @@ impl<'a> Varargs<'a> {
166224
}
167225
}
168226

227+
/// Parses a structure that implements `FromVarargs` incrementally from the
228+
/// remaining arguments.
229+
#[inline]
230+
pub fn read_many<T: FromVarargs>(&mut self) -> Result<T, Vec<ArgumentError<'a>>> {
231+
T::read(self)
232+
}
233+
169234
/// Returns the remaining arguments as a slice of `Variant`s.
170235
#[inline]
171236
pub fn as_slice(&self) -> &'a [&'a Variant] {
@@ -213,19 +278,36 @@ impl<'a> Iterator for Varargs<'a> {
213278
}
214279
}
215280

281+
/// Trait for structures that can be parsed from `Varargs`.
282+
///
283+
/// This trait can be derived for structure types where each type implements `FromVariant`.
284+
/// The order of fields matter for this purpose:
285+
///
286+
/// ```ignore
287+
/// #[derive(FromVarargs)]
288+
/// struct MyArgs {
289+
/// foo: i32,
290+
/// bar: String,
291+
/// #[opt] baz: Option<Ref<Node>>,
292+
/// }
293+
/// ```
294+
pub trait FromVarargs: Sized {
295+
fn read<'a>(args: &mut Varargs<'a>) -> Result<Self, Vec<ArgumentError<'a>>>;
296+
}
297+
216298
/// Builder for providing additional argument information for error reporting.
217299
pub struct ArgBuilder<'r, 'a, T> {
218300
args: &'r mut Varargs<'a>,
219-
name: Option<&'r str>,
220-
ty: Option<&'r str>,
301+
name: Option<&'a str>,
302+
ty: Option<&'a str>,
221303
_marker: PhantomData<T>,
222304
}
223305

224306
impl<'r, 'a, T> ArgBuilder<'r, 'a, T> {
225307
/// Provides a name for this argument. If an old name is already set, it is
226308
/// silently replaced.
227309
#[inline]
228-
pub fn with_name(mut self, name: &'r str) -> Self {
310+
pub fn with_name(mut self, name: &'a str) -> Self {
229311
self.name = Some(name);
230312
self
231313
}
@@ -234,7 +316,7 @@ impl<'r, 'a, T> ArgBuilder<'r, 'a, T> {
234316
/// already set, it is silently replaced. If no type name is given, a value
235317
/// from `std::any::type_name` is used.
236318
#[inline]
237-
pub fn with_type_name(mut self, ty: &'r str) -> Self {
319+
pub fn with_type_name(mut self, ty: &'a str) -> Self {
238320
self.ty = Some(ty);
239321
self
240322
}
@@ -247,7 +329,7 @@ impl<'r, 'a, T: FromVariant> ArgBuilder<'r, 'a, T> {
247329
///
248330
/// If the argument is missing, or cannot be converted to the desired type.
249331
#[inline]
250-
pub fn get(self) -> Result<T, ArgumentError<'r>> {
332+
pub fn get(self) -> Result<T, ArgumentError<'a>> {
251333
let name = self.name;
252334
let idx = self.args.idx;
253335

@@ -261,7 +343,7 @@ impl<'r, 'a, T: FromVariant> ArgBuilder<'r, 'a, T> {
261343
///
262344
/// If the argument is present, but cannot be converted to the desired type.
263345
#[inline]
264-
pub fn get_optional(self) -> Result<Option<T>, ArgumentError<'r>> {
346+
pub fn get_optional(self) -> Result<Option<T>, ArgumentError<'a>> {
265347
let Self { args, name, ty, .. } = self;
266348
let idx = args.idx;
267349

@@ -282,6 +364,7 @@ impl<'r, 'a, T: FromVariant> ArgBuilder<'r, 'a, T> {
282364
}
283365
}
284366

367+
/// Error during argument parsing.
285368
#[derive(Debug)]
286369
pub enum ArgumentError<'a> {
287370
Missing {
@@ -380,7 +463,7 @@ unsafe extern "C" fn method_wrapper<C: NativeClass, F: Method<C>>(
380463
C::class_name(),
381464
);
382465
return Variant::new().forget();
383-
},
466+
}
384467
};
385468

386469
let result = std::panic::catch_unwind(move || {
@@ -389,7 +472,7 @@ unsafe extern "C" fn method_wrapper<C: NativeClass, F: Method<C>>(
389472
let this: Ref<C::Base, Shared> = Ref::from_sys(this);
390473
let this: TRef<'_, C::Base, _> = this.assume_safe_unchecked();
391474
let this: RefInstance<'_, C, _> = RefInstance::from_raw_unchecked(this, user_data);
392-
475+
393476
let args = Varargs::from_sys(num_args, args);
394477

395478
F::call(method, this, args)
@@ -405,4 +488,4 @@ unsafe extern "C" fn method_wrapper<C: NativeClass, F: Method<C>>(
405488

406489
unsafe extern "C" fn free_func<F>(method_data: *mut libc::c_void) {
407490
drop(Box::from_raw(method_data as *mut F))
408-
}
491+
}

0 commit comments

Comments
 (0)