Skip to content

Commit 97259b6

Browse files
authored
Merge pull request #1332 from godot-rust/qol/callable-no-result
`Callable::from_local_fn()` now returns `R: ToGodot`
2 parents fd16af5 + 728fc6f commit 97259b6

File tree

15 files changed

+92
-74
lines changed

15 files changed

+92
-74
lines changed

godot-core/src/builtin/callable.rs

Lines changed: 55 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ impl Callable {
7272
/// If the builtin type does not have the method, the returned callable will be invalid.
7373
///
7474
/// Static builtin methods (e.g. `String.humanize_size`) are not supported in reflection as of Godot 4.4. For static _class_ functions,
75-
/// use [`from_local_static()`][Self::from_local_static] instead.
75+
/// use [`from_class_static()`][Self::from_class_static] instead.
7676
///
7777
/// _Godot equivalent: `Callable.create(Variant variant, StringName method)`_
7878
#[cfg(since_api = "4.3")]
@@ -84,17 +84,15 @@ impl Callable {
8484
inner::InnerCallable::create(variant, method_name)
8585
}
8686

87-
/// Create a callable for the static method `class_name::function` (single-threaded).
87+
/// Create a callable for the static method `class_name::function`
8888
///
89-
/// Allows you to call static functions through `Callable`.
89+
/// Allows you to call static functions through `Callable`. Allows both single- and multi-threaded calls; what happens on Godot side
90+
/// is your responsibility.
9091
///
91-
/// Does not support built-in types (such as `String`), only classes.
92-
///
93-
/// # Compatibility
94-
/// Not available before Godot 4.4. Library versions <0.3 used to provide this, however the polyfill used to emulate it was half-broken
95-
/// (not supporting signals, bind(), method_name(), is_valid(), etc).
92+
/// Does not support built-in types (such as `String`), only classes. Static functions on built-in types are not supported in Godot's
93+
/// reflection APIs at the moment.
9694
#[cfg(since_api = "4.4")]
97-
pub fn from_local_static(
95+
pub fn from_class_static(
9896
class_name: impl meta::AsArg<StringName>,
9997
function_name: impl meta::AsArg<StringName>,
10098
) -> Self {
@@ -103,16 +101,33 @@ impl Callable {
103101

104102
let callable_name = format!("{class_name}.{function_name}");
105103

106-
Self::from_local_fn(&callable_name, move |args| {
104+
let function = move |args: &[&Variant]| {
107105
let args = args.iter().cloned().cloned().collect::<Vec<_>>();
108106

109107
let result: Variant = classes::ClassDb::singleton().class_call_static(
110108
&class_name,
111109
&function_name,
112110
args.as_slice(),
113111
);
114-
Ok(result)
115-
})
112+
result
113+
};
114+
115+
#[cfg(feature = "experimental-threads")]
116+
let callable = Self::from_sync_fn(&callable_name, function);
117+
118+
#[cfg(not(feature = "experimental-threads"))]
119+
let callable = Self::from_local_fn(&callable_name, function);
120+
121+
callable
122+
}
123+
124+
#[deprecated = "Renamed to `from_class_static`."]
125+
#[cfg(since_api = "4.4")]
126+
pub fn from_local_static(
127+
class_name: impl meta::AsArg<StringName>,
128+
function_name: impl meta::AsArg<StringName>,
129+
) -> Self {
130+
Self::from_class_static(class_name, function_name)
116131
}
117132

118133
fn default_callable_custom_info() -> CallableCustomInfo {
@@ -139,14 +154,15 @@ impl Callable {
139154
///
140155
/// This constructor only allows the callable to be invoked from the same thread as creating it. If you need to invoke it from any thread,
141156
/// use [`from_sync_fn`][Self::from_sync_fn] instead (requires crate feature `experimental-threads`; only enable if really needed).
142-
pub fn from_local_fn<F, S>(name: S, rust_function: F) -> Self
157+
pub fn from_local_fn<R, F, S>(name: S, rust_function: F) -> Self
143158
where
144-
F: 'static + FnMut(&[&Variant]) -> Result<Variant, ()>,
159+
R: ToGodot,
160+
F: 'static + FnMut(&[&Variant]) -> R,
145161
S: meta::AsArg<GString>,
146162
{
147163
meta::arg_into_owned!(name);
148164

149-
Self::from_fn_wrapper(FnWrapper {
165+
Self::from_fn_wrapper::<F, R>(FnWrapper {
150166
rust_function,
151167
name,
152168
thread_id: Some(std::thread::current().id()),
@@ -165,12 +181,12 @@ impl Callable {
165181
pub fn from_linked_fn<F, T, S>(name: S, linked_object: &Gd<T>, rust_function: F) -> Self
166182
where
167183
T: GodotClass,
168-
F: 'static + FnMut(&[&Variant]) -> Result<Variant, ()>,
184+
F: 'static + FnMut(&[&Variant]) -> Variant,
169185
S: meta::AsArg<GString>,
170186
{
171187
meta::arg_into_owned!(name);
172188

173-
Self::from_fn_wrapper(FnWrapper {
189+
Self::from_fn_wrapper::<F, Variant>(FnWrapper {
174190
rust_function,
175191
name,
176192
thread_id: Some(std::thread::current().id()),
@@ -184,9 +200,10 @@ impl Callable {
184200
///
185201
/// After the first invocation, subsequent calls will panic with a message indicating the callable has already been consumed. This is
186202
/// useful for deferred operations that should only execute once. For repeated execution, use [`from_local_fn()][Self::from_local_fn].
187-
pub(crate) fn from_once_fn<F, S>(name: S, rust_function: F) -> Self
203+
pub(crate) fn from_once_fn<R, F, S>(name: S, rust_function: F) -> Self
188204
where
189-
F: 'static + FnOnce(&[&Variant]) -> Result<Variant, ()>,
205+
R: ToGodot,
206+
F: 'static + FnOnce(&[&Variant]) -> R,
190207
S: meta::AsArg<GString>,
191208
{
192209
meta::arg_into_owned!(name);
@@ -196,6 +213,7 @@ impl Callable {
196213
let rust_fn_once = rust_fn_once
197214
.take()
198215
.expect("callable created with from_once_fn() has already been consumed");
216+
199217
rust_fn_once(args)
200218
})
201219
}
@@ -204,7 +222,7 @@ impl Callable {
204222
#[doc(hidden)]
205223
pub fn __once_fn<F, S>(name: S, rust_function: F) -> Self
206224
where
207-
F: 'static + FnOnce(&[&Variant]) -> Result<Variant, ()>,
225+
F: 'static + FnOnce(&[&Variant]) -> Variant,
208226
S: meta::AsArg<GString>,
209227
{
210228
Self::from_once_fn(name, rust_function)
@@ -213,12 +231,12 @@ impl Callable {
213231
pub(crate) fn with_scoped_fn<S, F, Fc, R>(name: S, rust_function: F, callable_usage: Fc) -> R
214232
where
215233
S: meta::AsArg<GString>,
216-
F: FnMut(&[&Variant]) -> Result<Variant, ()>,
234+
F: FnMut(&[&Variant]) -> Variant,
217235
Fc: FnOnce(&Callable) -> R,
218236
{
219237
meta::arg_into_owned!(name);
220238

221-
let callable = Self::from_fn_wrapper(FnWrapper {
239+
let callable = Self::from_fn_wrapper::<F, Variant>(FnWrapper {
222240
rust_function,
223241
name,
224242
thread_id: Some(std::thread::current().id()),
@@ -248,14 +266,15 @@ impl Callable {
248266
/// });
249267
/// ```
250268
#[cfg(feature = "experimental-threads")]
251-
pub fn from_sync_fn<F, S>(name: S, rust_function: F) -> Self
269+
pub fn from_sync_fn<R, F, S>(name: S, rust_function: F) -> Self
252270
where
253-
F: 'static + Send + Sync + FnMut(&[&Variant]) -> Result<Variant, ()>,
271+
R: ToGodot,
272+
F: 'static + Send + Sync + FnMut(&[&Variant]) -> R,
254273
S: meta::AsArg<GString>,
255274
{
256275
meta::arg_into_owned!(name);
257276

258-
Self::from_fn_wrapper(FnWrapper {
277+
Self::from_fn_wrapper::<F, R>(FnWrapper {
259278
rust_function,
260279
name,
261280
thread_id: None,
@@ -287,9 +306,10 @@ impl Callable {
287306
Self::from_custom_info(info)
288307
}
289308

290-
fn from_fn_wrapper<F>(inner: FnWrapper<F>) -> Self
309+
fn from_fn_wrapper<F, R>(inner: FnWrapper<F>) -> Self
291310
where
292-
F: FnMut(&[&Variant]) -> Result<Variant, ()>,
311+
F: FnMut(&[&Variant]) -> R,
312+
R: ToGodot,
293313
{
294314
let object_id = inner.linked_object_id();
295315

@@ -298,7 +318,7 @@ impl Callable {
298318
let info = CallableCustomInfo {
299319
object_id,
300320
callable_userdata: Box::into_raw(Box::new(userdata)) as *mut std::ffi::c_void,
301-
call_func: Some(rust_callable_call_fn::<F>),
321+
call_func: Some(rust_callable_call_fn::<F, R>),
302322
free_func: Some(rust_callable_destroy::<FnWrapper<F>>),
303323
to_string_func: Some(rust_callable_to_string_named::<F>),
304324
is_valid_func: Some(rust_callable_is_valid),
@@ -593,7 +613,7 @@ mod custom_callable {
593613
/// Return `Ok(...)` if the call succeeded, and `Err(())` otherwise.
594614
/// Error handling is mostly needed in case argument number or types mismatch.
595615
#[allow(clippy::result_unit_err)] // TODO remove once there's a clear error type here.
596-
fn invoke(&mut self, args: &[&Variant]) -> Result<Variant, ()>;
616+
fn invoke(&mut self, args: &[&Variant]) -> Variant;
597617

598618
// TODO(v0.3): add object_id().
599619

@@ -626,19 +646,20 @@ mod custom_callable {
626646
// Get the RustCallable again inside closure so it doesn't have to be UnwindSafe.
627647
let c: &mut C = CallableUserdata::inner_from_raw(callable_userdata);
628648
let result = c.invoke(arg_refs);
629-
meta::varcall_return_checked(result, r_return, r_error);
649+
meta::varcall_return_checked(Ok(result), r_return, r_error);
630650
Ok(())
631651
});
632652
}
633653

634-
pub unsafe extern "C" fn rust_callable_call_fn<F>(
654+
pub unsafe extern "C" fn rust_callable_call_fn<F, R>(
635655
callable_userdata: *mut std::ffi::c_void,
636656
p_args: *const sys::GDExtensionConstVariantPtr,
637657
p_argument_count: sys::GDExtensionInt,
638658
r_return: sys::GDExtensionVariantPtr,
639659
r_error: *mut sys::GDExtensionCallError,
640660
) where
641-
F: FnMut(&[&Variant]) -> Result<Variant, ()>,
661+
F: FnMut(&[&Variant]) -> R,
662+
R: ToGodot,
642663
{
643664
let arg_refs: &[&Variant] = Variant::borrow_ref_slice(p_args, p_argument_count as usize);
644665

@@ -664,8 +685,8 @@ mod custom_callable {
664685
);
665686
}
666687

667-
let result = (w.rust_function)(arg_refs);
668-
meta::varcall_return_checked(result, r_return, r_error);
688+
let result = (w.rust_function)(arg_refs).to_variant();
689+
meta::varcall_return_checked(Ok(result), r_return, r_error);
669690
Ok(())
670691
});
671692
}

godot-core/src/builtin/collections/array.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,7 @@ impl<T: ArrayElement> Array<T> {
710710
let value = T::from_variant(args[0]);
711711
let is_less = matches!(func(&value), cmp::Ordering::Less);
712712

713-
Ok(is_less.to_variant())
713+
is_less.to_variant()
714714
};
715715

716716
let debug_name = std::any::type_name::<F>();
@@ -787,7 +787,7 @@ impl<T: ArrayElement> Array<T> {
787787
let rhs = T::from_variant(args[1]);
788788
let is_less = matches!(func(&lhs, &rhs), cmp::Ordering::Less);
789789

790-
Ok(is_less.to_variant())
790+
is_less.to_variant()
791791
};
792792

793793
let debug_name = std::any::type_name::<F>();

godot-core/src/obj/base.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
1414
use std::mem::ManuallyDrop;
1515
use std::rc::Rc;
1616

17-
use crate::builtin::{Callable, Variant};
17+
use crate::builtin::Callable;
1818
use crate::obj::{bounds, Gd, GodotClass, InstanceId, PassiveGd};
1919
use crate::{classes, sys};
2020

@@ -226,7 +226,6 @@ impl<T: GodotClass> Base<T> {
226226
let name = format!("Base<{}> deferred unref", T::class_id());
227227
let callable = Callable::from_once_fn(&name, move |_args| {
228228
Self::drop_strong_ref(instance_id);
229-
Ok(Variant::nil())
230229
});
231230

232231
// Use Callable::call_deferred() instead of Gd::apply_deferred(). The latter implicitly borrows &mut self,

godot-core/src/obj/gd.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ impl<T: GodotClass> Gd<T> {
586586
/// If you need a Callable which can live indefinitely use [`Callable::from_local_fn()`].
587587
pub fn linked_callable<F>(&self, method_name: impl AsArg<GString>, rust_function: F) -> Callable
588588
where
589-
F: 'static + FnMut(&[&Variant]) -> Result<Variant, ()>,
589+
F: 'static + FnMut(&[&Variant]) -> Variant,
590590
{
591591
Callable::from_linked_fn(method_name, self, rust_function)
592592
}
@@ -711,7 +711,6 @@ impl<T: GodotClass> Gd<T> {
711711

712712
let callable = Callable::from_once_fn("run_deferred", move |_| {
713713
gd_function(obj);
714-
Ok(Variant::nil())
715714
});
716715
callable.call_deferred(&[]);
717716
}

godot-core/src/registry/signal/connect_builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ where
126126
/// type state builder for simple + common connections, thus hopefully being a tiny bit lighter on compile times.
127127
fn inner_connect_godot_fn<F>(
128128
self,
129-
godot_fn: impl FnMut(&[&Variant]) -> Result<Variant, ()> + 'static,
129+
godot_fn: impl FnMut(&[&Variant]) -> Variant + 'static,
130130
bound: &Gd<impl GodotClass>,
131131
) -> ConnectHandle {
132132
let callable_name = match &self.data.callable_name {

godot-core/src/registry/signal/mod.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,15 @@ pub mod priv_re_export {
3939
// ----------------------------------------------------------------------------------------------------------------------------------------------
4040

4141
// Used by both `TypedSignal` and `ConnectBuilder`.
42-
fn make_godot_fn<Ps, F>(mut input: F) -> impl FnMut(&[&Variant]) -> Result<Variant, ()>
42+
fn make_godot_fn<Ps, F>(mut input: F) -> impl FnMut(&[&Variant]) -> Variant
4343
where
4444
F: FnMut(Ps),
4545
Ps: meta::InParamTuple,
4646
{
47-
move |variant_args: &[&Variant]| -> Result<Variant, ()> {
47+
move |variant_args: &[&Variant]| {
4848
let args = Ps::from_variant_array(variant_args);
4949
input(args);
50-
51-
Ok(Variant::nil())
50+
Variant::nil()
5251
}
5352
}
5453

godot-core/src/registry/signal/typed_signal.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ impl<'c, C: WithSignals, Ps: meta::ParamTuple> TypedSignal<'c, C, Ps> {
149149
/// type state builder for simple + common connections, thus hopefully being a tiny bit lighter on compile times.
150150
fn inner_connect_godot_fn<F>(
151151
&self,
152-
godot_fn: impl FnMut(&[&Variant]) -> Result<Variant, ()> + 'static,
152+
godot_fn: impl FnMut(&[&Variant]) -> Variant + 'static,
153153
bound: &Gd<impl GodotClass>,
154154
) -> ConnectHandle {
155155
let callable_name = make_callable_name::<F>();

godot-core/src/task/async_runtime.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,7 @@ impl Wake for GodotWaker {
500500
/// This appears to be a common issue: https://github.com/rust-lang/rust/issues/89976
501501
fn callback_type_hint<F>(f: F) -> F
502502
where
503-
F: for<'a> FnMut(&'a [&Variant]) -> Result<Variant, ()>,
503+
F: for<'a> FnMut(&'a [&Variant]) -> Variant,
504504
{
505505
f
506506
}
@@ -515,7 +515,7 @@ impl Wake for GodotWaker {
515515
"GodotWaker::wake",
516516
callback_type_hint(move |_args| {
517517
poll_future(waker.take().expect("Callable will never be called again"));
518-
Ok(Variant::nil())
518+
Variant::nil()
519519
}),
520520
);
521521

godot-core/src/task/futures.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ impl<R: IntoDynamicSend> PartialEq for SignalFutureResolver<R> {
115115
}
116116

117117
impl<R: InParamTuple + IntoDynamicSend> RustCallable for SignalFutureResolver<R> {
118-
fn invoke(&mut self, args: &[&Variant]) -> Result<Variant, ()> {
118+
fn invoke(&mut self, args: &[&Variant]) -> Variant {
119119
let waker = {
120120
let mut data = self.data.lock().unwrap();
121121
data.state = SignalFutureState::Ready(R::from_variant_array(args).into_dynamic_send());
@@ -128,7 +128,7 @@ impl<R: InParamTuple + IntoDynamicSend> RustCallable for SignalFutureResolver<R>
128128
waker.wake();
129129
}
130130

131-
Ok(Variant::nil())
131+
Variant::nil()
132132
}
133133
}
134134

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -562,9 +562,9 @@ fn array_bsearch_custom() {
562562
}
563563

564564
fn backwards_sort_callable() -> Callable {
565-
Callable::from_local_fn("sort backwards", |args: &[&Variant]| {
566-
let res = args[0].to::<i32>() > args[1].to::<i32>();
567-
Ok(res.to_variant())
565+
// No &[&Variant] explicit type in arguments.
566+
Callable::from_local_fn("sort backwards", |args| {
567+
args[0].to::<i32>() > args[1].to::<i32>()
568568
})
569569
}
570570

0 commit comments

Comments
 (0)