use crate::*;
use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::default::Default;
use std::fmt;
use std::hash::Hash;
use std::mem::{forget, transmute};
use std::ptr;
use crate::core_types::*;
use crate::object::ownership::*;
use crate::object::*;
use crate::private::{get_api, ManuallyManagedClassPlaceholder};
#[cfg(feature = "serde")]
mod serialize;
pub struct Variant(pub(crate) sys::godot_variant);
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)(&v.0))
}
}
}
};
(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! impl_coerce_from_variant {
(
$(
impl CoerceFromVariant for $type:path = $kind:ident ($to_gd_method:ident);
)*
) => {
$(
impl_coerce_from_variant_inner!(impl CoerceFromVariant for $type = $kind($to_gd_method));
)*
}
}
macro_rules! variant_dispatch_arm {
($v:expr, $variant:ident ( $inner:ty )) => {
VariantDispatch::$variant(<$inner>::from_variant($v).unwrap())
};
($v:expr, $variant:ident) => {
VariantDispatch::$variant
};
}
macro_rules! decl_variant_type {
(
pub enum VariantType, VariantDispatch {
$(
$variant:ident $( ($inner:ty) )? = $c_const:path,
)*
}
) => {
#[repr(u32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum VariantType {
$(
$variant = $c_const as u32,
)*
}
impl VariantType {
pub const NAMES: &'static [&'static str] = &[
$(stringify!($variant),)*
];
}
#[repr(u32)]
pub enum VariantDispatch {
$(
$variant $( ($inner) )?,
)*
}
impl<'a> From<&'a Variant> for VariantDispatch {
#[inline]
fn from(v: &'a Variant) -> Self {
match v.get_type() {
$(
VariantType::$variant => {
variant_dispatch_arm!(v, $variant $( ($inner) )?)
},
)*
}
}
}
impl<'a> From<&'a VariantDispatch> for Variant {
#[inline]
fn from(v: &'a VariantDispatch) -> Self {
match v {
$($(VariantDispatch::$variant(v) => {
let v: &$inner = v;
v.to_variant()
})?)*
_ => Variant::nil()
}
}
}
}
}
decl_variant_type!(
pub enum VariantType, VariantDispatch {
Nil = sys::godot_variant_type_GODOT_VARIANT_TYPE_NIL,
Bool(bool) = sys::godot_variant_type_GODOT_VARIANT_TYPE_BOOL,
I64(i64) = sys::godot_variant_type_GODOT_VARIANT_TYPE_INT,
F64(f64) = sys::godot_variant_type_GODOT_VARIANT_TYPE_REAL,
GodotString(GodotString) = sys::godot_variant_type_GODOT_VARIANT_TYPE_STRING,
Vector2(Vector2) = sys::godot_variant_type_GODOT_VARIANT_TYPE_VECTOR2,
Rect2(Rect2) = sys::godot_variant_type_GODOT_VARIANT_TYPE_RECT2,
Vector3(Vector3) = sys::godot_variant_type_GODOT_VARIANT_TYPE_VECTOR3,
Transform2D(Transform2D) = sys::godot_variant_type_GODOT_VARIANT_TYPE_TRANSFORM2D,
Plane(Plane) = sys::godot_variant_type_GODOT_VARIANT_TYPE_PLANE,
Quat(Quat) = sys::godot_variant_type_GODOT_VARIANT_TYPE_QUAT,
Aabb(Aabb) = sys::godot_variant_type_GODOT_VARIANT_TYPE_AABB,
Basis(Basis) = sys::godot_variant_type_GODOT_VARIANT_TYPE_BASIS,
Transform(Transform) = sys::godot_variant_type_GODOT_VARIANT_TYPE_TRANSFORM,
Color(Color) = sys::godot_variant_type_GODOT_VARIANT_TYPE_COLOR,
NodePath(NodePath) = sys::godot_variant_type_GODOT_VARIANT_TYPE_NODE_PATH,
Rid(Rid) = sys::godot_variant_type_GODOT_VARIANT_TYPE_RID,
Object(Variant) = sys::godot_variant_type_GODOT_VARIANT_TYPE_OBJECT,
Dictionary(Dictionary) = sys::godot_variant_type_GODOT_VARIANT_TYPE_DICTIONARY,
VariantArray(VariantArray) = sys::godot_variant_type_GODOT_VARIANT_TYPE_ARRAY,
ByteArray(PoolArray<u8>) = sys::godot_variant_type_GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY,
Int32Array(PoolArray<i32>) = sys::godot_variant_type_GODOT_VARIANT_TYPE_POOL_INT_ARRAY,
Float32Array(PoolArray<f32>) = sys::godot_variant_type_GODOT_VARIANT_TYPE_POOL_REAL_ARRAY,
StringArray(PoolArray<GodotString>) = sys::godot_variant_type_GODOT_VARIANT_TYPE_POOL_STRING_ARRAY,
Vector2Array(PoolArray<Vector2>) = sys::godot_variant_type_GODOT_VARIANT_TYPE_POOL_VECTOR2_ARRAY,
Vector3Array(PoolArray<Vector3>) = sys::godot_variant_type_GODOT_VARIANT_TYPE_POOL_VECTOR3_ARRAY,
ColorArray(PoolArray<Color>) = sys::godot_variant_type_GODOT_VARIANT_TYPE_POOL_COLOR_ARRAY,
}
);
impl VariantType {
#[allow(clippy::unnecessary_cast)] #[doc(hidden)]
#[inline]
pub fn from_sys(v: sys::godot_variant_type) -> VariantType {
unsafe { transmute(v as u32) }
}
#[inline]
pub const fn name(self) -> &'static str {
Self::NAMES[self as usize]
}
}
#[allow(clippy::unnecessary_cast)] #[repr(u32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum CallError {
InvalidMethod =
sys::godot_variant_call_error_error_GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD as u32,
InvalidArgument =
sys::godot_variant_call_error_error_GODOT_CALL_ERROR_CALL_ERROR_INVALID_ARGUMENT as u32,
TooManyArguments =
sys::godot_variant_call_error_error_GODOT_CALL_ERROR_CALL_ERROR_TOO_MANY_ARGUMENTS as u32,
TooFewArguments =
sys::godot_variant_call_error_error_GODOT_CALL_ERROR_CALL_ERROR_TOO_FEW_ARGUMENTS as u32,
InstanceIsNull =
sys::godot_variant_call_error_error_GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL as u32,
}
impl CallError {
#[allow(clippy::unnecessary_cast)] #[inline]
fn from_sys(v: sys::godot_variant_call_error_error) -> Result<(), CallError> {
if v == sys::godot_variant_call_error_error_GODOT_CALL_ERROR_CALL_OK {
Ok(())
} else {
debug_assert!(
(v as u32) <= sys::godot_variant_call_error_error_GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL as u32,
"Godot should have passed a known error",
);
Err(unsafe { transmute(v as u32) })
}
}
}
impl std::fmt::Display for CallError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use CallError::*;
match self {
InvalidMethod => write!(f, "invalid method"),
InvalidArgument => write!(f, "invalid argument"),
TooManyArguments => write!(f, "too many arguments"),
TooFewArguments => write!(f, "too few arguments"),
InstanceIsNull => write!(f, "instance is null"),
}
}
}
impl std::error::Error for CallError {}
#[allow(clippy::unnecessary_cast)] #[repr(u32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum VariantOperator {
Equal = sys::godot_variant_operator_GODOT_VARIANT_OP_EQUAL as u32,
NotEqual = sys::godot_variant_operator_GODOT_VARIANT_OP_NOT_EQUAL as u32,
Less = sys::godot_variant_operator_GODOT_VARIANT_OP_LESS as u32,
LessEqual = sys::godot_variant_operator_GODOT_VARIANT_OP_LESS_EQUAL as u32,
Greater = sys::godot_variant_operator_GODOT_VARIANT_OP_GREATER as u32,
GreaterEqual = sys::godot_variant_operator_GODOT_VARIANT_OP_GREATER_EQUAL as u32,
Add = sys::godot_variant_operator_GODOT_VARIANT_OP_ADD as u32,
Subtract = sys::godot_variant_operator_GODOT_VARIANT_OP_SUBTRACT as u32,
Multiply = sys::godot_variant_operator_GODOT_VARIANT_OP_MULTIPLY as u32,
Divide = sys::godot_variant_operator_GODOT_VARIANT_OP_DIVIDE as u32,
Negate = sys::godot_variant_operator_GODOT_VARIANT_OP_NEGATE as u32,
Positive = sys::godot_variant_operator_GODOT_VARIANT_OP_POSITIVE as u32,
Module = sys::godot_variant_operator_GODOT_VARIANT_OP_MODULE as u32,
StringConcat = sys::godot_variant_operator_GODOT_VARIANT_OP_STRING_CONCAT as u32,
ShiftLeft = sys::godot_variant_operator_GODOT_VARIANT_OP_SHIFT_LEFT as u32,
ShiftRight = sys::godot_variant_operator_GODOT_VARIANT_OP_SHIFT_RIGHT as u32,
BitAnd = sys::godot_variant_operator_GODOT_VARIANT_OP_BIT_AND as u32,
BitOr = sys::godot_variant_operator_GODOT_VARIANT_OP_BIT_OR as u32,
BitXor = sys::godot_variant_operator_GODOT_VARIANT_OP_BIT_XOR as u32,
BitNegate = sys::godot_variant_operator_GODOT_VARIANT_OP_BIT_NEGATE as u32,
And = sys::godot_variant_operator_GODOT_VARIANT_OP_AND as u32,
Or = sys::godot_variant_operator_GODOT_VARIANT_OP_OR as u32,
Xor = sys::godot_variant_operator_GODOT_VARIANT_OP_XOR as u32,
Not = sys::godot_variant_operator_GODOT_VARIANT_OP_NOT as u32,
In = sys::godot_variant_operator_GODOT_VARIANT_OP_IN as u32,
}
#[allow(clippy::unnecessary_cast)] impl VariantOperator {
const MAX: u32 = sys::godot_variant_operator_GODOT_VARIANT_OP_MAX as u32;
#[doc(hidden)]
#[inline]
pub fn to_sys(self) -> sys::godot_variant_operator {
self as u32 as sys::godot_variant_operator
}
#[doc(hidden)]
#[inline]
pub fn try_from_sys(op: sys::godot_variant_operator) -> Option<Self> {
let op = op as u32;
if op >= Self::MAX {
return None;
}
unsafe { std::mem::transmute(op) }
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Default, Debug)]
pub struct InvalidOp;
type Bool = bool;
impl Variant {
#[inline]
pub fn new<T: OwnedToVariant>(from: T) -> Self {
from.owned_to_variant()
}
#[inline]
pub fn nil() -> Self {
unsafe {
let api = get_api();
let mut dest = sys::godot_variant::default();
(api.godot_variant_new_nil)(&mut dest);
Variant(dest)
}
}
#[doc(hidden)]
#[inline]
pub unsafe fn from_object_ptr(val: *mut sys::godot_object) -> Variant {
let api = get_api();
let mut dest = sys::godot_variant::default();
(api.godot_variant_new_object)(&mut dest, val);
Variant(dest)
}
#[inline]
fn try_as_sys_of_type(
&self,
expected: VariantType,
) -> Result<&sys::godot_variant, FromVariantError> {
let variant_type = self.get_type();
if variant_type != expected {
return Err(FromVariantError::InvalidVariantType {
expected,
variant_type,
});
}
Ok(&self.0)
}
#[inline]
pub fn to<T: FromVariant>(&self) -> Option<T> {
self.try_to().ok()
}
#[inline]
pub fn try_to<T: FromVariant>(&self) -> Result<T, FromVariantError> {
T::from_variant(self)
}
#[inline]
pub fn coerce_to<T: CoerceFromVariant>(&self) -> T {
T::coerce_from_variant(self)
}
#[inline]
pub fn to_object<T>(&self) -> Option<Ref<T, Shared>>
where
T: GodotObject,
{
self.try_to_object::<T>().ok()
}
#[inline]
pub fn try_to_object<T>(&self) -> Result<Ref<T, Shared>, FromVariantError>
where
T: GodotObject,
{
unsafe {
let api = get_api();
let obj = self.try_as_sys_of_type(VariantType::Object)?;
let obj = ptr::NonNull::new((api.godot_variant_as_object)(obj))
.ok_or(FromVariantError::InvalidNil)?;
let obj =
object::RawObject::<ManuallyManagedClassPlaceholder>::from_sys_ref_unchecked(obj);
let obj = obj
.cast::<T>()
.ok_or_else(|| FromVariantError::CannotCast {
class: obj.class_name(),
to: T::class_name(),
})?;
Ok(Ref::from_sys(obj.sys()))
}
}
#[inline]
pub fn get_type(&self) -> VariantType {
unsafe { VariantType::from_sys((get_api().godot_variant_get_type)(&self.0)) }
}
#[inline]
pub fn dispatch(&self) -> VariantDispatch {
self.into()
}
#[inline]
pub fn is_nil(&self) -> bool {
match self.get_type() {
VariantType::Nil => true,
VariantType::Object => {
let ptr = unsafe { (get_api().godot_variant_as_object)(&self.0) };
ptr.is_null()
}
_ => false,
}
}
#[inline]
pub fn has_method(&self, method: impl Into<GodotString>) -> bool {
let method = method.into();
unsafe { (get_api().godot_variant_has_method)(&self.0, &method.0) }
}
#[inline]
pub unsafe fn call(
&mut self,
method: impl Into<GodotString>,
args: &[Variant],
) -> Result<Variant, CallError> {
let method = method.into();
let api = get_api();
let mut err = sys::godot_variant_call_error::default();
let mut arg_refs = args.iter().map(Variant::sys).collect::<Vec<_>>();
let variant = (api.godot_variant_call)(
&mut self.0,
&method.0,
arg_refs.as_mut_ptr(),
args.len() as i32,
&mut err,
);
CallError::from_sys(err.error).map(|_| Variant::from_sys(variant))
}
#[inline]
pub fn evaluate(&self, op: VariantOperator, rhs: &Self) -> Result<Variant, InvalidOp> {
unsafe {
let api = get_api();
let mut ret = Variant::nil();
let mut valid = false;
(api.godot_variant_evaluate)(
op.to_sys(),
self.sys(),
rhs.sys(),
ret.sys_mut(),
&mut valid,
);
if valid {
Ok(ret)
} else {
Err(InvalidOp)
}
}
}
#[inline]
pub(crate) unsafe fn cast_ref<'l>(ptr: *const sys::godot_variant) -> &'l Variant {
&*(ptr as *const variant::Variant)
}
#[inline]
pub(crate) fn cast_mut_ref<'l>(ptr: *mut sys::godot_variant) -> &'l mut Variant {
unsafe { &mut *(ptr as *mut variant::Variant) }
}
#[inline]
#[doc(hidden)]
pub fn leak(self) -> sys::godot_variant {
let v = self.0;
forget(self);
v
}
#[doc(hidden)]
#[inline]
pub fn to_sys(&self) -> sys::godot_variant {
self.0
}
#[doc(hidden)]
#[inline]
pub fn sys(&self) -> *const sys::godot_variant {
&self.0
}
#[doc(hidden)]
#[inline]
pub fn sys_mut(&mut self) -> *mut sys::godot_variant {
&mut self.0
}
#[doc(hidden)]
#[inline]
pub fn from_sys(sys: sys::godot_variant) -> Self {
Variant(sys)
}
}
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<Shared> = from_sys(godot_variant_as_array);
impl CoerceFromVariant for PoolArray<u8> = from_sys(godot_variant_as_pool_byte_array);
impl CoerceFromVariant for PoolArray<i32> = from_sys(godot_variant_as_pool_int_array);
impl CoerceFromVariant for PoolArray<f32> = from_sys(godot_variant_as_pool_real_array);
impl CoerceFromVariant for PoolArray<GodotString> = from_sys(godot_variant_as_pool_string_array);
impl CoerceFromVariant for PoolArray<Vector2> = from_sys(godot_variant_as_pool_vector2_array);
impl CoerceFromVariant for PoolArray<Vector3> = from_sys(godot_variant_as_pool_vector3_array);
impl CoerceFromVariant for PoolArray<Color> = from_sys(godot_variant_as_pool_color_array);
impl CoerceFromVariant for Dictionary<Shared> = from_sys(godot_variant_as_dictionary);
);
impl_basic_traits_as_sys!(
for Variant as godot_variant {
Drop => godot_variant_destroy;
Clone => godot_variant_new_copy;
PartialEq => godot_variant_operator_equal;
Ord => godot_variant_operator_less;
}
);
impl Eq for Variant {}
impl fmt::Display for Variant {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.coerce_to::<GodotString>().fmt(f)
}
}
impl Default for Variant {
#[inline]
fn default() -> Self {
Variant::nil()
}
}
impl fmt::Debug for Variant {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "{:?}({})", self.get_type(), self)
}
}
godot_test!(
test_variant_nil {
let nil = Variant::nil();
assert!(nil.is_nil());
assert!(nil.try_to::<VariantArray>().is_err());
assert!(nil.try_to::<Rid>().is_err());
assert!(nil.try_to::<i64>().is_err());
assert!(nil.try_to::<bool>().is_err());
assert!(nil.try_to::<Aabb>().is_err());
assert!(nil.try_to::<Vector2>().is_err());
assert!(nil.try_to::<Basis>().is_err());
assert!(!nil.has_method("foo"));
let clone = nil.clone();
assert!(clone == nil);
}
test_variant_i64 {
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>(), Ok(42));
assert!(v_42.try_to::<f64>().is_err());
assert!(v_42.try_to::<VariantArray>().is_err());
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>(), Ok(-1));
assert!(v_m1.try_to::<f64>().is_err());
assert!(v_m1.try_to::<VariantArray>().is_err());
}
test_variant_bool {
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>(), Ok(true));
assert!(v_true.try_to::<f64>().is_err());
assert!(v_true.try_to::<VariantArray>().is_err());
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>(), Ok(false));
assert!(v_false.try_to::<f64>().is_err());
assert!(v_false.try_to::<VariantArray>().is_err());
}
);
pub trait ToVariant {
fn to_variant(&self) -> Variant;
}
pub trait OwnedToVariant {
fn owned_to_variant(self) -> Variant;
}
pub trait ToVariantEq: Eq {}
pub trait FromVariant: Sized {
fn from_variant(variant: &Variant) -> Result<Self, FromVariantError>;
}
pub trait CoerceFromVariant: Sized + private::Sealed {
fn coerce_from_variant(variant: &Variant) -> Self;
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum FromVariantError {
Unspecified,
Custom(String),
InvalidNil,
InvalidVariantType {
variant_type: VariantType,
expected: VariantType,
},
CannotCast { class: String, to: &'static str },
InvalidLength { len: usize, expected: usize },
InvalidEnumRepr {
expected: VariantEnumRepr,
error: Box<FromVariantError>,
},
InvalidStructRepr {
expected: VariantStructRepr,
error: Box<FromVariantError>,
},
UnknownEnumVariant {
variant: String,
expected: &'static [&'static str],
},
InvalidEnumVariant {
variant: &'static str,
error: Box<FromVariantError>,
},
InvalidInstance { expected: Cow<'static, str> },
InvalidField {
field_name: &'static str,
error: Box<FromVariantError>,
},
InvalidItem {
index: usize,
error: Box<FromVariantError>,
},
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum VariantEnumRepr {
ExternallyTagged,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum VariantStructRepr {
Unit,
Tuple,
Struct,
}
impl FromVariantError {
#[inline]
pub fn custom<T: fmt::Display>(message: T) -> Self {
FromVariantError::Custom(format!("{message}"))
}
}
impl fmt::Display for FromVariantError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use FromVariantError as E;
match self {
E::Unspecified => write!(f, "unspecified error"),
E::Custom(s) => write!(f, "{s}"),
E::InvalidNil => write!(f, "expected non-nullable type, got null"),
E::InvalidVariantType {
variant_type,
expected,
} => write!(
f,
"invalid variant type: expected {expected:?}, got {variant_type:?}"
),
E::CannotCast { class, to } => {
write!(f, "cannot cast object of class {class} to {to}")
}
E::InvalidLength { len, expected } => {
write!(f, "expected collection of length {expected}, got {len}")
}
E::InvalidEnumRepr { expected, error } => write!(
f,
"invalid enum representation: expected {expected:?}, {error}"
),
E::InvalidStructRepr { expected, error } => write!(
f,
"invalid struct representation: expected {expected:?}, {error}"
),
E::UnknownEnumVariant { variant, expected } => {
write!(f, "unknown enum variant {variant}, expected variants are: ")?;
let mut first = true;
for v in *expected {
if first {
first = false;
} else {
write!(f, ", ")?;
}
write!(f, "{v}")?;
}
Ok(())
}
E::InvalidEnumVariant { variant, error } => {
write!(f, "invalid value for variant {variant}: {error}")
}
E::InvalidInstance { expected } => {
write!(f, "object is not an instance of `NativeClass` {expected}")
}
E::InvalidField { field_name, error } => {
write!(f, "invalid value for field {field_name}")?;
let mut next_error = error.as_ref();
loop {
match next_error {
E::InvalidField { field_name, error } => {
write!(f, ".{field_name}")?;
next_error = error.as_ref();
}
E::InvalidItem { index, error } => {
write!(f, "[{index}]")?;
next_error = error.as_ref();
}
_ => {
write!(f, ": {next_error}")?;
return Ok(());
}
}
}
}
E::InvalidItem { index, error } => {
write!(f, "invalid value for item at index {index}: {error}")
}
}
}
}
impl std::error::Error for FromVariantError {}
impl<T: ToVariant> OwnedToVariant for T {
#[inline]
fn owned_to_variant(self) -> Variant {
self.to_variant()
}
}
impl ToVariant for () {
#[inline]
fn to_variant(&self) -> Variant {
Variant::nil()
}
}
impl ToVariantEq for () {}
impl FromVariant for () {
#[inline]
fn from_variant(variant: &Variant) -> Result<Self, FromVariantError> {
if variant.is_nil() {
return Ok(());
}
Err(FromVariantError::InvalidVariantType {
variant_type: variant.get_type(),
expected: VariantType::Nil,
})
}
}
impl<'a, T> ToVariant for &'a T
where
T: ToVariant + ?Sized,
{
#[inline]
fn to_variant(&self) -> Variant {
T::to_variant(*self)
}
}
impl<'a, T> ToVariantEq for &'a T where T: ToVariantEq + ?Sized {}
impl ToVariantEq for Variant {}
impl<'a, T> ToVariant for &'a mut T
where
T: ToVariant + ?Sized,
{
#[inline]
fn to_variant(&self) -> Variant {
T::to_variant(*self)
}
}
impl<'a, T> ToVariantEq for &'a mut T where T: ToVariantEq + ?Sized {}
impl<T: GodotObject> ToVariant for Ref<T, Shared> {
#[inline]
fn to_variant(&self) -> Variant {
unsafe { Variant::from_object_ptr(self.as_ptr()) }
}
}
impl<T: GodotObject> OwnedToVariant for Ref<T, Unique> {
#[inline]
fn owned_to_variant(self) -> Variant {
unsafe { Variant::from_object_ptr(self.as_ptr()) }
}
}
impl<'a, T: GodotObject> ToVariant for TRef<'a, T, Shared> {
#[inline]
fn to_variant(&self) -> Variant {
unsafe { Variant::from_object_ptr(self.as_ptr()) }
}
}
impl<T: GodotObject> FromVariant for Ref<T, Shared> {
#[inline]
fn from_variant(variant: &Variant) -> Result<Self, FromVariantError> {
variant.try_to_object::<T>()
}
}
macro_rules! from_variant_direct {
(
$(
impl FromVariant for $TryType:ident : VariantType :: $VarType:ident => $try_gd_method:ident;
)*
) => (
$(
impl FromVariant for $TryType {
#[inline]
fn from_variant(variant: &Variant) -> Result<Self, FromVariantError> {
variant.try_as_sys_of_type(VariantType::$VarType)
.map(|v| unsafe { (get_api().$try_gd_method)(v) })
}
}
)*
);
}
from_variant_direct!(
impl FromVariant for f64 : VariantType::F64 => godot_variant_as_real;
impl FromVariant for i64 : VariantType::I64 => godot_variant_as_int;
impl FromVariant for u64 : VariantType::I64 => godot_variant_as_uint;
impl FromVariant for bool : VariantType::Bool => godot_variant_as_bool;
);
impl ToVariant for i64 {
#[inline]
fn to_variant(&self) -> Variant {
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 {}
impl ToVariant for u64 {
#[inline]
fn to_variant(&self) -> Variant {
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 {
unsafe {
let api = get_api();
let mut ret = sys::godot_variant::default();
(api.godot_variant_new_real)(&mut ret, *self);
Variant(ret)
}
}
}
macro_rules! impl_to_variant_for_num {
(
$($ty:ty : $src_ty:ty)*
) => {
$(
impl ToVariant for $ty {
#[inline]
fn to_variant(&self) -> Variant {
((*self) as $src_ty).to_variant()
}
}
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<Self, FromVariantError> {
<$src_ty>::from_variant(variant).map(|i| i as Self)
}
}
)*
};
}
impl_to_variant_for_num!(
i8: i64
i16: i64
i32: i64
isize: i64
u8: u64
u16: u64
u32: u64
usize: u64
f32: f64
);
impl ToVariantEq for i8 {}
impl ToVariantEq for i16 {}
impl ToVariantEq for i32 {}
impl ToVariantEq for isize {}
impl ToVariantEq for u8 {}
impl ToVariantEq for u16 {}
impl ToVariantEq for u32 {}
impl ToVariantEq for usize {}
macro_rules! to_variant_transmute {
(
$(impl ToVariant for $ty:ident: $ctor:ident;)*
) => {
$(
impl ToVariant for $ty {
#[inline]
fn to_variant(&self) -> Variant {
unsafe {
let api = get_api();
let mut dest = sys::godot_variant::default();
#[allow(clippy::useless_transmute)]
(api.$ctor)(&mut dest, transmute(self));
Variant::from_sys(dest)
}
}
}
)*
}
}
to_variant_transmute! {
impl ToVariant for Vector2 : godot_variant_new_vector2;
impl ToVariant for Vector3 : godot_variant_new_vector3;
impl ToVariant for Quat : godot_variant_new_quat;
impl ToVariant for Rect2 : godot_variant_new_rect2;
impl ToVariant for Transform2D : godot_variant_new_transform2d;
}
macro_rules! to_variant_as_sys {
(
$(impl ToVariant for $ty:ty: $ctor:ident;)*
) => {
$(
impl ToVariant for $ty {
#[inline]
fn to_variant(&self) -> Variant {
unsafe {
let api = get_api();
let mut dest = sys::godot_variant::default();
(api.$ctor)(&mut dest, self.sys());
Variant::from_sys(dest)
}
}
}
)*
}
}
to_variant_as_sys! {
impl ToVariant for Plane : godot_variant_new_plane;
impl ToVariant for Transform : godot_variant_new_transform;
impl ToVariant for Basis : godot_variant_new_basis;
impl ToVariant for Color : godot_variant_new_color;
impl ToVariant for Aabb : godot_variant_new_aabb;
impl ToVariant for Rid : godot_variant_new_rid;
impl ToVariant for NodePath : godot_variant_new_node_path;
impl ToVariant for GodotString : godot_variant_new_string;
impl ToVariant for VariantArray<Shared> : godot_variant_new_array;
impl ToVariant for Dictionary<Shared> : godot_variant_new_dictionary;
}
impl ToVariantEq for Rid {}
impl ToVariantEq for NodePath {}
impl ToVariantEq for GodotString {}
impl OwnedToVariant for Dictionary<Unique> {
#[inline]
fn owned_to_variant(self) -> Variant {
self.into_shared().to_variant()
}
}
impl OwnedToVariant for VariantArray<Unique> {
#[inline]
fn owned_to_variant(self) -> Variant {
self.into_shared().to_variant()
}
}
macro_rules! from_variant_transmute {
(
$(
impl FromVariant for $TryType:ident : $try_gd_method:ident;
)*
) => (
$(
impl FromVariant for $TryType {
#[inline]
fn from_variant(variant: &Variant) -> Result<Self, FromVariantError> {
unsafe {
variant.try_as_sys_of_type(VariantType::$TryType)
.map(|v| (get_api().$try_gd_method)(v))
.map(|v| transmute(v))
}
}
}
)*
);
}
from_variant_transmute!(
impl FromVariant for Vector2 : godot_variant_as_vector2;
impl FromVariant for Vector3 : godot_variant_as_vector3;
impl FromVariant for Quat : godot_variant_as_quat;
impl FromVariant for Rect2 : godot_variant_as_rect2;
impl FromVariant for Transform2D : godot_variant_as_transform2d;
);
macro_rules! from_variant_from_sys {
(
$(
impl FromVariant for $TryType:ty as $EnumVar:ident : $try_gd_method:ident;
)*
) => (
$(
impl FromVariant for $TryType {
#[inline]
fn from_variant(variant: &Variant) -> Result<Self, FromVariantError> {
unsafe {
variant.try_as_sys_of_type(VariantType::$EnumVar)
.map(|v| (get_api().$try_gd_method)(v))
.map(<$TryType>::from_sys)
}
}
}
)*
);
}
from_variant_from_sys!(
impl FromVariant for Plane as Plane : godot_variant_as_plane;
impl FromVariant for Transform as Transform : godot_variant_as_transform;
impl FromVariant for Basis as Basis : godot_variant_as_basis;
impl FromVariant for Color as Color : godot_variant_as_color;
impl FromVariant for Aabb as Aabb : godot_variant_as_aabb;
impl FromVariant for NodePath as NodePath : godot_variant_as_node_path;
impl FromVariant for GodotString as GodotString: godot_variant_as_string;
impl FromVariant for Rid as Rid : godot_variant_as_rid;
impl FromVariant for VariantArray<Shared> as VariantArray : godot_variant_as_array;
impl FromVariant for Dictionary<Shared> as Dictionary : godot_variant_as_dictionary;
);
impl<T: crate::core_types::PoolElement> ToVariant for PoolArray<T> {
#[inline]
fn to_variant(&self) -> Variant {
unsafe {
let api = get_api();
let mut dest = sys::godot_variant::default();
(T::array_to_variant_fn(api))(&mut dest, self.sys());
Variant::from_sys(dest)
}
}
}
impl<T: crate::core_types::PoolElement + Eq> ToVariantEq for PoolArray<T> {}
impl<T: crate::core_types::PoolElement> FromVariant for PoolArray<T> {
#[inline]
fn from_variant(variant: &Variant) -> Result<Self, FromVariantError> {
unsafe {
variant
.try_as_sys_of_type(VariantType::from_sys(T::SYS_VARIANT_TYPE))
.map(|v| (T::array_from_variant_fn(get_api()))(v))
.map(Self::from_sys)
}
}
}
impl ToVariant for str {
#[inline]
fn to_variant(&self) -> Variant {
GodotString::from_str(self).owned_to_variant()
}
}
impl ToVariantEq for str {}
impl ToVariant for String {
#[inline]
fn to_variant(&self) -> Variant {
self.as_str().to_variant()
}
}
impl ToVariantEq for String {}
impl FromVariant for String {
#[inline]
fn from_variant(variant: &Variant) -> Result<Self, FromVariantError> {
GodotString::from_variant(variant).map(|s| s.to_string())
}
}
impl ToVariant for Variant {
#[inline]
fn to_variant(&self) -> Variant {
self.clone()
}
}
impl FromVariant for Variant {
#[inline]
fn from_variant(variant: &Variant) -> Result<Self, FromVariantError> {
Ok(variant.clone())
}
}
impl<T> ToVariant for std::marker::PhantomData<T> {
#[inline]
fn to_variant(&self) -> Variant {
Variant::nil()
}
}
impl<T> ToVariantEq for std::marker::PhantomData<T> {}
impl<T> FromVariant for std::marker::PhantomData<T> {
#[inline]
fn from_variant(variant: &Variant) -> Result<Self, FromVariantError> {
if variant.is_nil() {
return Ok(std::marker::PhantomData);
}
Err(FromVariantError::InvalidVariantType {
variant_type: variant.get_type(),
expected: VariantType::Nil,
})
}
}
impl<T: ToVariant> ToVariant for Option<T> {
#[inline]
fn to_variant(&self) -> Variant {
match &self {
Some(thing) => thing.to_variant(),
None => Variant::nil(),
}
}
}
impl<T: ToVariantEq> ToVariantEq for Option<T> {}
impl<T: FromVariant> FromVariant for Option<T> {
#[inline]
fn from_variant(variant: &Variant) -> Result<Self, FromVariantError> {
T::from_variant(variant).map(Some).or_else(
|e| {
if variant.is_nil() {
Ok(None)
} else {
Err(e)
}
},
)
}
}
#[derive(Clone, Debug)]
pub struct MaybeNot<T>(Result<T, Variant>);
impl<T: FromVariant> FromVariant for MaybeNot<T> {
#[inline]
fn from_variant(variant: &Variant) -> Result<Self, FromVariantError> {
Ok(MaybeNot(
T::from_variant(variant).map_err(|_| variant.clone()),
))
}
}
impl<T> MaybeNot<T> {
#[inline]
pub fn into_result(self) -> Result<T, Variant> {
self.0
}
#[inline]
pub fn as_ref(&self) -> Result<&T, &Variant> {
self.0.as_ref()
}
#[inline]
pub fn as_mut(&mut self) -> Result<&mut T, &mut Variant> {
self.0.as_mut()
}
#[inline]
pub fn cloned(&self) -> Result<T, Variant>
where
T: Clone,
{
self.0.clone()
}
#[inline]
pub fn ok(self) -> Option<T> {
self.0.ok()
}
}
impl<T: ToVariant, E: ToVariant> ToVariant for Result<T, E> {
#[inline]
fn to_variant(&self) -> Variant {
let dict = Dictionary::new();
match &self {
Ok(val) => dict.insert("Ok", val),
Err(err) => dict.insert("Err", err),
}
dict.into_shared().to_variant()
}
}
impl<T: FromVariant, E: FromVariant> FromVariant for Result<T, E> {
#[inline]
fn from_variant(variant: &Variant) -> Result<Self, FromVariantError> {
use FromVariantError as FVE;
let dict = Dictionary::from_variant(variant).map_err(|err| FVE::InvalidEnumRepr {
expected: VariantEnumRepr::ExternallyTagged,
error: Box::new(err),
})?;
if dict.len() != 1 {
return Err(FVE::InvalidEnumRepr {
expected: VariantEnumRepr::ExternallyTagged,
error: Box::new(FVE::InvalidLength {
expected: 1,
len: dict.len() as usize,
}),
});
}
let keys = dict.keys();
let key_variant = &keys.get(0);
let key = String::from_variant(key_variant).map_err(|err| FVE::InvalidEnumRepr {
expected: VariantEnumRepr::ExternallyTagged,
error: Box::new(err),
})?;
match key.as_str() {
"Ok" => {
let val = T::from_variant(&dict.get_or_nil(key_variant)).map_err(|err| {
FVE::InvalidEnumVariant {
variant: "Ok",
error: Box::new(err),
}
})?;
Ok(Ok(val))
}
"Err" => {
let err = E::from_variant(&dict.get_or_nil(key_variant)).map_err(|err| {
FVE::InvalidEnumVariant {
variant: "Err",
error: Box::new(err),
}
})?;
Ok(Err(err))
}
variant => Err(FVE::UnknownEnumVariant {
variant: variant.to_string(),
expected: &["Ok", "Err"],
}),
}
}
}
impl<T: ToVariant> ToVariant for &[T] {
#[inline]
fn to_variant(&self) -> Variant {
let array = VariantArray::new();
for val in self.iter() {
array.push(&val.to_variant());
}
array.into_shared().to_variant()
}
}
impl<T: ToVariant> ToVariant for Vec<T> {
#[inline]
fn to_variant(&self) -> Variant {
self.as_slice().to_variant()
}
}
impl<T: FromVariant> FromVariant for Vec<T> {
#[inline]
fn from_variant(variant: &Variant) -> Result<Self, FromVariantError> {
let arr = VariantArray::from_variant(variant)?;
let len: usize = arr
.len()
.try_into()
.expect("variant array length should fit in usize");
let mut vec = Vec::with_capacity(len);
for idx in 0..len as i32 {
let item =
T::from_variant(&arr.get(idx)).map_err(|e| FromVariantError::InvalidItem {
index: idx as usize,
error: Box::new(e),
})?;
vec.push(item);
}
Ok(vec)
}
}
impl<K: ToVariant + Hash + ToVariantEq, V: ToVariant> ToVariant for HashMap<K, V> {
#[inline]
fn to_variant(&self) -> Variant {
let mut intermediate: Vec<(Variant, Variant)> = self
.iter()
.map(|(k, v)| (k.to_variant(), v.to_variant()))
.collect();
intermediate.sort();
let dict = Dictionary::new();
for (key, value) in intermediate.into_iter() {
dict.insert(key, value);
}
dict.owned_to_variant()
}
}
impl<K: FromVariant + Hash + Eq, V: FromVariant> FromVariant for HashMap<K, V> {
#[inline]
fn from_variant(variant: &Variant) -> Result<Self, FromVariantError> {
let dictionary = Dictionary::from_variant(variant)?;
let len: usize = dictionary
.len()
.try_into()
.expect("Dictionary length should fit in usize");
let mut hash_map = HashMap::with_capacity(len);
for (key, value) in dictionary.iter() {
hash_map.insert(K::from_variant(&key)?, V::from_variant(&value)?);
}
Ok(hash_map)
}
}
impl<T: ToVariant> ToVariant for HashSet<T> {
#[inline]
fn to_variant(&self) -> Variant {
let array = VariantArray::new();
for value in self {
array.push(value.to_variant());
}
array.sort(); array.owned_to_variant()
}
}
impl<T: FromVariant + Eq + Hash> FromVariant for HashSet<T> {
#[inline]
fn from_variant(variant: &Variant) -> Result<Self, FromVariantError> {
let arr = VariantArray::from_variant(variant)?;
let len: usize = arr
.len()
.try_into()
.expect("VariantArray length should fit in usize");
let mut set = HashSet::with_capacity(len);
for idx in 0..len as i32 {
let item =
T::from_variant(&arr.get(idx)).map_err(|e| FromVariantError::InvalidItem {
index: idx as usize,
error: Box::new(e),
})?;
set.insert(item);
}
Ok(set)
}
}
macro_rules! tuple_length {
() => { 0usize };
($_x:ident, $($xs:ident,)*) => {
1usize + tuple_length!($($xs,)*)
};
}
macro_rules! impl_variant_for_tuples_next {
($_x:ident, $($xs:ident,)*) => {
impl_variant_for_tuples!($($xs,)*);
}
}
macro_rules! impl_variant_for_tuples {
() => {};
( $($name:ident,)+ ) => {
impl<$($name: ToVariant,)+> ToVariant for ($($name,)+) {
#[allow(non_snake_case)]
#[inline]
fn to_variant(&self) -> Variant {
let array = VariantArray::new();
let ($($name,)+) = self;
$(
array.push(&$name.to_variant());
)+
array.into_shared().to_variant()
}
}
impl<$($name: FromVariant,)+> FromVariant for ($($name,)+) {
#[allow(non_snake_case, unused_assignments)]
#[inline]
fn from_variant(v: &Variant) -> Result<Self, FromVariantError> {
let array = VariantArray::from_variant(v)?;
let expected = tuple_length!($($name,)+);
let len = array.len() as usize;
if len != expected {
return Err(FromVariantError::InvalidLength { expected, len });
}
let mut iter = array.iter();
let mut index = 0;
$(
let $name = $name::from_variant(&iter.next().unwrap())
.map_err(|err| FromVariantError::InvalidItem {
index,
error: Box::new(err),
})?;
index += 1;
)+
Ok(($($name,)+))
}
}
impl_variant_for_tuples_next!($($name,)+);
};
}
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!(Ok(42), variant.try_to::<i64>());
let variant = Option::<bool>::None.to_variant();
assert!(variant.is_nil());
let variant = Variant::nil();
assert_eq!(Ok(None), Option::<i64>::from_variant(&variant));
assert_eq!(Ok(None), Option::<bool>::from_variant(&variant));
assert_eq!(Ok(None), Option::<String>::from_variant(&variant));
let variant = Variant::new(42);
assert_eq!(Ok(Some(42)), Option::<i64>::from_variant(&variant));
assert!(Option::<bool>::from_variant(&variant).is_err());
assert!(Option::<String>::from_variant(&variant).is_err());
let variant = Variant::nil();
assert_eq!(Ok(Some(())), Option::<()>::from_variant(&variant));
assert_eq!(Ok(Some(PhantomData)), Option::<PhantomData<*const u8>>::from_variant(&variant));
let variant = Variant::new(42);
assert!(Option::<PhantomData<*const u8>>::from_variant(&variant).is_err());
}
test_variant_result {
let variant = Result::<i64, ()>::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>().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>().ok()));
let variant = Variant::new(true);
assert_eq!(
Err(FromVariantError::InvalidEnumRepr {
expected: VariantEnumRepr::ExternallyTagged,
error: Box::new(FromVariantError::InvalidVariantType {
expected: VariantType::Dictionary,
variant_type: VariantType::Bool,
}),
}),
Result::<(), i64>::from_variant(&variant),
);
let dict = Dictionary::new();
dict.insert("Ok", 42);
assert_eq!(Ok(Ok(42)), Result::<i64, i64>::from_variant(&dict.into_shared().to_variant()));
let dict = Dictionary::new();
dict.insert("Err", 54);
assert_eq!(Ok(Err(54)), Result::<i64, i64>::from_variant(&dict.into_shared().to_variant()));
}
test_to_variant_iter {
let slice: &[i64] = &[0, 1, 2, 3, 4];
let variant = slice.to_variant();
let array = variant.try_to::<VariantArray>().expect("should be array");
assert_eq!(5, array.len());
for i in 0..5 {
assert_eq!(Ok(i), array.get(i as i32).try_to::<i64>());
}
let vec = Vec::<i64>::from_variant(&variant).expect("should succeed");
assert_eq!(slice, vec.as_slice());
let het_array = VariantArray::new();
het_array.push(&Variant::new(42));
het_array.push(&Variant::nil());
assert_eq!(
Err(FromVariantError::InvalidItem {
index: 1,
error: Box::new(FromVariantError::InvalidVariantType {
expected: VariantType::I64,
variant_type: VariantType::Nil,
}),
}),
Vec::<i64>::from_variant(&het_array.duplicate().into_shared().to_variant()),
);
assert_eq!(Ok(vec![Some(42), None]), Vec::<Option<i64>>::from_variant(&het_array.duplicate().into_shared().to_variant()));
het_array.push(&f64::to_variant(&54.0));
assert_eq!(
Err(FromVariantError::InvalidItem {
index: 2,
error: Box::new(FromVariantError::InvalidVariantType {
expected: VariantType::I64,
variant_type: VariantType::F64,
}),
}),
Vec::<Option<i64>>::from_variant(&het_array.duplicate().into_shared().to_variant()),
);
let vec_maybe = Vec::<MaybeNot<i64>>::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::nil()), vec_maybe[1].as_ref().err());
assert_eq!(Some(&f64::to_variant(&54.0)), vec_maybe[2].as_ref().err());
}
test_variant_hash_map {
let original_hash_map = HashMap::from([
("Foo".to_string(), 4u32),
("Bar".to_string(), 2u32)
]);
let variant = original_hash_map.to_variant();
let check_hash_map = variant.try_to::<HashMap<String, u32>>().expect("should be hash map");
assert_eq!(original_hash_map, check_hash_map);
let non_homogenous_key_dictonary = Dictionary::new();
non_homogenous_key_dictonary.insert("Foo".to_string(), 4u32);
non_homogenous_key_dictonary.insert(7, 2u32);
assert_eq!(
non_homogenous_key_dictonary.owned_to_variant().try_to::<HashMap<String, u32>>(),
Err(FromVariantError::InvalidVariantType {
variant_type: VariantType::I64,
expected: VariantType::GodotString
}),
);
let non_homogenous_value_dictonary = Dictionary::new();
non_homogenous_value_dictonary.insert("Foo".to_string(), 4u32);
non_homogenous_value_dictonary.insert("Bar".to_string(), "Unexpected".to_string());
assert_eq!(
non_homogenous_value_dictonary.owned_to_variant().try_to::<HashMap<String, u32>>(),
Err(FromVariantError::InvalidVariantType {
variant_type: VariantType::GodotString,
expected: VariantType::I64
}),
);
}
test_variant_hash_set {
let original_hash_set = HashSet::from([
"Foo".to_string(),
"Bar".to_string(),
]);
let variant = original_hash_set.to_variant();
let check_hash_set = variant.try_to::<HashSet<String>>().expect("should be hash set");
assert_eq!(original_hash_set, check_hash_set);
let duplicate_variant_set = VariantArray::new();
duplicate_variant_set.push("Foo".to_string());
duplicate_variant_set.push("Bar".to_string());
duplicate_variant_set.push("Bar".to_string());
let duplicate_hash_set = variant.try_to::<HashSet<String>>().expect("should be hash set");
assert_eq!(original_hash_set, duplicate_hash_set);
let non_homogenous_set = VariantArray::new();
non_homogenous_set.push("Foo".to_string());
non_homogenous_set.push(7);
assert_eq!(
non_homogenous_set.owned_to_variant().try_to::<HashSet<String>>(),
Err(FromVariantError::InvalidItem {
index: 1,
error: Box::new(FromVariantError::InvalidVariantType {
variant_type: VariantType::I64,
expected: VariantType::GodotString
})
}),
);
}
test_variant_vec {
let original_vec = Vec::from([
"Foo".to_string(),
"Bar".to_string(),
]);
let variant = original_vec.to_variant();
let check_vec = variant.try_to::<Vec<String>>().expect("should be hash set");
assert_eq!(original_vec, check_vec);
let non_homogenous_vec = VariantArray::new();
non_homogenous_vec.push("Foo".to_string());
non_homogenous_vec.push(7);
assert_eq!(
non_homogenous_vec.owned_to_variant().try_to::<Vec<String>>(),
Err(FromVariantError::InvalidItem {
index: 1,
error: Box::new(FromVariantError::InvalidVariantType {
variant_type: VariantType::I64,
expected: VariantType::GodotString
})
}),
);
}
test_variant_tuple {
let variant = (42i64, 54i64).to_variant();
let arr = variant.try_to::<VariantArray>().expect("should be array");
assert_eq!(Ok(42), arr.get(0).try_to::<i64>());
assert_eq!(Ok(54), arr.get(1).try_to::<i64>());
let tuple = <(i64, i64)>::from_variant(&variant);
assert_eq!(Ok((42, 54)), tuple);
}
test_variant_dispatch {
let variant = 42i64.to_variant();
if let VariantDispatch::I64(i) = variant.dispatch() {
assert_eq!(42, i);
} else {
panic!("incorrect dispatch type");
};
let variant = true.to_variant();
if let VariantDispatch::Bool(b) = variant.dispatch() {
assert!(b);
} else {
panic!("incorrect dispatch type");
};
let variant = 42.to_variant();
let number_as_float = match variant.dispatch() {
VariantDispatch::I64(i) => i as f64,
VariantDispatch::F64(f) => f,
_ => panic!("not a number"),
};
approx::assert_relative_eq!(42.0, number_as_float);
}
);