Skip to content

Commit 7334fa1

Browse files
committed
Add higher-level CallErrorType for script instance APIs
1 parent c4051c0 commit 7334fa1

File tree

4 files changed

+88
-9
lines changed

4 files changed

+88
-9
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright (c) godot-rust; Bromeon and contributors.
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
use crate::sys;
9+
10+
/// Enum representing different errors during script instance calls.
11+
///
12+
/// Provides a type-safe way to handle call errors when implementing [`ScriptInstance::call()`](crate::obj::script::ScriptInstance::call).
13+
/// It maps to the underlying `GDExtensionCallErrorType` constants from Godot's C API.
14+
///
15+
/// Note that the `OK` variant is not included here, as it represents a successful call. This type is meant to be used in
16+
/// `Result<T, CallErrorType>`, where `Ok(T)` indicates success.
17+
///
18+
/// See [`SiMut::base_mut()`][crate::obj::script::SiMut::base_mut] for an example.
19+
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
20+
#[repr(C)]
21+
#[non_exhaustive]
22+
pub enum CallErrorType {
23+
/// The method is invalid or was not found.
24+
InvalidMethod = sys::GDEXTENSION_CALL_ERROR_INVALID_METHOD as isize,
25+
26+
/// One or more arguments cannot be converted to the expected parameter types.
27+
InvalidArgument = sys::GDEXTENSION_CALL_ERROR_INVALID_ARGUMENT as isize,
28+
29+
/// Too many arguments were provided to the method.
30+
TooManyArguments = sys::GDEXTENSION_CALL_ERROR_TOO_MANY_ARGUMENTS as isize,
31+
32+
/// Too few arguments were provided to the method.
33+
TooFewArguments = sys::GDEXTENSION_CALL_ERROR_TOO_FEW_ARGUMENTS as isize,
34+
35+
/// The instance is null.
36+
InstanceIsNull = sys::GDEXTENSION_CALL_ERROR_INSTANCE_IS_NULL as isize,
37+
38+
/// The method is not const, but was called on a const instance.
39+
MethodNotConst = sys::GDEXTENSION_CALL_ERROR_METHOD_NOT_CONST as isize,
40+
}
41+
42+
impl CallErrorType {
43+
/// Converts the enum variant to its underlying `GDExtensionCallErrorType` value.
44+
// Used to be result_to_sys(), but not really simplifying things at use site.
45+
#[doc(hidden)]
46+
pub const fn to_sys(self) -> sys::GDExtensionCallErrorType {
47+
self as sys::GDExtensionCallErrorType
48+
}
49+
50+
/// Creates a `CallErrorType` from a `GDExtensionCallErrorType` value.
51+
///
52+
/// Returns `None` if the value doesn't correspond to a known error type.
53+
///
54+
/// # Panics (Debug)
55+
/// If the input doesn't match any known error type.
56+
#[doc(hidden)]
57+
pub fn result_from_sys(value: sys::GDExtensionCallErrorType) -> Result<(), Self> {
58+
match value {
59+
sys::GDEXTENSION_CALL_OK => Ok(()),
60+
sys::GDEXTENSION_CALL_ERROR_INVALID_METHOD => Err(Self::InvalidMethod),
61+
sys::GDEXTENSION_CALL_ERROR_INVALID_ARGUMENT => Err(Self::InvalidArgument),
62+
sys::GDEXTENSION_CALL_ERROR_TOO_MANY_ARGUMENTS => Err(Self::TooManyArguments),
63+
sys::GDEXTENSION_CALL_ERROR_TOO_FEW_ARGUMENTS => Err(Self::TooFewArguments),
64+
sys::GDEXTENSION_CALL_ERROR_INSTANCE_IS_NULL => Err(Self::InstanceIsNull),
65+
sys::GDEXTENSION_CALL_ERROR_METHOD_NOT_CONST => Err(Self::MethodNotConst),
66+
_ => {
67+
debug_assert!(false, "Unknown GDExtensionCallErrorType value: {value}");
68+
Err(Self::InvalidMethod) // in Release builds, return known error.
69+
}
70+
}
71+
}
72+
}

godot-core/src/meta/error/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
//! Errors in the gdext library.
99
1010
mod call_error;
11+
mod call_error_type;
1112
mod convert_error;
1213
mod io_error;
1314
mod string_error;
1415

1516
pub use call_error::*;
17+
pub use call_error_type::*;
1618
pub use convert_error::*;
1719
pub use io_error::*;
1820
pub use string_error::*;

godot-core/src/obj/script.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use godot_cell::panicking::{GdCell, MutGuard, RefGuard};
2222

2323
use crate::builtin::{GString, StringName, Variant, VariantType};
2424
use crate::classes::{Object, Script, ScriptLanguage};
25+
use crate::meta::error::CallErrorType;
2526
use crate::meta::{MethodInfo, PropertyInfo};
2627
use crate::obj::{Base, Gd, GodotClass};
2728
use crate::sys;
@@ -113,12 +114,11 @@ pub trait ScriptInstance: Sized {
113114
/// mutable method calls like rust.
114115
///
115116
/// It's important that the script does not cause a second call to this function while executing a method call. This would result in a panic.
116-
// TODO: map the sys::GDExtensionCallErrorType to some public API type.
117117
fn call(
118118
this: SiMut<Self>,
119119
method: StringName,
120120
args: &[&Variant],
121-
) -> Result<Variant, sys::GDExtensionCallErrorType>;
121+
) -> Result<Variant, CallErrorType>;
122122

