Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions godot-core/src/builtin/collections/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,22 @@ use sys::{ffi_methods, interface_fn, GodotFfi};
/// compiler will enforce this as long as you use only Rust threads, but it cannot protect against
/// concurrent modification on other threads (e.g. created through GDScript).
///
/// # Element type safety
///
/// We provide a richer set of element types than Godot, for convenience and stronger invariants in your _Rust_ code.
/// This, however, means that the Godot representation of such arrays is not capable of incorporating the additional "Rust-side" information.
/// This can lead to situations where GDScript code or the editor UI can insert values that do not fulfill the Rust-side invariants.
/// The library offers some best-effort protection in Debug mode, but certain errors may only occur on element access, in the form of panics.
///
/// Concretely, the following types lose type information when passed to Godot. If you want 100% bullet-proof arrays, avoid those.
/// - Non-`i64` integers: `i8`, `i16`, `i32`, `u8`, `u16`, `u32`. (`u64` is unsupported).
/// - Non-`f64` floats: `f32`.
/// - Non-null objects: [`Gd<T>`][crate::obj::Gd].
/// Godot generally allows `null` in arrays due to default-constructability, e.g. when using `resize()`.
/// The Godot-faithful (but less convenient) alternative is to use `Option<Gd<T>>` element types.
/// - Objects with dyn-trait association: [`DynGd<T, D>`][crate::obj::DynGd].
/// Godot doesn't know Rust traits and will only see the `T` part.
///
/// # Godot docs
///
/// [`Array[T]` (stable)](https://docs.godotengine.org/en/stable/classes/class_array.html)
Expand Down
18 changes: 16 additions & 2 deletions godot-core/src/classes/class_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

//! Runtime checks and inspection of Godot classes.

use crate::builtin::GString;
use crate::builtin::{GString, StringName};
use crate::classes::{ClassDb, Object};
use crate::meta::{CallContext, ClassName};
use crate::obj::{bounds, Bounds, Gd, GodotClass, InstanceId};
Expand All @@ -19,13 +19,27 @@ pub(crate) fn debug_string<T: GodotClass>(
ty: &str,
) -> std::fmt::Result {
if let Some(id) = obj.instance_id_or_none() {
let class: GString = obj.raw.as_object().get_class();
let class: StringName = obj.dynamic_class_string();
write!(f, "{ty} {{ id: {id}, class: {class} }}")
} else {
write!(f, "{ty} {{ freed obj }}")
}
}

pub(crate) fn debug_string_with_trait<T: GodotClass>(
obj: &Gd<T>,
f: &mut std::fmt::Formatter<'_>,
ty: &str,
trt: &str,
) -> std::fmt::Result {
if let Some(id) = obj.instance_id_or_none() {
let class: StringName = obj.dynamic_class_string();
write!(f, "{ty} {{ id: {id}, class: {class}, trait: {trt} }}")
} else {
write!(f, "{ty} {{ freed obj }}")
}
}

pub(crate) fn display_string<T: GodotClass>(
obj: &Gd<T>,
f: &mut std::fmt::Formatter<'_>,
Expand Down
5 changes: 2 additions & 3 deletions godot-core/src/meta/error/call_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,8 @@ impl CallError {
expected: VariantType,
) -> Self {
// Note: reason is same wording as in FromVariantError::description().
let reason = format!(
"parameter #{param_index} conversion -- expected type {expected:?}, got {actual:?}"
);
let reason =
format!("parameter #{param_index} -- cannot convert from {actual:?} to {expected:?}");

Self::new(call_ctx, reason, None)
}
Expand Down
28 changes: 26 additions & 2 deletions godot-core/src/meta/error/convert_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,15 @@ pub(crate) enum FromGodotError {
/// InvalidEnum is also used by bitfields.
InvalidEnum,

/// Cannot map object to `dyn Trait` because none of the known concrete classes implements it.
UnimplementedDynTrait {
trait_name: String,
class_name: String,
},

/// Cannot map object to `dyn Trait` because none of the known concrete classes implements it.
UnregisteredDynTrait { trait_name: String },

/// `InstanceId` cannot be 0.
ZeroInstanceId,
}
Expand Down Expand Up @@ -235,6 +244,21 @@ impl fmt::Display for FromGodotError {
}
Self::InvalidEnum => write!(f, "invalid engine enum value"),
Self::ZeroInstanceId => write!(f, "`InstanceId` cannot be 0"),
Self::UnimplementedDynTrait {
trait_name,
class_name,
} => {
write!(
f,
"none of the classes derived from `{class_name}` have been linked to trait `{trait_name}` with #[godot_dyn]"
)
}
FromGodotError::UnregisteredDynTrait { trait_name } => {
write!(
f,
"trait `{trait_name}` has not been registered with #[godot_dyn]"
)
}
}
}
}
Expand Down Expand Up @@ -313,11 +337,11 @@ impl fmt::Display for FromVariantError {
match self {
Self::BadType { expected, actual } => {
// Note: wording is the same as in CallError::failed_param_conversion_engine()
write!(f, "expected type {expected:?}, got {actual:?}")
write!(f, "cannot convert from {actual:?} to {expected:?}")
}
Self::BadValue => write!(f, "value cannot be represented in target type's domain"),
Self::WrongClass { expected } => {
write!(f, "expected class {expected}")
write!(f, "cannot convert to class {expected}")
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion godot-core/src/meta/sealed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use crate::builtin::*;
use crate::meta;
use crate::meta::traits::{ArrayElement, GodotNullableFfi, GodotType};
use crate::obj::{Gd, GodotClass, RawGd};
use crate::obj::{DynGd, Gd, GodotClass, RawGd};

pub trait Sealed {}
impl Sealed for Aabb {}
Expand Down Expand Up @@ -64,6 +64,7 @@ impl Sealed for Variant {}
impl<T: ArrayElement> Sealed for Array<T> {}
impl<T: GodotClass> Sealed for Gd<T> {}
impl<T: GodotClass> Sealed for RawGd<T> {}
impl<T: GodotClass, D: ?Sized> Sealed for DynGd<T, D> {}
impl<T: GodotClass> Sealed for meta::ObjectArg<T> {}
impl<T> Sealed for Option<T>
where
Expand Down
Loading
Loading