From f218c3117542bf0b0c98900836d571fbc4e9ac14 Mon Sep 17 00:00:00 2001 From: Chitose Yuuzaki Date: Sun, 14 Nov 2021 12:12:20 +0000 Subject: [PATCH 1/2] Re-organize Variant conversion methods Inherent conversion methods are now genericized into `new`, `to`, `try_to`, and `coerce_to`, to reduce the clutter and make other `Variant` methods more discoverable in docs. - `new` replaces the `from_*` methods. The original version that returns nil variants is renamed to `Variant::nil`, to better reflect its behavior. - `to` and `try_to` replaces the `try_to_*` methods, with naming more consistent with the rest of the Rust ecosystem. - `to_object` and `try_to_object` are kept for convenience around `Ref`. - `coerce_to` replaces the `to_*` methods. A new trait `CoerceFromVariant` is introduced and implemented for all GDScript built-in types. - Documentation is updated around convertion methods/traits to highlight what is used for exported methods. Close #774 --- bindings_generator/src/methods.rs | 10 - examples/rpc/src/client.rs | 2 +- examples/rpc/src/server.rs | 2 +- examples/scene_create/src/lib.rs | 10 +- examples/signals/src/lib.rs | 6 +- gdnative-async/src/method.rs | 6 +- gdnative-async/src/rt/bridge.rs | 2 +- gdnative-async/src/rt/func_state.rs | 2 +- gdnative-core/src/core_types/dictionary.rs | 20 +- gdnative-core/src/core_types/string.rs | 4 +- gdnative-core/src/core_types/variant.rs | 693 ++++++------------ gdnative-core/src/core_types/variant_array.rs | 34 +- gdnative-core/src/export/class_builder.rs | 2 +- gdnative-core/src/export/macros.rs | 2 +- gdnative-core/src/export/method.rs | 10 +- gdnative-core/src/export/property/accessor.rs | 8 +- .../src/export/property/invalid_accessor.rs | 2 +- gdnative-core/src/object/instance.rs | 2 +- test/src/lib.rs | 8 +- test/src/test_derive.rs | 28 +- test/src/test_free_ub.rs | 4 +- test/src/test_register.rs | 20 +- test/src/test_variant_call_args.rs | 19 +- 23 files changed, 307 insertions(+), 589 deletions(-) diff --git a/bindings_generator/src/methods.rs b/bindings_generator/src/methods.rs index 345396427..d00c40432 100644 --- a/bindings_generator/src/methods.rs +++ b/bindings_generator/src/methods.rs @@ -628,11 +628,6 @@ mod varargs_call { let #name: Variant = Variant::from_object_ptr(#name); } } - Ty::String => { - quote! { - let #name: Variant = Variant::from_godot_string(&#name); - } - } _ => { quote! { let #name: Variant = (&#name).to_variant(); @@ -706,11 +701,6 @@ mod varcall { let #name: Variant = Variant::from_object_ptr(#name); } } - Ty::String => { - quote! { - let #name: Variant = Variant::from_godot_string(&#name); - } - } _ => { quote! { let #name: Variant = (&#name).to_variant(); diff --git a/examples/rpc/src/client.rs b/examples/rpc/src/client.rs index 5fa5b1902..dbd53e66d 100644 --- a/examples/rpc/src/client.rs +++ b/examples/rpc/src/client.rs @@ -46,7 +46,7 @@ impl ServerPuppet { #[export] fn on_connected_to_server(&mut self, owner: TRef) { - owner.rpc("greet_server", &[Variant::from_str("hello")]); + owner.rpc("greet_server", &[Variant::new("hello")]); } #[export(rpc = "puppet")] diff --git a/examples/rpc/src/server.rs b/examples/rpc/src/server.rs index c69aa0596..b10243f27 100644 --- a/examples/rpc/src/server.rs +++ b/examples/rpc/src/server.rs @@ -38,7 +38,7 @@ impl Server { owner.rpc_id( tree.get_rpc_sender_id(), "return_greeting", - &[Variant::from_str("hello")], + &[Variant::new("hello")], ); } } diff --git a/examples/scene_create/src/lib.rs b/examples/scene_create/src/lib.rs index 6e14da462..d4ba42b0b 100644 --- a/examples/scene_create/src/lib.rs +++ b/examples/scene_create/src/lib.rs @@ -140,13 +140,9 @@ fn update_panel(owner: &Spatial, num_children: i64) { let panel_node = unsafe { panel_node.assume_safe() }; // Put the Node - let mut as_variant = Variant::from_object(panel_node); - let result = unsafe { - as_variant.call( - "set_num_children", - &[Variant::from_u64(num_children as u64)], - ) - }; + let mut as_variant = Variant::new(panel_node); + let result = + unsafe { as_variant.call("set_num_children", &[Variant::new(num_children as u64)]) }; match result { Ok(_) => godot_print!("Called Panel OK."), diff --git a/examples/signals/src/lib.rs b/examples/signals/src/lib.rs index d972b10e6..eb53250c1 100644 --- a/examples/signals/src/lib.rs +++ b/examples/signals/src/lib.rs @@ -22,7 +22,7 @@ impl SignalEmitter { // Argument list used by the editor for GUI and generation of GDScript handlers. It can be omitted if the signal is only used from code. args: &[SignalArgument { name: "data", - default: Variant::from_i64(100), + default: Variant::new(100), export_info: ExportInfo::new(VariantType::I64), usage: PropertyUsage::DEFAULT, }], @@ -48,7 +48,7 @@ impl SignalEmitter { if self.data % 2 == 0 { owner.emit_signal("tick", &[]); } else { - owner.emit_signal("tick_with_data", &[Variant::from_i64(self.data)]); + owner.emit_signal("tick_with_data", &[Variant::new(self.data)]); } } } @@ -96,7 +96,7 @@ impl SignalSubscriber { fn notify_with_data(&mut self, owner: &Label, data: Variant) { let msg = format!( "Received signal \"tick_with_data\" with data {}", - data.try_to_u64().unwrap() + data.try_to::().unwrap() ); owner.set_text(msg); diff --git a/gdnative-async/src/method.rs b/gdnative-async/src/method.rs index 7b4c89797..8afe37387 100644 --- a/gdnative-async/src/method.rs +++ b/gdnative-async/src/method.rs @@ -106,14 +106,14 @@ impl> Method for Async { Self::site().unwrap_or_default(), format_args!("unable to spawn future: {}", err), ); - Variant::new() + Variant::nil() } None => { log::error( Self::site().unwrap_or_default(), format_args!("implementation did not spawn a future"), ); - Variant::new() + Variant::nil() } } } else { @@ -121,7 +121,7 @@ impl> Method for Async { Self::site().unwrap_or_default(), "a global executor must be set before any async methods can be called on this thread", ); - Variant::new() + Variant::nil() } } } diff --git a/gdnative-async/src/rt/bridge.rs b/gdnative-async/src/rt/bridge.rs index 8bb674677..ae49e5cd1 100644 --- a/gdnative-async/src/rt/bridge.rs +++ b/gdnative-async/src/rt/bridge.rs @@ -125,7 +125,7 @@ impl Method for OnSignalFn { }) .unwrap(); - Variant::new() + Variant::nil() } fn site() -> Option> { diff --git a/gdnative-async/src/rt/func_state.rs b/gdnative-async/src/rt/func_state.rs index 01f4531b7..b3e213bff 100644 --- a/gdnative-async/src/rt/func_state.rs +++ b/gdnative-async/src/rt/func_state.rs @@ -36,7 +36,7 @@ impl NativeClass for FuncState { name: "completed", args: &[SignalArgument { name: "value", - default: Variant::new(), + default: Variant::nil(), export_info: ExportInfo::new(VariantType::Nil), usage: PropertyUsage::DEFAULT, }], diff --git a/gdnative-core/src/core_types/dictionary.rs b/gdnative-core/src/core_types/dictionary.rs index f1a6972f1..d8c773463 100644 --- a/gdnative-core/src/core_types/dictionary.rs +++ b/gdnative-core/src/core_types/dictionary.rs @@ -95,13 +95,13 @@ impl Dictionary { } /// Returns a copy of the element corresponding to the key, or `Nil` if it doesn't exist. - /// Shorthand for `self.get_or(key, Variant::new())`. + /// Shorthand for `self.get_or(key, Variant::nil())`. #[inline] pub fn get_or_nil(&self, key: K) -> Variant where K: OwnedToVariant + ToVariantEq, { - self.get_or(key, Variant::new()) + self.get_or(key, Variant::nil()) } /// Update an existing element corresponding to the key. @@ -534,12 +534,12 @@ godot_test!(test_dictionary { use std::collections::HashSet; use crate::core_types::VariantType; - let foo = Variant::from_str("foo"); - let bar = Variant::from_str("bar"); - let nope = Variant::from_str("nope"); + let foo = Variant::new("foo"); + let bar = Variant::new("bar"); + let nope = Variant::new("nope"); - let x = Variant::from_i64(42); - let y = Variant::from_i64(1337); + let x = Variant::new(42); + let y = Variant::new(1337); let dict = Dictionary::new(); @@ -551,7 +551,7 @@ godot_test!(test_dictionary { assert!(!dict.contains(&nope)); let keys_array = dict.keys(); - let baz = Variant::from_str("baz"); + let baz = Variant::new("baz"); keys_array.push(&baz); dict.insert(&baz, &x); @@ -561,14 +561,14 @@ godot_test!(test_dictionary { assert!(!dict.contains_all(&keys_array)); - let variant = Variant::from_dictionary(&dict.duplicate().into_shared()); + let variant = Variant::new(&dict.duplicate().into_shared()); assert!(variant.get_type() == VariantType::Dictionary); let dict2 = dict.duplicate(); assert!(dict2.contains(&foo)); assert!(dict2.contains(&bar)); - if let Some(dic_variant) = variant.try_to_dictionary() { + if let Ok(dic_variant) = variant.try_to::() { assert!(dic_variant.len() == dict.len()); } else { panic!("variant should be a Dictionary"); diff --git a/gdnative-core/src/core_types/string.rs b/gdnative-core/src/core_types/string.rs index a8b3a0e7c..cb59ffe77 100644 --- a/gdnative-core/src/core_types/string.rs +++ b/gdnative-core/src/core_types/string.rs @@ -695,13 +695,13 @@ godot_test!(test_string { assert_eq!(index_string[1], 'a'); assert_eq!(index_string[2], 'r'); - let variant = Variant::from_godot_string(&foo); + let variant = Variant::new(&foo); assert!(variant.get_type() == VariantType::GodotString); let variant2: Variant = "foo".to_variant(); assert!(variant == variant2); - if let Some(foo_variant) = variant.try_to_godot_string() { + if let Ok(foo_variant) = variant.try_to::() { assert!(foo_variant == foo); } else { panic!("variant should be a GodotString"); diff --git a/gdnative-core/src/core_types/variant.rs b/gdnative-core/src/core_types/variant.rs index a4bb889c9..6bb3431e7 100644 --- a/gdnative-core/src/core_types/variant.rs +++ b/gdnative-core/src/core_types/variant.rs @@ -20,76 +20,45 @@ mod serialize; /// depending on the size of the type and whether the it is trivially copyable. /// /// If you compile godot-rust with the `serde` feature enabled, you will have -/// access to serialization/deserialization support: the traits `Serializable` -/// and `Deserializable` will be automatically implemented on [`VariantDispatch`] +/// access to serialization/deserialization support: the traits `Serialize` +/// and `Deserialize` will be automatically implemented on [`VariantDispatch`] /// as well as most of the types in [`core_types`]. pub struct Variant(pub(crate) sys::godot_variant); -macro_rules! variant_constructors { - ( - $( - $(#[$attr:meta])* - pub fn $ctor:ident($Type:ty) -> Self; - )* - ) => ( - $( - $(#[$attr])* - pub fn $ctor(val: $Type) -> Variant { - ToVariant::to_variant(val) - } - )* - ) -} - -macro_rules! variant_to_type_transmute { - ( - $( - $(#[$to_attr:meta])* - pub fn $to_method:ident(&self) -> $ToType:ident : $to_gd_method:ident; - $(#[$try_attr:meta])* - pub fn $try_method:ident(&self) -> Option<$TryType:ident>; - )* - ) => ( - $( - $(#[$to_attr])* - pub fn $to_method(&self) -> $ToType { +macro_rules! impl_coerce_from_variant_inner { + (impl CoerceFromVariant for $type:path = transmute($to_gd_method:ident)) => { + impl private::Sealed for $type {} + impl CoerceFromVariant for $type { + #[inline] + fn coerce_from_variant(v: &Variant) -> Self { unsafe { #[allow(clippy::useless_transmute)] - transmute((get_api().$to_gd_method)(&self.0)) + transmute((get_api().$to_gd_method)(&v.0)) } } - - $(#[$try_attr])* - pub fn $try_method(&self) -> Option<$TryType> { - $TryType::from_variant(self).ok() + } + }; + (impl CoerceFromVariant for $type:path = from_sys($to_gd_method:ident)) => { + impl private::Sealed for $type {} + impl CoerceFromVariant for $type { + #[inline] + fn coerce_from_variant(v: &Variant) -> Self { + unsafe { Self::from_sys((get_api().$to_gd_method)(&v.0)) } } - )* - ) + } + }; } -macro_rules! variant_to_type_from_sys { +macro_rules! impl_coerce_from_variant { ( $( - $(#[$to_attr:meta])* - pub fn $to_method:ident(&self) -> $ToType:ty : $to_gd_method:ident; - $(#[$try_attr:meta])* - pub fn $try_method:ident(&self) -> Option<$TryType:ty>; + impl CoerceFromVariant for $type:path = $kind:ident ($to_gd_method:ident); )* - ) => ( + ) => { $( - $(#[$to_attr])* - pub fn $to_method(&self) -> $ToType { - unsafe { - <$ToType>::from_sys((get_api().$to_gd_method)(&self.0)) - } - } - - $(#[$try_attr])* - pub fn $try_method(&self) -> Option<$TryType> { - <$TryType>::from_variant(self).ok() - } + impl_coerce_from_variant_inner!(impl CoerceFromVariant for $type = $kind($to_gd_method)); )* - ) + } } macro_rules! variant_dispatch_arm { @@ -156,7 +125,7 @@ macro_rules! decl_variant_type { let v: &$inner = v; v.to_variant() })?)* - _ => Variant::new() + _ => Variant::nil() } } } @@ -325,126 +294,29 @@ impl VariantOperator { #[derive(Copy, Clone, Eq, PartialEq, Hash, Default, Debug)] pub struct InvalidOp; -//fn to_godot_varianty_type(v: VariantType) -> sys::godot_variant_type { -// unsafe { transmute(v) } -//} - // These aliases are just here so the type name matches the VariantType's variant names // to make writing macros easier. -type F64 = f64; -type I64 = i64; type Bool = bool; impl Variant { - variant_constructors!( - /// Creates a `Variant` wrapping a `Vector2`. - #[inline] - pub fn from_vector2(&Vector2) -> Self; - /// Creates a `Variant` wrapping a `Vector3`. - #[inline] - pub fn from_vector3(&Vector3) -> Self; - /// Creates a `Variant` wrapping a `Quat`. - #[inline] - pub fn from_quat(&Quat) -> Self; - /// Creates a `Variant` wrapping a `Plane`. - #[inline] - pub fn from_plane(&Plane) -> Self; - /// Creates a `Variant` wrapping a `Rect2`. - #[inline] - pub fn from_rect2(&Rect2) -> Self; - /// Creates a `Variant` wrapping a `Transform`. - #[inline] - pub fn from_transform(&Transform) -> Self; - /// Creates a `Variant` wrapping a `Transform2D`. - #[inline] - pub fn from_transform2d(&Transform2D) -> Self; - /// Creates a `Variant` wrapping a `Basis`. - #[inline] - pub fn from_basis(&Basis) -> Self; - /// Creates a `Variant` wrapping a `Color`. - #[inline] - pub fn from_color(&Color) -> Self; - /// Creates a `Variant` wrapping an `Aabb`. - #[inline] - pub fn from_aabb(&Aabb) -> Self; - /// Creates a `Variant` wrapping an `Rid`. - #[inline] - pub fn from_rid(&Rid) -> Self; - /// Creates a `Variant` wrapping a `NodePath`. - #[inline] - pub fn from_node_path(&NodePath) -> Self; - /// Creates a `Variant` wrapping a `GodotString`. - #[inline] - pub fn from_godot_string(&GodotString) -> Self; - /// Creates an `Variant` wrapping an array of variants. - #[inline] - pub fn from_array(&VariantArray) -> Self; - /// Creates a `Variant` wrapping a byte array. - #[inline] - pub fn from_byte_array(&ByteArray) -> Self; - /// Creates a `Variant` wrapping an array of 32bit signed integers. - #[inline] - pub fn from_int32_array(&Int32Array) -> Self; - /// Creates a `Variant` wrapping an array of 32bit floats. - #[inline] - pub fn from_float32_array(&Float32Array) -> Self; - /// Creates a `Variant` wrapping an array of godot strings. - #[inline] - pub fn from_string_array(&StringArray) -> Self; - /// Creates a `Variant` wrapping an array of 2d vectors. - #[inline] - pub fn from_vector2_array(&Vector2Array) -> Self; - /// Creates a `Variant` wrapping an array of 3d vectors. - #[inline] - pub fn from_vector3_array(&Vector3Array) -> Self; - /// Creates a `Variant` wrapping an array of colors. - #[inline] - pub fn from_color_array(&ColorArray) -> Self; - /// Creates a `Variant` wrapping a dictionary. - #[inline] - pub fn from_dictionary(&Dictionary) -> Self; - ); - - /// Creates an empty `Variant`. + /// Creates a `Variant` from a value that implements [`ToVariant`]. #[inline] - pub fn new() -> Self { - unsafe { - let api = get_api(); - let mut dest = sys::godot_variant::default(); - (api.godot_variant_new_nil)(&mut dest); - Variant(dest) - } + pub fn new(from: T) -> Self { + from.owned_to_variant() } - /// Creates a `Variant` wrapping a string. + /// Creates an empty `Variant`. #[inline] - #[allow(clippy::should_implement_trait)] - pub fn from_str(s: S) -> Variant - where - S: AsRef, - { + pub fn nil() -> Self { unsafe { let api = get_api(); let mut dest = sys::godot_variant::default(); - let val = s.as_ref(); - let mut godot_s = - (api.godot_string_chars_to_utf8_with_len)(val.as_ptr() as *const _, val.len() as _); - (api.godot_variant_new_string)(&mut dest, &godot_s); - (api.godot_string_destroy)(&mut godot_s); + (api.godot_variant_new_nil)(&mut dest); Variant(dest) } } - /// Creates a `Variant` wrapping a Godot object. - #[inline] - pub fn from_object(val: R) -> Variant - where - R: AsVariant, - { - unsafe { R::to_arg_variant(&val) } - } - - /// Creats a `Variant` from a raw object pointer. + /// Creates a `Variant` from a raw object pointer. /// /// # Safety /// @@ -458,50 +330,6 @@ impl Variant { Variant(dest) } - /// Creates a `Variant` wrapping a signed integer value. - #[inline] - pub fn from_i64(v: i64) -> Variant { - unsafe { - let api = get_api(); - let mut dest = sys::godot_variant::default(); - (api.godot_variant_new_int)(&mut dest, v); - Variant(dest) - } - } - - /// Creates a `Variant` wrapping an unsigned integer value. - #[inline] - pub fn from_u64(v: u64) -> Variant { - unsafe { - let api = get_api(); - let mut dest = sys::godot_variant::default(); - (api.godot_variant_new_uint)(&mut dest, v); - Variant(dest) - } - } - - /// Creates a `Variant` wrapping a double-precision float value. - #[inline] - pub fn from_f64(v: f64) -> Variant { - unsafe { - let api = get_api(); - let mut ret = sys::godot_variant::default(); - (api.godot_variant_new_real)(&mut ret, v); - Variant(ret) - } - } - - /// Creates a `Variant` wrapping an boolean. - #[inline] - pub fn from_bool(v: bool) -> Variant { - unsafe { - let api = get_api(); - let mut dest = sys::godot_variant::default(); - (api.godot_variant_new_bool)(&mut dest, v); - Variant(dest) - } - } - #[inline] fn try_as_sys_of_type( &self, @@ -517,219 +345,58 @@ impl Variant { Ok(&self.0) } - variant_to_type_transmute!( - /// Do a best effort to create a `Vector2` out of the variant, possibly returning a default value. - #[inline] - pub fn to_vector2(&self) -> Vector2 : godot_variant_as_vector2; - /// Returns `Some(Vector2)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_vector2(&self) -> Option; - - /// Do a best effort to create a `Vector3` out of the variant, possibly returning a default value. - #[inline] - pub fn to_vector3(&self) -> Vector3 : godot_variant_as_vector3; - /// Returns `Some(Vector3)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_vector3(&self) -> Option; - - /// Do a best effort to create a `Quat` out of the variant, possibly returning a default value. - #[inline] - pub fn to_quat(&self) -> Quat : godot_variant_as_quat; - /// Returns `Some(Quat)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_quat(&self) -> Option; - - /// Do a best effort to create a `Rect2` out of the variant, possibly returning a default value. - #[inline] - pub fn to_rect2(&self) -> Rect2 : godot_variant_as_rect2; - /// Returns `Some(Rect2)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_rect2(&self) -> Option; - - /// Do a best effort to create a `Transform2D` out of the variant, possibly returning a default value. - #[inline] - pub fn to_transform2d(&self) -> Transform2D : godot_variant_as_transform2d; - /// Returns `Some(Transform2D)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_transform2d(&self) -> Option; - - /// Do a best effort to create a `f64` out of the variant, possibly returning a default value. - #[inline] - pub fn to_f64(&self) -> F64 : godot_variant_as_real; - /// Returns `Some(f64)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_f64(&self) -> Option; - - /// Do a best effort to create an `i64` out of the variant, possibly returning a default value. - #[inline] - pub fn to_i64(&self) -> I64 : godot_variant_as_int; - /// Returns `Some(i64)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_i64(&self) -> Option; - - /// Do a best effort to create a `bool` out of the variant, possibly returning a default value. - #[inline] - pub fn to_bool(&self) -> Bool : godot_variant_as_bool; - /// Returns `Some(bool)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_bool(&self) -> Option; - ); - - /// Do a best effort to create a `u64` out of the variant, possibly returning a default value. + /// Performs a strongly-typed, structure-aware conversion to `T` from this variant, if it + /// is a valid representation of said type. This is the same as `T::from_variant(self).ok()`. + /// + /// This is the same conversion used to parse arguments of exported methods. See + /// [`FromVariant`] for more details. #[inline] - pub fn to_u64(&self) -> u64 { - unsafe { - let api = get_api(); - (api.godot_variant_as_uint)(&self.0) - } + pub fn to(&self) -> Option { + self.try_to().ok() } - /// Returns `Some(u64)` if this variant is one, `None` otherwise. + /// Performs a strongly-typed, structure-aware conversion to `T` from this variant, if it + /// is a valid representation of said type. This is the same as `T::from_variant(self)`. + /// + /// This is the same conversion used to parse arguments of exported methods. See + /// [`FromVariant`] for more details. #[inline] - pub fn try_to_u64(&self) -> Option { - unsafe { - let api = get_api(); - if (api.godot_variant_get_type)(&self.0) - == sys::godot_variant_type_GODOT_VARIANT_TYPE_INT - { - Some((api.godot_variant_as_uint)(&self.0)) - } else { - None - } - } + pub fn try_to(&self) -> Result { + T::from_variant(self) } - variant_to_type_from_sys!( - /// Do a best effort to create a `Plane` out of the variant, possibly returning a default value. - #[inline] - pub fn to_plane(&self) -> Plane : godot_variant_as_plane; - /// Returns `Some(Plane)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_plane(&self) -> Option; - - /// Do a best effort to create a `Transform` out of the variant, possibly returning a default value. - #[inline] - pub fn to_transform(&self) -> Transform : godot_variant_as_transform; - /// Returns `Some(Transform)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_transform(&self) -> Option; - - /// Do a best effort to create a `Color` out of the variant, possibly returning a default value. - #[inline] - pub fn to_color(&self) -> Color : godot_variant_as_color; - /// Returns `Some(Color)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_color(&self) -> Option; - - /// Do a best effort to create a `Basis` out of the variant, possibly returning a default value. - #[inline] - pub fn to_basis(&self) -> Basis : godot_variant_as_basis; - /// Returns `Some(Basis)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_basis(&self) -> Option; - - /// Do a best effort to create an `Aabb` out of the variant, possibly returning a default value. - #[inline] - pub fn to_aabb(&self) -> Aabb : godot_variant_as_aabb; - /// Returns `Some(Aabb)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_aabb(&self) -> Option; - - /// Do a best effort to create a `NodePath` out of the variant, possibly returning a default value. - #[inline] - pub fn to_node_path(&self) -> NodePath : godot_variant_as_node_path; - /// Returns `Some(NodePath)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_node_path(&self) -> Option; - - /// Do a best effort to create a `GodotString` out of the variant, possibly returning a default value. - #[inline] - pub fn to_godot_string(&self) -> GodotString : godot_variant_as_string; - /// Returns `Some(GodotString)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_godot_string(&self) -> Option; - - /// Do a best effort to create a `Rid` out of the variant, possibly returning a default value. - #[inline] - pub fn to_rid(&self) -> Rid : godot_variant_as_rid; - /// Returns `Some(Rid)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_rid(&self) -> Option; - - /// Do a best effort to create a `VariantArray` out of the variant, possibly returning a default value. - #[inline] - pub fn to_array(&self) -> VariantArray : godot_variant_as_array; - /// Returns `Some(VariantArray)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_array(&self) -> Option>; - - /// Do a best effort to create a `ByteArray` out of the variant, possibly returning a default value. - #[inline] - pub fn to_byte_array(&self) -> ByteArray : godot_variant_as_pool_byte_array; - /// Returns `Some(ByteArray)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_byte_array(&self) -> Option; - - /// Do a best effort to create an `Int32Array` out of the variant, possibly returning a default value. - #[inline] - pub fn to_int32_array(&self) -> Int32Array : godot_variant_as_pool_int_array; - /// Returns `Some(Int32Array)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_int32_array(&self) -> Option; - - /// Do a best effort to create a `Float32Array` out of the variant, possibly returning a default value. - #[inline] - pub fn to_float32_array(&self) -> Float32Array : godot_variant_as_pool_real_array; - /// Returns `Some(Float32Array)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_float32_array(&self) -> Option; - - /// Do a best effort to create a `StringArray` out of the variant, possibly returning a default value. - #[inline] - pub fn to_string_array(&self) -> StringArray : godot_variant_as_pool_string_array; - /// Returns `Some(StringArray)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_string_array(&self) -> Option; - - /// Do a best effort to create a `Vector2Array` out of the variant, possibly returning a default value. - #[inline] - pub fn to_vector2_array(&self) -> Vector2Array : godot_variant_as_pool_vector2_array; - /// Returns `Some(Vector2Array)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_vector2_array(&self) -> Option; - - /// Do a best effort to create a `Vector3Array` out of the variant, possibly returning a default value. - #[inline] - pub fn to_vector3_array(&self) -> Vector3Array : godot_variant_as_pool_vector3_array; - /// Returns `Some(Vector3Array)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_vector3_array(&self) -> Option; - - /// Do a best effort to create a `ColorArray` out of the variant, possibly returning a default value. - #[inline] - pub fn to_color_array(&self) -> ColorArray : godot_variant_as_pool_color_array; - /// Returns `Some(ColorArray)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_color_array(&self) -> Option; - - /// Do a best effort to create a `Dictionary` out of the variant, possibly returning a default value. - #[inline] - pub fn to_dictionary(&self) -> Dictionary : godot_variant_as_dictionary; - /// Returns `Some(Dictionary)` if this variant is one, `None` otherwise. - #[inline] - pub fn try_to_dictionary(&self) -> Option>; - ); + /// Coerce a value of type `T` out of this variant, through what Godot presents as a + /// "best-effort" conversion, possibly returning a default value. + /// + /// See [`CoerceFromVariant`] for more details. + /// + /// See also [`Variant::to`] and [`Variant::try_to`] for strongly-typed, structure-aware + /// conversions into Rust types. + #[inline] + pub fn coerce_to(&self) -> T { + T::coerce_from_variant(self) + } + /// Convenience method to extract a `Ref` from this variant, if the type + /// matches. This is the same as `Ref::::from_variant(self).ok()`. + /// + /// This is the same conversion used to parse arguments of exported methods. See + /// [`FromVariant`] for more details. #[inline] - pub fn try_to_object(&self) -> Option> + pub fn to_object(&self) -> Option> where T: GodotObject, { - self.try_to_object_with_error::().ok() + self.try_to_object::().ok() } + /// Convenience method to extract a `Ref` from this variant, if the type + /// matches. This is the same as `Ref::::from_variant(self)`. + /// + /// This is the same conversion used to parse arguments of exported methods. See + /// [`FromVariant`] for more details. #[inline] - pub fn try_to_object_with_error(&self) -> Result, FromVariantError> + pub fn try_to_object(&self) -> Result, FromVariantError> where T: GodotObject, { @@ -751,11 +418,6 @@ impl Variant { } } - #[inline] - pub fn try_to_string(&self) -> Option { - self.try_to_godot_string().map(|s| s.to_string()) - } - /// Returns this variant's type. #[inline] pub fn get_type(&self) -> VariantType { @@ -828,7 +490,7 @@ impl Variant { pub fn evaluate(&self, op: VariantOperator, rhs: &Self) -> Result { unsafe { let api = get_api(); - let mut ret = Variant::new(); + let mut ret = Variant::nil(); let mut valid = false; (api.godot_variant_evaluate)( @@ -869,6 +531,7 @@ impl Variant { /// responsible for running the destructor for the object, otherwise /// it is leaked. #[inline] + #[doc(hidden)] pub fn forget(self) -> sys::godot_variant { let v = self.0; forget(self); @@ -904,6 +567,35 @@ impl Variant { } } +impl_coerce_from_variant!( + impl CoerceFromVariant for Vector2 = transmute(godot_variant_as_vector2); + impl CoerceFromVariant for Vector3 = transmute(godot_variant_as_vector3); + impl CoerceFromVariant for Quat = transmute(godot_variant_as_quat); + impl CoerceFromVariant for Rect2 = transmute(godot_variant_as_rect2); + impl CoerceFromVariant for Transform2D = transmute(godot_variant_as_transform2d); + impl CoerceFromVariant for f64 = transmute(godot_variant_as_real); + impl CoerceFromVariant for i64 = transmute(godot_variant_as_int); + impl CoerceFromVariant for u64 = transmute(godot_variant_as_uint); + impl CoerceFromVariant for Bool = transmute(godot_variant_as_bool); + impl CoerceFromVariant for Plane = from_sys(godot_variant_as_plane); + impl CoerceFromVariant for Transform = from_sys(godot_variant_as_transform); + impl CoerceFromVariant for Color = from_sys(godot_variant_as_color); + impl CoerceFromVariant for Basis = from_sys(godot_variant_as_basis); + impl CoerceFromVariant for Aabb = from_sys(godot_variant_as_aabb); + impl CoerceFromVariant for NodePath = from_sys(godot_variant_as_node_path); + impl CoerceFromVariant for GodotString = from_sys(godot_variant_as_string); + impl CoerceFromVariant for Rid = from_sys(godot_variant_as_rid); + impl CoerceFromVariant for VariantArray = from_sys(godot_variant_as_array); + impl CoerceFromVariant for ByteArray = from_sys(godot_variant_as_pool_byte_array); + impl CoerceFromVariant for Int32Array = from_sys(godot_variant_as_pool_int_array); + impl CoerceFromVariant for Float32Array = from_sys(godot_variant_as_pool_real_array); + impl CoerceFromVariant for StringArray = from_sys(godot_variant_as_pool_string_array); + impl CoerceFromVariant for Vector2Array = from_sys(godot_variant_as_pool_vector2_array); + impl CoerceFromVariant for Vector3Array = from_sys(godot_variant_as_pool_vector3_array); + impl CoerceFromVariant for ColorArray = from_sys(godot_variant_as_pool_color_array); + impl CoerceFromVariant for Dictionary = from_sys(godot_variant_as_dictionary); +); + impl_basic_traits_as_sys!( for Variant as godot_variant { Drop => godot_variant_destroy; @@ -914,17 +606,17 @@ impl_basic_traits_as_sys!( impl Eq for Variant {} -impl ToString for Variant { +impl fmt::Display for Variant { #[inline] - fn to_string(&self) -> String { - self.to_godot_string().to_string() + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.coerce_to::().fmt(f) } } impl Default for Variant { #[inline] fn default() -> Self { - Variant::new() + Variant::nil() } } @@ -937,17 +629,17 @@ impl fmt::Debug for Variant { godot_test!( test_variant_nil { - let nil = Variant::new(); + let nil = Variant::nil(); assert_eq!(nil.get_type(), VariantType::Nil); assert!(nil.is_nil()); - assert!(nil.try_to_array().is_none()); - assert!(nil.try_to_rid().is_none()); - assert!(nil.try_to_i64().is_none()); - assert!(nil.try_to_bool().is_none()); - assert!(nil.try_to_aabb().is_none()); - assert!(nil.try_to_vector2().is_none()); - assert!(nil.try_to_basis().is_none()); + assert!(nil.try_to::().is_err()); + assert!(nil.try_to::().is_err()); + assert!(nil.try_to::().is_err()); + assert!(nil.try_to::().is_err()); + assert!(nil.try_to::().is_err()); + assert!(nil.try_to::().is_err()); + assert!(nil.try_to::().is_err()); assert!(!nil.has_method("foo")); @@ -956,39 +648,39 @@ godot_test!( } test_variant_i64 { - let v_42 = Variant::from_i64(42); + let v_42 = Variant::new(42); assert_eq!(v_42.get_type(), VariantType::I64); assert!(!v_42.is_nil()); - assert_eq!(v_42.try_to_i64(), Some(42)); - assert!(v_42.try_to_f64().is_none()); - assert!(v_42.try_to_array().is_none()); + assert_eq!(v_42.try_to::(), Ok(42)); + assert!(v_42.try_to::().is_err()); + assert!(v_42.try_to::().is_err()); - let v_m1 = Variant::from_i64(-1); + let v_m1 = Variant::new(-1); assert_eq!(v_m1.get_type(), VariantType::I64); assert!(!v_m1.is_nil()); - assert_eq!(v_m1.try_to_i64(), Some(-1)); - assert!(v_m1.try_to_f64().is_none()); - assert!(v_m1.try_to_array().is_none()); + assert_eq!(v_m1.try_to::(), Ok(-1)); + assert!(v_m1.try_to::().is_err()); + assert!(v_m1.try_to::().is_err()); } test_variant_bool { - let v_true = Variant::from_bool(true); + let v_true = Variant::new(true); assert_eq!(v_true.get_type(), VariantType::Bool); assert!(!v_true.is_nil()); - assert_eq!(v_true.try_to_bool(), Some(true)); - assert!(v_true.try_to_f64().is_none()); - assert!(v_true.try_to_array().is_none()); + assert_eq!(v_true.try_to::(), Ok(true)); + assert!(v_true.try_to::().is_err()); + assert!(v_true.try_to::().is_err()); - let v_false = Variant::from_bool(false); + let v_false = Variant::new(false); assert_eq!(v_false.get_type(), VariantType::Bool); assert!(!v_false.is_nil()); - assert_eq!(v_false.try_to_bool(), Some(false)); - assert!(v_false.try_to_f64().is_none()); - assert!(v_false.try_to_array().is_none()); + assert_eq!(v_false.try_to::(), Ok(false)); + assert!(v_false.try_to::().is_err()); + assert!(v_false.try_to::().is_err()); } ); @@ -1060,6 +752,8 @@ pub trait ToVariant { /// This has a blanket implementation for all types that have `ToVariant`. As such, users /// should only derive or implement `OwnedToVariant` when `ToVariant` is not applicable. /// +/// This trait is used for return types of exported methods. +/// /// ## Deriving `OwnedToVariant` /// /// The derive macro behaves the same as `ToVariant`. See the documentation for the latter for @@ -1094,7 +788,15 @@ pub trait OwnedToVariant { /// ``` pub trait ToVariantEq: Eq {} -/// Types that can be converted from a `Variant`. +/// Types that can be converted from a `Variant`. Conversions are performed in Rust, and can be +/// implemented for custom types. +/// +/// [`FromVariant`] generally avoids inexact conversions, favoring returning an error instead, +/// with the noted exception of integer and float types that are not supported by Godot natively. +/// This is stricter than what GDScript performs by default. For weakly-typed coercions of GDScript +/// built-in typed, see [`CoerceFromVariant`] instead. +/// +/// This trait is used for argument types of exported methods. /// /// ## `Option` and `MaybeNot` /// @@ -1115,6 +817,19 @@ pub trait FromVariant: Sized { fn from_variant(variant: &Variant) -> Result; } +/// Types that can be coerced from a `Variant`. Coercions are provided by Godot, with results +/// consistent with GDScript. This cannot be implemented for custom types. +/// +/// [`CoerceFromVariant`] exposes what Godot presents as "best-effort" conversions. These are +/// weakly-typed conversions to GDScript built-in types, that will never fail, but can return +/// empty or "default" values, as defined by Godot. +/// +/// For strongly-typed conversions that can be implemented or derived for custom types, see +/// [`FromVariant`]. +pub trait CoerceFromVariant: Sized + private::Sealed { + fn coerce_from_variant(variant: &Variant) -> Self; +} + #[derive(Clone, PartialEq, Eq, Debug)] /// Error type returned by `FromVariant::from_variant`. pub enum FromVariantError { @@ -1299,7 +1014,7 @@ impl OwnedToVariant for T { impl ToVariant for () { #[inline] fn to_variant(&self) -> Variant { - Variant::new() + Variant::nil() } } impl ToVariantEq for () {} @@ -1359,7 +1074,7 @@ impl<'a, T: GodotObject> ToVariant for TRef<'a, T, Shared> { impl FromVariant for Ref { #[inline] fn from_variant(variant: &Variant) -> Result { - variant.try_to_object_with_error::() + variant.try_to_object::() } } @@ -1391,7 +1106,12 @@ from_variant_direct!( impl ToVariant for i64 { #[inline] fn to_variant(&self) -> Variant { - Variant::from_i64(*self) + unsafe { + let api = get_api(); + let mut dest = sys::godot_variant::default(); + (api.godot_variant_new_int)(&mut dest, *self); + Variant(dest) + } } } impl ToVariantEq for i64 {} @@ -1399,15 +1119,38 @@ impl ToVariantEq for i64 {} impl ToVariant for u64 { #[inline] fn to_variant(&self) -> Variant { - Variant::from_u64(*self) + unsafe { + let api = get_api(); + let mut dest = sys::godot_variant::default(); + (api.godot_variant_new_uint)(&mut dest, *self); + Variant(dest) + } } } impl ToVariantEq for u64 {} +impl ToVariant for bool { + #[inline] + fn to_variant(&self) -> Variant { + unsafe { + let api = get_api(); + let mut dest = sys::godot_variant::default(); + (api.godot_variant_new_bool)(&mut dest, *self); + Variant(dest) + } + } +} +impl ToVariantEq for bool {} + impl ToVariant for f64 { #[inline] fn to_variant(&self) -> Variant { - Variant::from_f64(*self) + unsafe { + let api = get_api(); + let mut ret = sys::godot_variant::default(); + (api.godot_variant_new_real)(&mut ret, *self); + Variant(ret) + } } } @@ -1423,6 +1166,14 @@ macro_rules! impl_to_variant_for_num { } } + impl private::Sealed for $ty {} + impl CoerceFromVariant for $ty { + #[inline] + fn coerce_from_variant(variant: &Variant) -> Self { + <$src_ty>::coerce_from_variant(variant) as Self + } + } + impl FromVariant for $ty { #[inline] fn from_variant(variant: &Variant) -> Result { @@ -1625,7 +1376,7 @@ impl FromVariant for PoolArray { impl ToVariant for str { #[inline] fn to_variant(&self) -> Variant { - Variant::from_str(self) + GodotString::from_str(self).owned_to_variant() } } impl ToVariantEq for str {} @@ -1633,7 +1384,7 @@ impl ToVariantEq for str {} impl ToVariant for String { #[inline] fn to_variant(&self) -> Variant { - Variant::from_str(&self) + self.as_str().to_variant() } } impl ToVariantEq for String {} @@ -1645,14 +1396,6 @@ impl FromVariant for String { } } -impl ToVariant for bool { - #[inline] - fn to_variant(&self) -> Variant { - Variant::from_bool(*self) - } -} -impl ToVariantEq for bool {} - impl ToVariant for Variant { #[inline] fn to_variant(&self) -> Variant { @@ -1670,7 +1413,7 @@ impl FromVariant for Variant { impl ToVariant for std::marker::PhantomData { #[inline] fn to_variant(&self) -> Variant { - Variant::new() + Variant::nil() } } impl ToVariantEq for std::marker::PhantomData {} @@ -1689,7 +1432,7 @@ impl ToVariant for Option { fn to_variant(&self) -> Variant { match &self { Some(thing) => thing.to_variant(), - None => Variant::new(), + None => Variant::nil(), } } } @@ -1922,44 +1665,48 @@ macro_rules! impl_variant_for_tuples { impl_variant_for_tuples!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,); +mod private { + pub trait Sealed {} +} + godot_test!( test_variant_option { use std::marker::PhantomData; let variant = Some(42_i64).to_variant(); - assert_eq!(Some(42), variant.try_to_i64()); + assert_eq!(Ok(42), variant.try_to::()); let variant = Option::::None.to_variant(); assert!(variant.is_nil()); - let variant = Variant::new(); + let variant = Variant::nil(); assert_eq!(Ok(None), Option::::from_variant(&variant)); assert_eq!(Ok(None), Option::::from_variant(&variant)); assert_eq!(Ok(None), Option::::from_variant(&variant)); - let variant = Variant::from_i64(42); + let variant = Variant::new(42); assert_eq!(Ok(Some(42)), Option::::from_variant(&variant)); assert!(Option::::from_variant(&variant).is_err()); assert!(Option::::from_variant(&variant).is_err()); - let variant = Variant::new(); + let variant = Variant::nil(); assert_eq!(Ok(Some(())), Option::<()>::from_variant(&variant)); assert_eq!(Ok(Some(PhantomData)), Option::>::from_variant(&variant)); - let variant = Variant::from_i64(42); + let variant = Variant::new(42); assert!(Option::>::from_variant(&variant).is_err()); } test_variant_result { let variant = Result::::Ok(42_i64).to_variant(); - let dict = variant.try_to_dictionary().expect("should be dic"); - assert_eq!(Some(42), dict.get("Ok").and_then(|v| v.try_to_i64())); + let dict = variant.try_to::().expect("should be dic"); + assert_eq!(Some(42), dict.get("Ok").and_then(|v| v.try_to::().ok())); let variant = Result::<(), i64>::Err(54_i64).to_variant(); - let dict = variant.try_to_dictionary().expect("should be dic"); - assert_eq!(Some(54), dict.get("Err").and_then(|v| v.try_to_i64())); + let dict = variant.try_to::().expect("should be dic"); + assert_eq!(Some(54), dict.get("Err").and_then(|v| v.try_to::().ok())); - let variant = Variant::from_bool(true); + let variant = Variant::new(true); assert_eq!( Err(FromVariantError::InvalidEnumRepr { expected: VariantEnumRepr::ExternallyTagged, @@ -1983,18 +1730,18 @@ godot_test!( test_to_variant_iter { let slice: &[i64] = &[0, 1, 2, 3, 4]; let variant = slice.to_variant(); - let array = variant.try_to_array().expect("should be array"); + let array = variant.try_to::().expect("should be array"); assert_eq!(5, array.len()); for i in 0..5 { - assert_eq!(Some(i), array.get(i as i32).try_to_i64()); + assert_eq!(Ok(i), array.get(i as i32).try_to::()); } let vec = Vec::::from_variant(&variant).expect("should succeed"); assert_eq!(slice, vec.as_slice()); let het_array = VariantArray::new(); - het_array.push(&Variant::from_i64(42)); - het_array.push(&Variant::new()); + het_array.push(&Variant::new(42)); + het_array.push(&Variant::nil()); assert_eq!( Err(FromVariantError::InvalidItem { @@ -2025,15 +1772,15 @@ godot_test!( let vec_maybe = Vec::>::from_variant(&het_array.into_shared().to_variant()).expect("should succeed"); assert_eq!(3, vec_maybe.len()); assert_eq!(Some(&42), vec_maybe[0].as_ref().ok()); - assert_eq!(Some(&Variant::new()), vec_maybe[1].as_ref().err()); + assert_eq!(Some(&Variant::nil()), vec_maybe[1].as_ref().err()); assert_eq!(Some(&f64::to_variant(&54.0)), vec_maybe[2].as_ref().err()); } test_variant_tuple { let variant = (42i64, 54i64).to_variant(); - let arr = variant.try_to_array().expect("should be array"); - assert_eq!(Some(42), arr.get(0).try_to_i64()); - assert_eq!(Some(54), arr.get(1).try_to_i64()); + let arr = variant.try_to::().expect("should be array"); + assert_eq!(Ok(42), arr.get(0).try_to::()); + assert_eq!(Ok(54), arr.get(1).try_to::()); let tuple = <(i64, i64)>::from_variant(&variant); assert_eq!(Ok((42, 54)), tuple); diff --git a/gdnative-core/src/core_types/variant_array.rs b/gdnative-core/src/core_types/variant_array.rs index 667bd63b2..5442e9be0 100644 --- a/gdnative-core/src/core_types/variant_array.rs +++ b/gdnative-core/src/core_types/variant_array.rs @@ -528,9 +528,9 @@ impl Extend for VariantArray { } godot_test!(test_array { - let foo = Variant::from_str("foo"); - let bar = Variant::from_str("bar"); - let nope = Variant::from_str("nope"); + let foo = Variant::new("foo"); + let bar = Variant::new("bar"); + let nope = Variant::new("nope"); let array = VariantArray::new(); // [] @@ -555,9 +555,9 @@ godot_test!(test_array { array.pop(); // [&bar] array.pop(); // [] - let x = Variant::from_i64(42); - let y = Variant::from_i64(1337); - let z = Variant::from_i64(512); + let x = Variant::new(42); + let y = Variant::new(1337); + let z = Variant::new(512); array.insert(0, &x); // [&x] array.insert(0, &y); // [&y, &x] @@ -590,13 +590,13 @@ godot_test!(test_array { let array3 = VariantArray::new(); // [] - array3.push(&Variant::from_i64(42)); - array3.push(&Variant::from_i64(1337)); - array3.push(&Variant::from_i64(512)); + array3.push(&Variant::new(42)); + array3.push(&Variant::new(1337)); + array3.push(&Variant::new(512)); assert_eq!( &[42, 1337, 512], - array3.iter().map(|v| v.try_to_i64().unwrap()).collect::>().as_slice(), + array3.iter().map(|v| v.try_to::().unwrap()).collect::>().as_slice(), ); let array4 = VariantArray::new(); // [] @@ -606,10 +606,10 @@ godot_test!(test_array { array5.push(array4); // [[&foo, &bar]] let array6 = array5.duplicate_deep(); // [[&foo, &bar]] - unsafe { array5.get(0).to_array().assume_unique().pop(); } // [[&foo]] + unsafe { array5.get(0).coerce_to::().assume_unique().pop(); } // [[&foo]] - assert!(!array5.get(0).to_array().contains(&bar)); - assert!(array6.get(0).to_array().contains(&bar)); + assert!(!array5.get(0).coerce_to::().contains(&bar)); + assert!(array6.get(0).coerce_to::().contains(&bar)); }); godot_test!( @@ -617,9 +617,9 @@ godot_test!( use std::panic::catch_unwind; let arr = VariantArray::new(); // [] - arr.push(&Variant::from_str("hello world")); - arr.push(&Variant::from_bool(true)); - arr.push(&Variant::from_i64(42)); + arr.push(&Variant::new("hello world")); + arr.push(&Variant::new(true)); + arr.push(&Variant::new(42)); assert_eq!(format!("{:?}", arr), "[GodotString(hello world), Bool(True), I64(42)]"); @@ -637,7 +637,7 @@ godot_test!( // TODO: clear arrays without affecting clones //godot_test!(test_array_clone_clear { -// let foo = Variant::from_str("foo"); +// let foo = Variant::new("foo"); // let mut array = VariantArray::new(); // // array.push(&foo); diff --git a/gdnative-core/src/export/class_builder.rs b/gdnative-core/src/export/class_builder.rs index fca15aacd..78614b3e1 100644 --- a/gdnative-core/src/export/class_builder.rs +++ b/gdnative-core/src/export/class_builder.rs @@ -143,7 +143,7 @@ impl ClassBuilder { /// fn call(&self, this: TInstance<'_, MyType, Shared>, _args: Varargs<'_>) -> Variant { /// this.map(|obj: &MyType, _| { /// let result = obj.my_method(); - /// Variant::from_i64(result) + /// Variant::new(result) /// }).expect("method call succeeds") /// } /// } diff --git a/gdnative-core/src/export/macros.rs b/gdnative-core/src/export/macros.rs index 6a8f171f2..92b3bea0f 100644 --- a/gdnative-core/src/export/macros.rs +++ b/gdnative-core/src/export/macros.rs @@ -62,7 +62,7 @@ macro_rules! godot_wrap_method_inner { .unwrap_or_else(|err| { $crate::godot_error!("gdnative-core: method call failed with error: {}", err); $crate::godot_error!("gdnative-core: check module level documentation on gdnative::user_data for more information"); - $crate::core_types::Variant::new() + $crate::core_types::Variant::nil() }) } diff --git a/gdnative-core/src/export/method.rs b/gdnative-core/src/export/method.rs index a018d58d9..b89ce1348 100644 --- a/gdnative-core/src/export/method.rs +++ b/gdnative-core/src/export/method.rs @@ -200,7 +200,7 @@ impl> Method for StaticArgs { Ok(parsed) => { if let Err(err) = args.done() { err.with_site(F::site().unwrap_or_default()).log_error(); - return Variant::new(); + return Variant::nil(); } F::call(&self.f, this, parsed) } @@ -208,7 +208,7 @@ impl> Method for StaticArgs { for err in errors { err.with_site(F::site().unwrap_or_default()).log_error(); } - Variant::new() + Variant::nil() } } } @@ -561,7 +561,7 @@ unsafe extern "C" fn method_wrapper>( C::class_name(), ), ); - return Variant::new().forget(); + return Variant::nil().forget(); } let this = match std::ptr::NonNull::new(this) { @@ -574,7 +574,7 @@ unsafe extern "C" fn method_wrapper>( C::class_name(), ), ); - return Variant::new().forget(); + return Variant::nil().forget(); } }; @@ -596,7 +596,7 @@ unsafe extern "C" fn method_wrapper>( F::site().unwrap_or_default(), "gdnative-core: method panicked (check stderr for output)", ); - Variant::new() + Variant::nil() }) .forget() } diff --git a/gdnative-core/src/export/property/accessor.rs b/gdnative-core/src/export/property/accessor.rs index 9380848fb..6849e2263 100644 --- a/gdnative-core/src/export/property/accessor.rs +++ b/gdnative-core/src/export/property/accessor.rs @@ -296,7 +296,7 @@ where "gdnative-core: user data pointer for {} is null (did the constructor fail?)", C::class_name(), ); - return Variant::new().forget(); + return Variant::nil().forget(); } let this = match NonNull::new(this) { @@ -306,7 +306,7 @@ where "gdnative-core: owner pointer for {} is null", C::class_name(), ); - return Variant::new().forget(); + return Variant::nil().forget(); } }; @@ -319,14 +319,14 @@ where Ok(variant) => variant.forget(), Err(err) => { godot_error!("gdnative-core: cannot call property getter: {:?}", err); - Variant::new().forget() + Variant::nil().forget() } } }); result.unwrap_or_else(|_| { godot_error!("gdnative-core: property getter panicked (check stderr for output)"); - Variant::new().forget() + Variant::nil().forget() }) } get.get_func = Some(invoke::); diff --git a/gdnative-core/src/export/property/invalid_accessor.rs b/gdnative-core/src/export/property/invalid_accessor.rs index 605a88e72..18cec2994 100644 --- a/gdnative-core/src/export/property/invalid_accessor.rs +++ b/gdnative-core/src/export/property/invalid_accessor.rs @@ -70,7 +70,7 @@ extern "C" fn invalid_getter( property_name, class_name ); - Variant::new().forget() + Variant::nil().forget() } extern "C" fn invalid_free_func(data: *mut libc::c_void) { diff --git a/gdnative-core/src/object/instance.rs b/gdnative-core/src/object/instance.rs index 19d5d9c4a..2d39521fa 100644 --- a/gdnative-core/src/object/instance.rs +++ b/gdnative-core/src/object/instance.rs @@ -177,7 +177,7 @@ impl Instance { let variant = Variant::from_sys(variant); let owner = variant - .try_to_object::() + .to_object::() .expect("the engine should return a base object of the correct type") .assume_unique(); diff --git a/test/src/lib.rs b/test/src/lib.rs index 0445252be..a9437c5b0 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -75,7 +75,7 @@ pub extern "C" fn run_tests( status &= test_variant_ops::run_tests(); status &= test_vararray_return::run_tests(); - gdnative::core_types::Variant::from_bool(status).forget() + gdnative::core_types::Variant::new(status).forget() } fn test_underscore_method_binding() -> bool { @@ -84,7 +84,7 @@ fn test_underscore_method_binding() -> bool { let ok = std::panic::catch_unwind(|| { let script = gdnative::api::NativeScript::new(); let result = script._new(&[]); - assert_eq!(Variant::new(), result); + assert_eq!(Variant::nil(), result); }) .is_ok(); @@ -139,7 +139,7 @@ impl Foo { #[export] fn choose_variant(&self, _owner: &Reference, a: i32, what: Variant, b: f64) -> Variant { - let what = what.try_to_string().expect("should be string"); + let what = what.try_to::().expect("should be string"); match what.as_str() { "int" => a.to_variant(), "float" => b.to_variant(), @@ -157,7 +157,7 @@ fn test_rust_class_construction() -> bool { assert_eq!(Ok(42), foo.map(|foo, owner| { foo.answer(&*owner) })); let base = foo.into_base(); - assert_eq!(Some(42), unsafe { base.call("answer", &[]).try_to_i64() }); + assert_eq!(Some(42), unsafe { base.call("answer", &[]).to() }); let foo = Instance::::try_from_base(base).expect("should be able to downcast"); assert_eq!(Ok(42), foo.map(|foo, owner| { foo.answer(&*owner) })); diff --git a/test/src/test_derive.rs b/test/src/test_derive.rs index 32b2a4218..ad92f8547 100644 --- a/test/src/test_derive.rs +++ b/test/src/test_derive.rs @@ -82,25 +82,27 @@ fn test_derive_to_variant() -> bool { }; let variant = data.to_variant(); - let dictionary = variant.try_to_dictionary().expect("should be dictionary"); - assert_eq!(Some(42), dictionary.get("foo").and_then(|v| v.try_to_i64())); + let dictionary = variant + .try_to::() + .expect("should be dictionary"); + assert_eq!(Some(42), dictionary.get("foo").and_then(|v| v.to::())); assert_eq!( Some(54.0), - dictionary.get("bar").and_then(|v| v.try_to_f64()) + dictionary.get("bar").and_then(|v| v.to::()) ); assert_eq!( Some("*mut ()".into()), - dictionary.get("ptr").and_then(|v| v.try_to_string()) + dictionary.get("ptr").and_then(|v| v.to::()) ); assert!(!dictionary.contains("skipped")); let enum_dict = dictionary .get("baz") - .and_then(|v| v.try_to_dictionary()) + .and_then(|v| v.to::()) .expect("should be dictionary"); assert_eq!( Some(true), - enum_dict.get("Foo").and_then(|v| v.try_to_bool()) + enum_dict.get("Foo").and_then(|v| v.to::()) ); assert_eq!( @@ -116,11 +118,11 @@ fn test_derive_to_variant() -> bool { let data = ToVarTuple::(1, 2, false); let variant = data.to_variant(); - let tuple_array = variant.try_to_array().expect("should be array"); + let tuple_array = variant.to::().expect("should be array"); assert_eq!(2, tuple_array.len()); - assert_eq!(Some(1), tuple_array.get(0).try_to_i64()); - assert_eq!(Some(false), tuple_array.get(1).try_to_bool()); + assert_eq!(Some(1), tuple_array.get(0).to::()); + assert_eq!(Some(false), tuple_array.get(1).to::()); assert_eq!( Ok(ToVarTuple::(1, 0, false)), ToVarTuple::from_variant(&variant) @@ -149,17 +151,17 @@ fn test_derive_owned_to_variant() -> bool { }; let variant = data.owned_to_variant(); - let dictionary = variant.try_to_dictionary().expect("should be dictionary"); + let dictionary = variant.to::().expect("should be dictionary"); let array = dictionary .get("arr") - .and_then(|v| v.try_to_array()) + .and_then(|v| v.to::()) .expect("should be array"); assert_eq!(3, array.len()); assert_eq!( &[1, 2, 3], array .iter() - .map(|v| v.try_to_i64().unwrap()) + .map(|v| v.to::().unwrap()) .collect::>() .as_slice() ); @@ -306,7 +308,7 @@ fn test_derive_nativeclass_without_constructor() -> bool { assert_eq!(Ok(54), foo.map(|foo, owner| { foo.answer(&*owner) })); let base = foo.into_base(); - assert_eq!(Some(54), unsafe { base.call("answer", &[]).try_to_i64() }); + assert_eq!(Some(54), unsafe { base.call("answer", &[]).to::() }); let foo = Instance::::try_from_base(base) .expect("should be able to downcast"); diff --git a/test/src/test_free_ub.rs b/test/src/test_free_ub.rs index ff00f2c2d..8ec2ab6f6 100644 --- a/test/src/test_free_ub.rs +++ b/test/src/test_free_ub.rs @@ -55,7 +55,7 @@ fn test_owner_free_ub() -> bool { let bar = Bar(42, Arc::clone(&drop_counter)).emplace(); assert_eq!(Some(true), unsafe { - bar.base().call("set_script_is_not_ub", &[]).try_to_bool() + bar.base().call("set_script_is_not_ub", &[]).to() }); bar.into_base().free(); @@ -65,7 +65,7 @@ fn test_owner_free_ub() -> bool { let bar = Bar(42, Arc::clone(&drop_counter)).emplace(); assert_eq!(Some(true), unsafe { - bar.base().call("free_is_not_ub", &[]).try_to_bool() + bar.base().call("free_is_not_ub", &[]).to() }); } diff --git a/test/src/test_register.rs b/test/src/test_register.rs index 1fd5ba624..a6f3a47bb 100644 --- a/test/src/test_register.rs +++ b/test/src/test_register.rs @@ -35,7 +35,7 @@ impl NativeClass for RegisterSignal { name: "progress", args: &[SignalArgument { name: "amount", - default: Variant::new(), + default: Variant::nil(), export_info: ExportInfo::new(VariantType::I64), usage: PropertyUsage::DEFAULT, }], @@ -90,21 +90,15 @@ fn test_register_property() -> bool { let base = obj.into_base(); - assert_eq!(Some(42), unsafe { - base.call("get_value", &[]).try_to_i64() - }); + assert_eq!(Some(42), unsafe { base.call("get_value", &[]).to() }); base.set("value", 54.to_variant()); - assert_eq!(Some(54), unsafe { - base.call("get_value", &[]).try_to_i64() - }); + assert_eq!(Some(54), unsafe { base.call("get_value", &[]).to() }); unsafe { base.call("set_value", &[4242.to_variant()]) }; - assert_eq!(Some(4242), unsafe { - base.call("get_value", &[]).try_to_i64() - }); + assert_eq!(Some(4242), unsafe { base.call("get_value", &[]).to() }); }) .is_ok(); @@ -190,7 +184,7 @@ fn test_advanced_methods() -> bool { i32::from_variant(unsafe { &thing.call( "add_ints", - &[1.to_variant(), 2.to_variant(), Variant::new()], + &[1.to_variant(), 2.to_variant(), Variant::nil()], ) }) .unwrap() @@ -212,7 +206,7 @@ fn test_advanced_methods() -> bool { f32::from_variant(unsafe { &thing.call( "add_floats", - &[(5.0).to_variant(), (-2.5).to_variant(), Variant::new()], + &[(5.0).to_variant(), (-2.5).to_variant(), Variant::nil()], ) }) .unwrap() @@ -224,7 +218,7 @@ fn test_advanced_methods() -> bool { &[ Vector2::new(5.0, -5.0).to_variant(), Vector2::new(-2.5, 2.5).to_variant(), - Variant::new(), + Variant::nil(), ], ) }) diff --git a/test/src/test_variant_call_args.rs b/test/src/test_variant_call_args.rs index be94b8667..d4173ad35 100644 --- a/test/src/test_variant_call_args.rs +++ b/test/src/test_variant_call_args.rs @@ -59,18 +59,11 @@ fn test_variant_call_args() -> bool { assert_eq!(Some(42), call_i64(&mut base, "zero", &[])); - assert_eq!( - Some(126), - call_i64(&mut base, "one", &[Variant::from_i64(3)]) - ); + assert_eq!(Some(126), call_i64(&mut base, "one", &[Variant::new(3)])); assert_eq!( Some(-10), - call_i64( - &mut base, - "two", - &[Variant::from_i64(-1), Variant::from_i64(32)] - ) + call_i64(&mut base, "two", &[Variant::new(-1), Variant::new(32)]) ); assert_eq!( @@ -78,11 +71,7 @@ fn test_variant_call_args() -> bool { call_i64( &mut base, "three", - &[ - Variant::from_i64(-2), - Variant::from_i64(4), - Variant::from_i64(8), - ] + &[Variant::new(-2), Variant::new(4), Variant::new(8),] ) ); }) @@ -98,5 +87,5 @@ fn test_variant_call_args() -> bool { fn call_i64(variant: &mut Variant, method: &str, args: &[Variant]) -> Option { let result = unsafe { variant.call(method, args) }; - result.unwrap().try_to_i64() + result.unwrap().to() } From 9f434d05bc557a96b6023c4f41b67fc11867e9e9 Mon Sep 17 00:00:00 2001 From: Chitose Yuuzaki Date: Sun, 14 Nov 2021 12:59:22 +0000 Subject: [PATCH 2/2] Add error reminders to array_debug test --- gdnative-core/src/core_types/variant_array.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gdnative-core/src/core_types/variant_array.rs b/gdnative-core/src/core_types/variant_array.rs index 5442e9be0..5b626d30e 100644 --- a/gdnative-core/src/core_types/variant_array.rs +++ b/gdnative-core/src/core_types/variant_array.rs @@ -616,6 +616,9 @@ godot_test!( test_array_debug { use std::panic::catch_unwind; + println!(" -- expected 4 'Index 3 out of bounds (len 3)' error messages for edge cases"); + println!(" -- the test is successful when and only when these errors are shown"); + let arr = VariantArray::new(); // [] arr.push(&Variant::new("hello world")); arr.push(&Variant::new(true));