Skip to content

Commit a60290c

Browse files
authored
Merge pull request #957 from godot-rust/qol/traits-and-tools
`sys::short_type_name`, conversions and relaxed `GodotType`
2 parents f93498c + c4cf5e1 commit a60290c

File tree

9 files changed

+175
-19
lines changed

9 files changed

+175
-19
lines changed

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ use crate::builtin::*;
1212
use crate::meta;
1313
use crate::meta::error::{ConvertError, FromGodotError, FromVariantError};
1414
use crate::meta::{
15-
ArrayElement, ArrayTypeInfo, AsArg, CowArg, FromGodot, GodotConvert, GodotFfiVariant,
16-
GodotType, ParamType, PropertyHintInfo, RefArg, ToGodot,
15+
element_godot_type_name, element_variant_type, ArrayElement, ArrayTypeInfo, AsArg, CowArg,
16+
FromGodot, GodotConvert, GodotFfiVariant, GodotType, ParamType, PropertyHintInfo, RefArg,
17+
ToGodot,
1718
};
1819
use crate::registry::property::{Export, Var};
1920
use godot_ffi as sys;
@@ -886,10 +887,16 @@ impl<T: ArrayElement> Array<T> {
886887
/// Used as `if` statement in trait impls. Avoids defining yet another trait or non-local overridden function just for this case;
887888
/// `Variant` is the only Godot type that has variant type NIL and can be used as an array element.
888889
fn has_variant_t() -> bool {
889-
T::Ffi::variant_type() == VariantType::NIL
890+
element_variant_type::<T>() == VariantType::NIL
890891
}
891892
}
892893

894+
#[test]
895+
fn correct_variant_t() {
896+
assert!(Array::<Variant>::has_variant_t());
897+
assert!(!Array::<i64>::has_variant_t());
898+
}
899+
893900
impl VariantArray {
894901
/// # Safety
895902
/// - Variant must have type `VariantType::ARRAY`.
@@ -1133,7 +1140,7 @@ impl<T: ArrayElement> GodotType for Array<T> {
11331140
// Typed arrays use type hint.
11341141
PropertyHintInfo {
11351142
hint: crate::global::PropertyHint::ARRAY_TYPE,
1136-
hint_string: T::godot_type_name().into(),
1143+
hint_string: GString::from(element_godot_type_name::<T>()),
11371144
}
11381145
}
11391146
}

godot-core/src/meta/args/as_arg.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*/
77

88
use crate::builtin::{GString, NodePath, StringName};
9-
use crate::meta::{CowArg, GodotType};
9+
use crate::meta::{sealed, CowArg};
1010
use std::ffi::CStr;
1111

