6
6
7
7
use godot_ffi as sys;
8
8
9
- use crate :: builtin:: { inner, ToVariant , Variant } ;
9
+ use crate :: builtin:: { inner, StringName , ToVariant , Variant , VariantArray } ;
10
10
use crate :: engine:: Object ;
11
11
use crate :: obj:: mem:: Memory ;
12
12
use crate :: obj:: { Gd , GodotClass , InstanceId } ;
13
- use std:: fmt;
13
+ use std:: { fmt, ptr } ;
14
14
use sys:: { ffi_methods, GodotFfi } ;
15
15
16
- use super :: { StringName , VariantArray } ;
17
-
18
16
/// A `Callable` represents a function in Godot.
19
17
///
20
18
/// Usually a callable is a reference to an `Object` and a method name, this is a standard callable. But can
@@ -53,14 +51,96 @@ impl Callable {
53
51
}
54
52
}
55
53
54
+ /// Create a callable from a Rust function or closure.
55
+ ///
56
+ /// `name` is used for the string representation of the closure, which helps debugging.
57
+ ///
58
+ /// Callables created through multiple `from_fn()` calls are never equal, even if they refer to the same function. If you want to use
59
+ /// equality, either clone an existing `Callable` instance, or define your own `PartialEq` impl with [`Callable::from_custom`].
60
+ ///
61
+ /// # Example
62
+ /// ```no_run
63
+ /// # use godot::prelude::*;
64
+ /// let callable = Callable::from_fn("sum", |args: &[&Variant]| {
65
+ /// let sum: i32 = args.iter().map(|arg| arg.to::<i32>()).sum();
66
+ /// Ok(sum.to_variant())
67
+ /// });
68
+ /// ```
69
+ #[ cfg( since_api = "4.2" ) ]
70
+ pub fn from_fn < F , S > ( name : S , rust_function : F ) -> Self
71
+ where
72
+ F : ' static + Send + Sync + FnMut ( & [ & Variant ] ) -> Result < Variant , ( ) > ,
73
+ S : Into < crate :: builtin:: GodotString > ,
74
+ {
75
+ let userdata = CallableUserdata {
76
+ inner : FnWrapper {
77
+ rust_function,
78
+ name : name. into ( ) ,
79
+ } ,
80
+ } ;
81
+
82
+ let info = sys:: GDExtensionCallableCustomInfo {
83
+ callable_userdata : Box :: into_raw ( Box :: new ( userdata) ) as * mut std:: ffi:: c_void ,
84
+ token : ptr:: null_mut ( ) ,
85
+ object : ptr:: null_mut ( ) ,
86
+ call_func : Some ( rust_callable_call_fn :: < F > ) ,
87
+ is_valid_func : None , // could be customized, but no real use case yet.
88
+ free_func : Some ( rust_callable_destroy :: < FnWrapper < F > > ) ,
89
+ hash_func : None ,
90
+ equal_func : None ,
91
+ // Op < is only used in niche scenarios and default is usually good enough, see https://github.com/godotengine/godot/issues/81901.
92
+ less_than_func : None ,
93
+ to_string_func : Some ( rust_callable_to_string_named :: < F > ) ,
94
+ } ;
95
+
96
+ Self :: from_custom_info ( info)
97
+ }
98
+
99
+ /// Create a highly configurable callable from Rust.
100
+ ///
101
+ /// See [`RustCallable`] for requirements on the type.
102
+ #[ cfg( since_api = "4.2" ) ]
103
+ pub fn from_custom < C : RustCallable > ( callable : C ) -> Self {
104
+ // Could theoretically use `dyn` but would need:
105
+ // - double boxing
106
+ // - a type-erased workaround for PartialEq supertrait (which has a `Self` type parameter and thus is not object-safe)
107
+ let userdata = CallableUserdata { inner : callable } ;
108
+
109
+ let info = sys:: GDExtensionCallableCustomInfo {
110
+ callable_userdata : Box :: into_raw ( Box :: new ( userdata) ) as * mut std:: ffi:: c_void ,
111
+ token : ptr:: null_mut ( ) ,
112
+ object : ptr:: null_mut ( ) ,
113
+ call_func : Some ( rust_callable_call_custom :: < C > ) ,
114
+ is_valid_func : None , // could be customized, but no real use case yet.
115
+ free_func : Some ( rust_callable_destroy :: < C > ) ,
116
+ hash_func : Some ( rust_callable_hash :: < C > ) ,
117
+ equal_func : Some ( rust_callable_equal :: < C > ) ,
118
+ // Op < is only used in niche scenarios and default is usually good enough, see https://github.com/godotengine/godot/issues/81901.
119
+ less_than_func : None ,
120
+ to_string_func : Some ( rust_callable_to_string_display :: < C > ) ,
121
+ } ;
122
+
123
+ Self :: from_custom_info ( info)
124
+ }
125
+
126
+ #[ cfg( since_api = "4.2" ) ]
127
+ fn from_custom_info ( mut info : sys:: GDExtensionCallableCustomInfo ) -> Callable {
128
+ // SAFETY: callable_custom_create() is a valid way of creating callables.
129
+ unsafe {
130
+ Callable :: from_sys_init ( |type_ptr| {
131
+ sys:: interface_fn!( callable_custom_create) ( type_ptr, ptr:: addr_of_mut!( info) )
132
+ } )
133
+ }
134
+ }
135
+
56
136
/// Creates an invalid/empty object that is not able to be called.
57
137
///
58
138
/// _Godot equivalent: `Callable()`_
59
139
pub fn invalid ( ) -> Self {
60
140
unsafe {
61
141
Self :: from_sys_init ( |self_ptr| {
62
- let ctor = :: godot_ffi :: builtin_fn!( callable_construct_default) ;
63
- ctor ( self_ptr, std :: ptr:: null_mut ( ) )
142
+ let ctor = sys :: builtin_fn!( callable_construct_default) ;
143
+ ctor ( self_ptr, ptr:: null_mut ( ) )
64
144
} )
65
145
}
66
146
}
@@ -177,12 +257,9 @@ impl_builtin_traits! {
177
257
// Currently no Default::default() to encourage explicit valid initialization.
178
258
//Default => callable_construct_default;
179
259
180
- // Equality for custom callables depend on the equality implementation of that custom callable. This
181
- // is from what i can tell currently implemented as total equality in all cases, but i dont believe
182
- // there are any guarantees that all implementations of equality for custom callables will be.
183
- //
184
- // So we cannot implement `Eq` here and be confident equality will be total for all future custom
185
- // callables.
260
+ // Equality for custom callables depend on the equality implementation of that custom callable.
261
+ // So we cannot implement `Eq` here and be confident equality will be total for all future custom callables.
262
+ // Godot does not define a less-than operator, so there is no `PartialOrd`.
186
263
PartialEq => callable_operator_equal;
187
264
Clone => callable_construct_copy;
188
265
Drop => callable_destroy;
@@ -202,7 +279,7 @@ unsafe impl GodotFfi for Callable {
202
279
}
203
280
}
204
281
205
- impl std :: fmt:: Debug for Callable {
282
+ impl fmt:: Debug for Callable {
206
283
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
207
284
let method = self . method_name ( ) ;
208
285
let object = self . object ( ) ;
@@ -214,8 +291,143 @@ impl std::fmt::Debug for Callable {
214
291
}
215
292
}
216
293
217
- impl std :: fmt:: Display for Callable {
294
+ impl fmt:: Display for Callable {
218
295
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
219
296
write ! ( f, "{}" , self . to_variant( ) )
220
297
}
221
298
}
299
+
300
+ // ----------------------------------------------------------------------------------------------------------------------------------------------
301
+ // Callbacks for custom implementations
302
+
303
+ #[ cfg( since_api = "4.2" ) ]
304
+ use custom_callable:: * ;
305
+
306
+ #[ cfg( since_api = "4.2" ) ]
307
+ pub use custom_callable:: RustCallable ;
308
+
309
+ #[ cfg( since_api = "4.2" ) ]
310
+ mod custom_callable {
311
+ use super :: * ;
312
+ use crate :: builtin:: GodotString ;
313
+ use std:: hash:: Hash ;
314
+
315
+ pub struct CallableUserdata < T > {
316
+ pub inner : T ,
317
+ }
318
+
319
+ impl < T > CallableUserdata < T > {
320
+ /// # Safety
321
+ /// Returns an unbounded reference. `void_ptr` must be a valid pointer to a `CallableUserdata`.
322
+ unsafe fn inner_from_raw < ' a > ( void_ptr : * mut std:: ffi:: c_void ) -> & ' a mut T {
323
+ let ptr = void_ptr as * mut CallableUserdata < T > ;
324
+ & mut ( * ptr) . inner
325
+ }
326
+ }
327
+
328
+ pub ( crate ) struct FnWrapper < F > {
329
+ pub ( crate ) rust_function : F ,
330
+ pub ( crate ) name : GodotString ,
331
+ }
332
+
333
+ /// Represents a custom callable object defined in Rust.
334
+ ///
335
+ /// This trait has a single method, `invoke`, which is called upon invocation.
336
+ ///
337
+ /// Since callables can be invoked from anywhere, they must be self-contained (`'static`) and thread-safe (`Send + Sync`).
338
+ /// They also should implement `Display` for the Godot string representation.
339
+ /// Furthermore, `PartialEq` and `Hash` are required for equality checks and usage as a key in a `Dictionary`.
340
+ pub trait RustCallable : ' static + PartialEq + Hash + fmt:: Display + Send + Sync {
341
+ /// Invokes the callable with the given arguments as `Variant` references.
342
+ ///
343
+ /// Return `Ok(...)` if the call succeeded, and `Err(())` otherwise.
344
+ /// Error handling is mostly needed in case argument number or types mismatch.
345
+ fn invoke ( & mut self , args : & [ & Variant ] ) -> Result < Variant , ( ) > ;
346
+ }
347
+
348
+ pub unsafe extern "C" fn rust_callable_call_custom < C : RustCallable > (
349
+ callable_userdata : * mut std:: ffi:: c_void ,
350
+ p_args : * const sys:: GDExtensionConstVariantPtr ,
351
+ p_argument_count : sys:: GDExtensionInt ,
352
+ r_return : sys:: GDExtensionVariantPtr ,
353
+ r_error : * mut sys:: GDExtensionCallError ,
354
+ ) {
355
+ let arg_refs: & [ & Variant ] =
356
+ Variant :: unbounded_refs_from_sys ( p_args, p_argument_count as usize ) ;
357
+
358
+ let c: & mut C = CallableUserdata :: inner_from_raw ( callable_userdata) ;
359
+
360
+ let result = c. invoke ( arg_refs) ;
361
+ crate :: builtin:: meta:: varcall_return_checked ( result, r_return, r_error) ;
362
+ }
363
+
364
+ pub unsafe extern "C" fn rust_callable_call_fn < F > (
365
+ callable_userdata : * mut std:: ffi:: c_void ,
366
+ p_args : * const sys:: GDExtensionConstVariantPtr ,
367
+ p_argument_count : sys:: GDExtensionInt ,
368
+ r_return : sys:: GDExtensionVariantPtr ,
369
+ r_error : * mut sys:: GDExtensionCallError ,
370
+ ) where
371
+ F : FnMut ( & [ & Variant ] ) -> Result < Variant , ( ) > ,
372
+ {
373
+ let arg_refs: & [ & Variant ] =
374
+ Variant :: unbounded_refs_from_sys ( p_args, p_argument_count as usize ) ;
375
+
376
+ let w: & mut FnWrapper < F > = CallableUserdata :: inner_from_raw ( callable_userdata) ;
377
+
378
+ let result = ( w. rust_function ) ( arg_refs) ;
379
+ crate :: builtin:: meta:: varcall_return_checked ( result, r_return, r_error) ;
380
+ }
381
+
382
+ pub unsafe extern "C" fn rust_callable_destroy < T > ( callable_userdata : * mut std:: ffi:: c_void ) {
383
+ let rust_ptr = callable_userdata as * mut CallableUserdata < T > ;
384
+ let _drop = Box :: from_raw ( rust_ptr) ;
385
+ }
386
+
387
+ pub unsafe extern "C" fn rust_callable_hash < T : Hash > (
388
+ callable_userdata : * mut std:: ffi:: c_void ,
389
+ ) -> u32 {
390
+ let c: & T = CallableUserdata :: < T > :: inner_from_raw ( callable_userdata) ;
391
+
392
+ // Just cut off top bits, not best-possible hash.
393
+ sys:: hash_value ( c) as u32
394
+ }
395
+
396
+ pub unsafe extern "C" fn rust_callable_equal < T : PartialEq > (
397
+ callable_userdata_a : * mut std:: ffi:: c_void ,
398
+ callable_userdata_b : * mut std:: ffi:: c_void ,
399
+ ) -> sys:: GDExtensionBool {
400
+ let a: & T = CallableUserdata :: inner_from_raw ( callable_userdata_a) ;
401
+ let b: & T = CallableUserdata :: inner_from_raw ( callable_userdata_b) ;
402
+
403
+ ( a == b) as sys:: GDExtensionBool
404
+ }
405
+
406
+ pub unsafe extern "C" fn rust_callable_to_string_display < T : fmt:: Display > (
407
+ callable_userdata : * mut std:: ffi:: c_void ,
408
+ r_is_valid : * mut sys:: GDExtensionBool ,
409
+ r_out : sys:: GDExtensionStringPtr ,
410
+ ) {
411
+ let c: & T = CallableUserdata :: inner_from_raw ( callable_userdata) ;
412
+ let s = crate :: builtin:: GodotString :: from ( c. to_string ( ) ) ;
413
+
414
+ s. move_string_ptr ( r_out) ;
415
+ * r_is_valid = true as sys:: GDExtensionBool ;
416
+ }
417
+
418
+ pub unsafe extern "C" fn rust_callable_to_string_named < F > (
419
+ callable_userdata : * mut std:: ffi:: c_void ,
420
+ r_is_valid : * mut sys:: GDExtensionBool ,
421
+ r_out : sys:: GDExtensionStringPtr ,
422
+ ) {
423
+ let w: & mut FnWrapper < F > = CallableUserdata :: inner_from_raw ( callable_userdata) ;
424
+
425
+ w. name . clone ( ) . move_string_ptr ( r_out) ;
426
+ * r_is_valid = true as sys:: GDExtensionBool ;
427
+ }
428
+
429
+ pub fn unqualified_type_name < T > ( ) -> & ' static str {
430
+ let type_name = std:: any:: type_name :: < T > ( ) ;
431
+ type_name. split ( "::" ) . last ( ) . unwrap ( )
432
+ }
433
+ }
0 commit comments