use crate::builtin::{Array, Variant};
use crate::meta;
use crate::meta::error::{ConvertError, ErrorKind, FromFfiError};
use crate::meta::shape::GodotShape;
use crate::meta::{Element, FromGodot, GodotConvert, GodotNullableType, GodotType, ToGodot};
use crate::registry::info::ParamMetadata;
impl<T: GodotNullableType> GodotType for Option<T> {
type Ffi = T::Ffi;
type ToFfi<'f> = T::ToFfi<'f>;
fn to_ffi(&self) -> Self::ToFfi<'_> {
self.as_ref()
.map(|t| t.to_ffi())
.unwrap_or_else(T::ffi_null_ref)
}
fn into_ffi(self) -> Self::Ffi {
self.map(|t| t.into_ffi()).unwrap_or_else(T::ffi_null)
}
fn try_from_ffi(ffi: Self::Ffi) -> Result<Self, ConvertError> {
if T::ffi_is_null(&ffi) {
return Ok(None);
}
GodotType::try_from_ffi(ffi).map(Some)
}
fn from_ffi(ffi: Self::Ffi) -> Self {
if T::ffi_is_null(&ffi) {
return None;
}
Some(GodotType::from_ffi(ffi))
}
fn as_object_arg(&self) -> meta::ObjectArg<'_> {
match self {
Some(inner) => inner.as_object_arg(),
None => meta::ObjectArg::null(),
}
}
}
impl<T> GodotConvert for Option<T>
where
T: GodotConvert,
Option<T::Via>: GodotType,
{
type Via = Option<T::Via>;
fn godot_shape() -> GodotShape {
match T::godot_shape() {
GodotShape::Class {
class_id, heritage, ..
} => GodotShape::Class {
class_id,
heritage,
is_nullable: true,
},
other => other,
}
}
}
impl<T> ToGodot for Option<T>
where
T: ToGodot<Pass = meta::ByObject>,
T::Via: GodotNullableType,
{
type Pass = meta::ByOption<T::Via>;
fn to_godot(&self) -> Option<&T::Via> {
self.as_ref().map(T::to_godot)
}
fn to_godot_owned(&self) -> Option<T::Via> {
self.as_ref().map(T::to_godot_owned)
}
fn to_variant(&self) -> Variant {
match self {
Some(inner) => inner.to_variant(),
None => Variant::nil(),
}
}
}
impl<T: FromGodot> FromGodot for Option<T>
where
Option<T::Via>: GodotType,
{
fn try_from_godot(via: Self::Via) -> Result<Self, ConvertError> {
match via {
Some(via) => T::try_from_godot(via).map(Some),
None => Ok(None),
}
}
fn from_godot(via: Self::Via) -> Self {
via.map(T::from_godot)
}
fn try_from_variant(variant: &Variant) -> Result<Self, ConvertError> {
if T::Via::qualifies_as_special_none(variant) {
return Ok(None);
}
if variant.is_nil() {
return Ok(None);
}
let value = T::try_from_variant(variant)?;
Ok(Some(value))
}
fn from_variant(variant: &Variant) -> Self {
if variant.is_nil() {
return None;
}
Some(T::from_variant(variant))
}
}
macro_rules! impl_godot_scalar {
($T:ty as $Via:ty, $err:path, $param_metadata:expr_2021) => {
impl GodotType for $T {
type Ffi = $Via;
type ToFfi<'f> = $Via;
fn to_ffi(&self) -> Self::ToFfi<'_> {
(*self).into()
}
fn into_ffi(self) -> Self::Ffi {
self.into()
}
fn try_from_ffi(ffi: Self::Ffi) -> Result<Self, ConvertError> {
Self::try_from(ffi).map_err(|_rust_err| {
$err.into_error(ffi)
})
}
impl_godot_scalar!(@shared_fns; $Via, $param_metadata);
}
impl Element for $T {
fn debug_validate_elements(array: &Array<Self>) -> Result<(), ConvertError> {
array.debug_validate_int_elements()
}
}
impl_godot_scalar!(@shared_traits; $T);
};
($T:ty as $Via:ty, $param_metadata:expr_2021; lossy) => {
impl GodotType for $T {
type Ffi = $Via;
type ToFfi<'f> = $Via;
fn to_ffi(&self) -> Self::ToFfi<'_> {
*self as $Via
}
fn into_ffi(self) -> Self::Ffi {
self as $Via
}
fn try_from_ffi(ffi: Self::Ffi) -> Result<Self, ConvertError> {
Ok(ffi as $T)
}
impl_godot_scalar!(@shared_fns; $Via, $param_metadata);
}
impl Element for $T {}
impl_godot_scalar!(@shared_traits; $T);
};
(@shared_fns; $Via:ty, $param_metadata:expr_2021) => {
fn default_metadata() -> ParamMetadata {
$param_metadata
}
};
(@shared_traits; $T:ty) => {
impl GodotConvert for $T {
type Via = $T;
fn godot_shape() -> GodotShape {
GodotShape::of_builtin::<$T>()
}
}
impl ToGodot for $T {
type Pass = meta::ByValue;
fn to_godot(&self) -> Self::Via {
*self
}
}
impl FromGodot for $T {
fn try_from_godot(via: Self::Via) -> Result<Self, ConvertError> {
Ok(via)
}
}
};
}
meta::impl_godot_as_self!(bool: ByValue);
meta::impl_godot_as_self!(i64: ByValue);
meta::impl_godot_as_self!(f64: ByValue);
meta::impl_godot_as_self!((): ByValue);
impl_godot_scalar!(i8 as i64, FromFfiError::I8, ParamMetadata::INT_IS_INT8);
impl_godot_scalar!(u8 as i64, FromFfiError::U8, ParamMetadata::INT_IS_UINT8);
impl_godot_scalar!(i16 as i64, FromFfiError::I16, ParamMetadata::INT_IS_INT16);
impl_godot_scalar!(u16 as i64, FromFfiError::U16, ParamMetadata::INT_IS_UINT16);
impl_godot_scalar!(i32 as i64, FromFfiError::I32, ParamMetadata::INT_IS_INT32);
impl_godot_scalar!(u32 as i64, FromFfiError::U32, ParamMetadata::INT_IS_UINT32);
impl_godot_scalar!(f32 as f64, ParamMetadata::REAL_IS_FLOAT; lossy);
impl GodotType for u64 {
type Ffi = i64;
type ToFfi<'f> = i64;
fn to_ffi(&self) -> Self::ToFfi<'_> {
*self as i64
}
fn into_ffi(self) -> Self::Ffi {
self as i64
}
fn try_from_ffi(ffi: Self::Ffi) -> Result<Self, ConvertError> {
Ok(ffi as u64)
}
impl_godot_scalar!(@shared_fns; i64, ParamMetadata::INT_IS_UINT64);
}
impl GodotConvert for u64 {
type Via = u64;
fn godot_shape() -> GodotShape {
GodotShape::of_builtin::<u64>()
}
}
impl meta::EngineToGodot for u64 {
type Pass = meta::ByValue;
fn engine_to_godot(&self) -> meta::ToArg<'_, Self::Via, Self::Pass> {
*self
}
fn engine_to_variant(&self) -> Variant {
Variant::from(*self as i64) }
}
impl meta::EngineFromGodot for u64 {
fn engine_try_from_godot(via: Self::Via) -> Result<Self, ConvertError> {
Ok(via)
}
fn engine_try_from_variant(variant: &Variant) -> Result<Self, ConvertError> {
variant.try_to::<i64>().map(|i| i as u64)
}
}
impl<T: Element> GodotConvert for Vec<T> {
type Via = Array<T>;
fn godot_shape() -> GodotShape {
<Array<T> as GodotConvert>::godot_shape()
}
}
impl<T: Element> ToGodot for Vec<T> {
type Pass = meta::ByValue;
fn to_godot(&self) -> Self::Via {
Array::from(self.as_slice())
}
}
impl<T: Element> FromGodot for Vec<T> {
fn try_from_godot(via: Self::Via) -> Result<Self, ConvertError> {
Ok(via.iter_shared().collect())
}
}
impl<T: Element, const LEN: usize> GodotConvert for [T; LEN] {
type Via = Array<T>;
fn godot_shape() -> GodotShape {
<Array<T> as GodotConvert>::godot_shape()
}
}
impl<T: Element, const LEN: usize> ToGodot for [T; LEN] {
type Pass = meta::ByValue;
fn to_godot(&self) -> Self::Via {
Array::from(self)
}
}
impl<T: Element, const LEN: usize> FromGodot for [T; LEN] {
fn try_from_godot(via: Self::Via) -> Result<Self, ConvertError> {
let via_len = via.len(); if via_len != LEN {
let message =
format!("Array<T> of length {via_len} cannot be stored in [T; {LEN}] Rust array");
return Err(ConvertError::with_kind_value(
ErrorKind::Custom(Some(message.into())),
via,
));
}
let mut option_array = [const { None }; LEN];
for (element, destination) in via.iter_shared().zip(&mut option_array) {
*destination = Some(element);
}
let array = option_array.map(|some| {
some.expect(
"Elements were removed from Array during `iter_shared()`, this is not allowed",
)
});
Ok(array)
}
}
impl<T: Element> GodotConvert for &[T] {
type Via = Array<T>;
fn godot_shape() -> GodotShape {
<Array<T> as GodotConvert>::godot_shape()
}
}
impl<T: Element> ToGodot for &[T] {
type Pass = meta::ByValue;
fn to_godot(&self) -> Self::Via {
Array::from(*self)
}
}
fn __doctest_u64() {}
fn __doctest_i32_ptr_to_variant() {}
fn __doctest_void_ptr_from_variant() {}
fn __doctest_native_struct_pointer_param() {}
fn __doctest_native_struct_pointer_return() {}
fn __doctest_u64_return() {}