1212
/// Implicit conversions for arguments passed to Godot APIs.
@@ -253,7 +253,9 @@ impl AsArg<NodePath> for &String {
253253
// ----------------------------------------------------------------------------------------------------------------------------------------------
254254

255255
/// Implemented for all parameter types `T` that are allowed to receive [impl `AsArg<T>`][AsArg].
256-
pub trait ParamType: GodotType
256+
// ParamType used to be a subtrait of GodotType, but this can be too restrictive. For example, DynGd is not a "Godot canonical type"
257+
// (GodotType), however it's still useful to store it in arrays -- which requires AsArg and subsequently ParamType.
258+
pub trait ParamType: sealed::Sealed + Sized + 'static
257259
// GodotType bound not required right now, but conceptually should always be the case.
258260
{
259261
/// Canonical argument passing type, either `T` or an internally-used CoW type.

godot-core/src/meta/array_type_info.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
*/
77

88
use crate::builtin::{StringName, VariantType};
9-
use crate::meta::traits::GodotFfi;
10-
use crate::meta::GodotType;
9+
use crate::meta::traits::element_variant_type;
10+
use crate::meta::{ArrayElement, GodotType};
1111
use std::fmt;
1212

1313
/// Represents the type information of a Godot array. See
@@ -26,8 +26,8 @@ pub(crate) struct ArrayTypeInfo {
2626
}
2727

2828
impl ArrayTypeInfo {
29-
pub fn of<T: GodotType>() -> Self {
30-
let variant_type = <T::Via as GodotType>::Ffi::variant_type();
29+
pub fn of<T: ArrayElement>() -> Self {
30+
let variant_type = element_variant_type::<T>();
3131
let class_name = if variant_type == VariantType::OBJECT {
3232
Some(T::Via::class_name().to_string_name())
3333
} else {

godot-core/src/meta/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ pub use godot_convert::{FromGodot, GodotConvert, ToGodot};
6161
pub use traits::{ArrayElement, GodotType, PackedArrayElement};
6262

6363
pub(crate) use array_type_info::ArrayTypeInfo;
64-
pub(crate) use traits::{GodotFfiVariant, GodotNullableFfi};
64+
pub(crate) use traits::{
65+
element_godot_type_name, element_variant_type, GodotFfiVariant, GodotNullableFfi,
66+
};
6567

6668
use crate::registry::method::MethodParamOrReturnInfo;
6769

godot-core/src/meta/property_info.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77

88
use crate::builtin::{GString, StringName};
99
use crate::global::{PropertyHint, PropertyUsageFlags};
10-
use crate::meta::{ArrayElement, ClassName, GodotType, PackedArrayElement};
10+
use crate::meta::{
11+
element_godot_type_name, ArrayElement, ClassName, GodotType, PackedArrayElement,
12+
};
1113
use crate::registry::property::{Export, Var};
1214
use crate::sys;
1315
use godot_ffi::VariantType;
@@ -234,7 +236,7 @@ impl PropertyHintInfo {
234236
pub fn var_array_element<T: ArrayElement>() -> Self {
235237
Self {
236238
hint: PropertyHint::ARRAY_TYPE,
237-
hint_string: GString::from(T::godot_type_name()),
239+
hint_string: GString::from(element_godot_type_name::<T>()),
238240
}
239241
}
240242

godot-core/src/meta/traits.rs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@
55
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
66
*/
77

8-
use godot_ffi as sys;
9-
10-
use crate::builtin::Variant;
8+
use crate::builtin::{Variant, VariantType};
119
use crate::global::PropertyUsageFlags;
1210
use crate::meta::error::ConvertError;
1311
use crate::meta::{
1412
sealed, ClassName, FromGodot, GodotConvert, PropertyHintInfo, PropertyInfo, ToGodot,
1513
};
1614
use crate::registry::method::MethodParamOrReturnInfo;
15+
use godot_ffi as sys;
1716

1817
// Re-export sys traits in this module, so all are in one place.
1918
use crate::registry::property::builtin_type_string;
@@ -162,7 +161,10 @@ pub trait GodotType: GodotConvert<Via = Self> + sealed::Sealed + Sized + 'static
162161
message = "`Array<T>` can only store element types supported in Godot arrays (no nesting).",
163162
label = "has invalid element type"
164163
)]
165-
pub trait ArrayElement: GodotType + ToGodot + FromGodot + sealed::Sealed + meta::ParamType {
164+
pub trait ArrayElement: ToGodot + FromGodot + sealed::Sealed + meta::ParamType {
165+
// Note: several indirections in ArrayElement and the global `element_*` functions go through `GodotConvert::Via`,
166+
// to not require Self: GodotType. What matters is how array elements map to Godot on the FFI level (GodotType trait).
167+
166168
/// Returns the representation of this type as a type string.
167169
///
168170
/// Used for elements in arrays (the latter despite `ArrayElement` not having a direct relation).
@@ -172,7 +174,7 @@ pub trait ArrayElement: GodotType + ToGodot + FromGodot + sealed::Sealed + meta:
172174
#[doc(hidden)]
173175
fn element_type_string() -> String {
174176
// Most array elements and all packed array elements are builtin types, so this is a good default.
175-
builtin_type_string::<Self>()
177+
builtin_type_string::<Self::Via>()
176178
}
177179

178180
#[doc(hidden)]
@@ -182,6 +184,23 @@ pub trait ArrayElement: GodotType + ToGodot + FromGodot + sealed::Sealed + meta:
182184
}
183185
}
184186

187+
// Non-polymorphic helper functions, to avoid constant `<T::Via as GodotType>::` in the code.
188+
189+
#[doc(hidden)]
190+
pub(crate) fn element_variant_type<T: ArrayElement>() -> VariantType {
191+
<T::Via as GodotType>::Ffi::variant_type()
192+
}
193+
194+
#[doc(hidden)]
195+
pub(crate) fn element_godot_type_name<T: ArrayElement>() -> String {
196+
<T::Via as GodotType>::godot_type_name()
197+
}
198+
199+
// #[doc(hidden)]
200+
// pub(crate) fn element_godot_type_name<T: ArrayElement>() -> String {
201+
// <T::Via as GodotType>::godot_type_name()
202+
// }
203+
185204
/// Marker trait to identify types that can be stored in `Packed*Array` types.
186205
#[diagnostic::on_unimplemented(
187206
message = "`Packed*Array` can only store element types supported in Godot packed arrays.",

godot-core/src/obj/gd.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,7 @@ impl<T: GodotClass> GodotConvert for Gd<T> {
692692
}
693693

694694
impl<T: GodotClass> ToGodot for Gd<T> {
695+
// TODO return RefArg here?
695696
type ToVia<'v> = Gd<T>;
696697

697698
fn to_godot(&self) -> Self::ToVia<'_> {

godot-ffi/src/conv.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,23 @@ pub fn u32_to_usize(i: u32) -> usize {
2525
unsafe { i.try_into().unwrap_unchecked() }
2626
}
2727

28-
/// Converts a rust-bool into a sys-bool.
28+
/// Converts a Rust-bool into a sys-bool.
2929
pub const fn bool_to_sys(value: bool) -> sys::GDExtensionBool {
3030
value as sys::GDExtensionBool
3131
}
3232

33+
/// Converts a sys-bool to Rust-bool.
34+
///
35+
/// # Panics
36+
/// If the value is not a valid sys-bool (0 or 1).
37+
pub fn bool_from_sys(value: sys::GDExtensionBool) -> bool {
38+
match value {
39+
SYS_TRUE => true,
40+
SYS_FALSE => false,
41+
_ => panic!("Invalid GDExtensionBool value: {}", value),
42+
}
43+
}
44+
3345
/// Convert a list into a pointer + length pair. Should be used together with [`ptr_list_from_sys`].
3446
///
3547
/// If `list_from_sys` is not called on this list then that will cause a memory leak.

godot-ffi/src/toolbox.rs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,67 @@ pub fn unqualified_type_name<T>() -> &'static str {
187187
}
188188
*/
189189

190+
/// Like [`std::any::type_name`], but returns a short type name without module paths.
191+
pub fn short_type_name<T>() -> String {
192+
let full_name = std::any::type_name::<T>();
193+
strip_module_paths(full_name)
194+
}
195+
196+
/// Like [`std::any::type_name_of_val`], but returns a short type name without module paths.
197+
pub fn short_type_name_of_val<T>(val: &T) -> String {
198+
let full_name = std::any::type_name_of_val(val);
199+
strip_module_paths(full_name)
200+
}
201+
202+
/// Helper function to strip module paths from a fully qualified type name.
203+
fn strip_module_paths(full_name: &str) -> String {
204+
let mut result = String::new();
205+
let mut identifier = String::new();
206+
207+
let mut chars = full_name.chars().peekable();
208+
209+
while let Some(c) = chars.next() {
210+
match c {
211+
'<' | '>' | ',' | ' ' | '&' | '(' | ')' | '[' | ']' => {
212+
// Process the current identifier.
213+
if !identifier.is_empty() {
214+
let short_name = identifier.split("::").last().unwrap_or(&identifier);
215+
result.push_str(short_name);
216+
identifier.clear();
217+
}
218+
result.push(c);
219+
220+
// Handle spaces after commas for readability.
221+
if c == ',' && chars.peek().map_or(false, |&next_c| next_c != ' ') {
222+
result.push(' ');
223+
}
224+
}
225+
':' => {
226+
// Check for '::' indicating module path separator.
227+
if chars.peek() == Some(&':') {
228+
// Skip the second ':'
229+
chars.next();
230+
identifier.push_str("::");
231+
} else {
232+
identifier.push(c);
233+
}
234+
}
235+
_ => {
236+
// Part of an identifier.
237+
identifier.push(c);
238+
}
239+
}
240+
}
241+
242+
// Process any remaining identifier.
243+
if !identifier.is_empty() {
244+
let short_name = identifier.split("::").last().unwrap_or(&identifier);
245+
result.push_str(short_name);
246+
}
247+
248+
result
249+
}
250+
190251
// ----------------------------------------------------------------------------------------------------------------------------------------------
191252
// Private helpers
192253

@@ -457,3 +518,53 @@ mod manual_init_cell {
457518
}
458519

459520
pub(crate) use manual_init_cell::ManualInitCell;
521+
522+
// ----------------------------------------------------------------------------------------------------------------------------------------------
523+
// Unit tests
524+
525+
#[cfg(test)]
526+
mod tests {
527+
use super::*;
528+
529+
#[test]
530+
fn test_short_type_name() {
531+
assert_eq!(short_type_name::<i32>(), "i32");
532+
assert_eq!(short_type_name::<Option<i32>>(), "Option<i32>");
533+
assert_eq!(
534+
short_type_name::<Result<Option<i32>, String>>(),
535+
"Result<Option<i32>, String>"
536+
);
537+
assert_eq!(
538+
short_type_name::<Vec<Result<Option<i32>, String>>>(),
539+
"Vec<Result<Option<i32>, String>>"
540+
);
541+
assert_eq!(
542+
short_type_name::<std::collections::HashMap<String, Vec<i32>>>(),
543+
"HashMap<String, Vec<i32>>"
544+
);
545+
assert_eq!(
546+
short_type_name::<Result<Option<i32>, String>>(),
547+
"Result<Option<i32>, String>"
548+
);
549+
assert_eq!(short_type_name::<i32>(), "i32");
550+
assert_eq!(short_type_name::<Vec<String>>(), "Vec<String>");
551+
}
552+
553+
#[test]
554+
fn test_short_type_name_of_val() {
555+
let value = Some(42);
556+
assert_eq!(short_type_name_of_val(&value), "Option<i32>");
557+
558+
let result: Result<_, String> = Ok(Some(42));
559+
assert_eq!(
560+
short_type_name_of_val(&result),
561+
"Result<Option<i32>, String>"
562+
);
563+
564+
let vec = vec![result];
565+
assert_eq!(
566+
short_type_name_of_val(&vec),
567+
"Vec<Result<Option<i32>, String>>"
568+
);
569+
}
570+
}

0 commit comments

Comments
 (0)