Skip to content

Commit f06a055

Browse files
committed
Flesh out Callable functions
1 parent ff2dec6 commit f06a055

File tree

3 files changed

+132
-13
lines changed

3 files changed

+132
-13
lines changed

godot-codegen/src/special_cases/special_cases.rs

+8
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,14 @@ pub fn is_builtin_method_exposed(builtin_ty: &TyName, godot_method_name: &str) -
241241

242242
// NodePath
243243

244+
// Callable
245+
| ("Callable", "call")
246+
| ("Callable", "call_deferred")
247+
| ("Callable", "bind")
248+
| ("Callable", "get_bound_arguments")
249+
| ("Callable", "rpc")
250+
| ("Callable", "rpc_id")
251+
244252
// (add more builtin types below)
245253

246254
=> true, _ => false

godot-core/src/builtin/callable.rs

+14
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,20 @@ impl Callable {
335335
self.as_inner().is_valid()
336336
}
337337

338+
pub fn unbind(&self, args: usize) -> Callable {
339+
self.as_inner().unbind(args as i64)
340+
}
341+
342+
#[doc(alias = "get_argument_count")]
343+
pub fn arg_len(&self) -> usize {
344+
self.as_inner().get_argument_count() as usize
345+
}
346+
347+
#[doc(alias = "get_bound_arguments_count")]
348+
pub fn bound_args_len(&self) -> i64 {
349+
self.as_inner().get_bound_arguments_count()
350+
}
351+
338352
#[doc(hidden)]
339353
pub fn as_inner(&self) -> inner::InnerCallable {
340354
inner::InnerCallable::from_outer(self)

itest/rust/src/builtin_tests/containers/callable_test.rs

+110-13
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
*/
77

88
use godot::builtin::inner::InnerCallable;
9-
use godot::builtin::{varray, Callable, GString, StringName, Variant};
10-
use godot::classes::{Node2D, Object};
9+
use godot::builtin::{
10+
array, varray, Array, Callable, GString, NodePath, StringName, Variant, VariantArray,
11+
};
12+
use godot::classes::{Node2D, Object, RefCounted};
1113
use godot::meta::ToGodot;
12-
use godot::obj::{NewAlloc, NewGd};
14+
use godot::obj::{Gd, NewAlloc, NewGd};
1315
use godot::register::{godot_api, GodotClass};
1416
use std::hash::Hasher;
1517
use std::sync::atomic::{AtomicU32, Ordering};
@@ -33,6 +35,11 @@ impl CallableTestObj {
3335
fn bar(&self, b: i32) -> GString {
3436
b.to_variant().stringify()
3537
}
38+
39+
#[func]
40+
fn baz(&self, a: i32, b: GString, c: Array<NodePath>, d: Gd<RefCounted>) -> VariantArray {
41+
varray![a, b, c, d]
42+
}
3643
}
3744

3845
#[itest]
@@ -86,7 +93,7 @@ fn callable_object_method() {
8693
}
8794

8895
#[itest]
89-
fn callable_call() {
96+
fn callable_callv() {
9097
let obj = CallableTestObj::new_gd();
9198
let callable = obj.callable("foo");
9299

@@ -106,6 +113,30 @@ fn callable_call() {
106113
assert_eq!(Callable::invalid().callv(&varray![1, 2, 3]), Variant::nil());
107114
}
108115

116+
#[itest]
117+
fn callable_call() {
118+
let obj = CallableTestObj::new_gd();
119+
let callable = obj.callable("foo");
120+
121+
assert_eq!(obj.bind().value, 0);
122+
callable.call(&[10.to_variant()]);
123+
assert_eq!(obj.bind().value, 10);
124+
125+
// Too many arguments: this call fails, its logic is not applied.
126+
// In the future, panic should be propagated to caller.
127+
callable.call(&[20.to_variant(), 30.to_variant()]);
128+
assert_eq!(obj.bind().value, 10);
129+
130+
// TODO(bromeon): this causes a Rust panic, but since call() is routed to Godot, the panic is handled at the FFI boundary.
131+
// Can there be a way to notify the caller about failed calls like that?
132+
assert_eq!(callable.call(&["string".to_variant()]), Variant::nil());
133+
134+
assert_eq!(
135+
Callable::invalid().call(&[1.to_variant(), 2.to_variant(), 3.to_variant()]),
136+
Variant::nil()
137+
);
138+
}
139+
109140
#[itest]
110141
fn callable_call_return() {
111142
let obj = CallableTestObj::new_gd();
@@ -151,24 +182,90 @@ fn callable_bindv() {
151182
);
152183
}
153184

154-
// This is also testing that using works at all.
155185
#[itest]
156-
#[cfg(since_api = "4.2")]
157-
fn callable_varargs() {
158-
// TODO: Replace with proper apis instead of using `InnerCallable`.
159-
use godot::builtin::inner;
186+
fn callable_bind() {
160187
let obj = CallableTestObj::new_gd();
161188
let callable = obj.callable("bar");
162-
let inner_callable = inner::InnerCallable::from_outer(&callable);
163-
let callable_bound = inner_callable.bind(&[10.to_variant()]);
164-
let inner_callable_bound = inner::InnerCallable::from_outer(&callable_bound);
189+
let callable_bound = callable.bind(&[10.to_variant()]);
165190

166191
assert_eq!(
167-
inner_callable_bound.call(&[]),
192+
callable_bound.call(&[]),
168193
10.to_variant().stringify().to_variant()
169194
);
170195
}
171196

197+
#[itest]
198+
fn callable_unbind() {
199+
let obj = CallableTestObj::new_gd();
200+
let callable = obj.callable("bar");
201+
let callable_unbound = callable.unbind(3);
202+
203+
assert_eq!(
204+
callable_unbound.call(&[
205+
121.to_variant(),
206+
20.to_variant(),
207+
30.to_variant(),
208+
40.to_variant()
209+
]),
210+
121.to_variant().stringify().to_variant()
211+
);
212+
}
213+
214+
#[itest]
215+
fn callable_arg_len() {
216+
let obj = CallableTestObj::new_gd();
217+
218+
assert_eq!(obj.callable("foo").arg_len(), 1);
219+
assert_eq!(obj.callable("bar").arg_len(), 1);
220+
assert_eq!(obj.callable("baz").arg_len(), 4);
221+
assert_eq!(obj.callable("foo").unbind(10).arg_len(), 11);
222+
assert_eq!(
223+
obj.callable("baz")
224+
.bind(&[10.to_variant(), "hello".to_variant()])
225+
.arg_len(),
226+
2
227+
);
228+
}
229+
230+
#[itest]
231+
fn callable_bound_args_len() {
232+
let obj = CallableTestObj::new_gd();
233+
234+
assert_eq!(obj.callable("foo").bound_args_len(), 0);
235+
assert_eq!(
236+
obj.callable("foo")
237+
.bind(&[10.to_variant()])
238+
.bound_args_len(),
239+
1
240+
);
241+
assert_eq!(obj.callable("foo").unbind(28).bound_args_len(), -28);
242+
assert_eq!(
243+
obj.callable("foo")
244+
.bind(&[10.to_variant()])
245+
.unbind(5)
246+
.bound_args_len(),
247+
-4
248+
);
249+
}
250+
251+
#[itest]
252+
fn callable_get_bound_arguments() {
253+
let obj = CallableTestObj::new_gd();
254+
255+
let a = 10.to_variant();
256+
let b = "hello!".to_variant();
257+
let c: Array<NodePath> = array!["my/node/path"];
258+
let c = c.to_variant();
259+
let d = RefCounted::new_gd().to_variant();
260+
261+
let callable = obj.callable("baz");
262+
let callable_bound = callable.bind(&[a.clone(), b.clone(), c.clone(), d.clone()]);
263+
264+
assert_eq!(callable_bound.get_bound_arguments(), varray![a, b, c, d]);
265+
}
266+
267+
// TODO: Add tests for `Callable::rpc` and `Callable::rpc_id`.
268+
172269
// Testing https://github.com/godot-rust/gdext/issues/410
173270

174271
#[derive(GodotClass)]

0 commit comments

Comments
 (0)