123123
/// Identifies the script instance as a placeholder, routing property writes to a fallback if applicable.
124124
///
@@ -400,7 +400,9 @@ impl<'a, T: ScriptInstance> SiMut<'a, T> {
400400
/// # use godot::classes::{ScriptLanguage, Script};
401401
/// # use godot::obj::script::{ScriptInstance, SiMut};
402402
/// # use godot::meta::{MethodInfo, PropertyInfo};
403+
/// # use godot::meta::error::CallErrorType;
403404
/// # use godot::sys;
405+
///
404406
/// struct ExampleScriptInstance;
405407
///
406408
/// impl ScriptInstance for ExampleScriptInstance {
@@ -410,7 +412,7 @@ impl<'a, T: ScriptInstance> SiMut<'a, T> {
410412
/// this: SiMut<Self>,
411413
/// method: StringName,
412414
/// args: &[&Variant],
413-
/// ) -> Result<Variant, sys::GDExtensionCallErrorType>{
415+
/// ) -> Result<Variant, CallErrorType>{
414416
/// let name = this.base().get_name();
415417
/// godot_print!("name is {name}");
416418
/// // However, we cannot call methods that require `&mut Base`, such as:
@@ -456,7 +458,9 @@ impl<'a, T: ScriptInstance> SiMut<'a, T> {
456458
/// # use godot::classes::{ScriptLanguage, Script};
457459
/// # use godot::obj::script::{ScriptInstance, SiMut};
458460
/// # use godot::meta::{MethodInfo, PropertyInfo};
461+
/// # use godot::meta::error::CallErrorType;
459462
/// # use godot::sys;
463+
///
460464
/// struct ExampleScriptInstance;
461465
///
462466
/// impl ScriptInstance for ExampleScriptInstance {
@@ -466,7 +470,7 @@ impl<'a, T: ScriptInstance> SiMut<'a, T> {
466470
/// mut this: SiMut<Self>,
467471
/// method: StringName,
468472
/// args: &[&Variant],
469-
/// ) -> Result<Variant, sys::GDExtensionCallErrorType> {
473+
/// ) -> Result<Variant, CallErrorType> {
470474
/// // Check whether method is available on this script
471475
/// if method == StringName::from("script_method") {
472476
/// godot_print!("script_method called!");
@@ -817,7 +821,8 @@ mod script_instance_info {
817821
) {
818822
// SAFETY: `p_method` is a valid [`StringName`] pointer.
819823
let method = unsafe { StringName::new_from_string_sys(p_method) };
820-
// SAFETY: `p_args` is a valid array of length `p_argument_count`
824+
825+
// SAFETY: `p_args` is a valid array of length `p_argument_count`.
821826
let args = unsafe {
822827
Variant::borrow_ref_slice(
823828
p_args,
@@ -845,7 +850,7 @@ mod script_instance_info {
845850
sys::GDEXTENSION_CALL_OK
846851
}
847852

848-
Ok(Err(err)) => err,
853+
Ok(Err(err)) => err.to_sys(),
849854

850855
Err(_) => sys::GDEXTENSION_CALL_ERROR_INVALID_METHOD,
851856
};

itest/rust/src/builtin_tests/script/script_instance_tests.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ use godot::classes::{
1313
ScriptLanguageExtension,
1414
};
1515
use godot::global::{Error, MethodFlags};
16+
use godot::meta::error::CallErrorType;
1617
use godot::meta::{ClassId, FromGodot, MethodInfo, PropertyInfo, ToGodot};
1718
use godot::obj::script::{create_script_instance, ScriptInstance, SiMut};
1819
use godot::obj::{Base, Gd, NewAlloc, WithBaseField};
1920
use godot::register::{godot_api, GodotClass};
20-
use godot::sys;
2121

2222
use crate::framework::itest;
2323

@@ -166,7 +166,7 @@ impl ScriptInstance for TestScriptInstance {
166166
mut this: SiMut<Self>,
167167
method: StringName,
168168
args: &[&Variant],
169-
) -> Result<Variant, sys::GDExtensionCallErrorType> {
169+
) -> Result<Variant, CallErrorType> {
170170
match method.to_string().as_str() {
171171
"script_method_a" => {
172172
let arg_a = args[0].to::<GString>();
@@ -190,7 +190,7 @@ impl ScriptInstance for TestScriptInstance {
190190

191191
other => {
192192
println!("CALL: {other} with args: {args:?}");
193-
Err(sys::GDEXTENSION_CALL_ERROR_INVALID_METHOD)
193+
Err(CallErrorType::InvalidMethod)
194194
}
195195
}
196196
}

0 commit comments

Comments
 (0)