godot_core/meta/godot_convert/
impls.rsuse crate::builtin::{Array, Variant};
use crate::meta::error::{ConvertError, ErrorKind, FromFfiError, FromVariantError};
use crate::meta::{
ArrayElement, ClassName, FromGodot, GodotConvert, GodotNullableFfi, GodotType,
PropertyHintInfo, PropertyInfo, ToGodot,
};
use crate::registry::method::MethodParamOrReturnInfo;
use godot_ffi as sys;
impl<T> GodotType for Option<T>
where
T: GodotType,
T::Ffi: GodotNullableFfi,
for<'f> T::ToFfi<'f>: GodotNullableFfi,
{
type Ffi = T::Ffi;
type ToFfi<'f> = T::ToFfi<'f>;
fn to_ffi(&self) -> Self::ToFfi<'_> {
GodotNullableFfi::flatten_option(self.as_ref().map(|t| t.to_ffi()))
}
fn into_ffi(self) -> Self::Ffi {
GodotNullableFfi::flatten_option(self.map(|t| t.into_ffi()))
}
fn try_from_ffi(ffi: Self::Ffi) -> Result<Self, ConvertError> {
if ffi.is_null() {
return Ok(None);
}
GodotType::try_from_ffi(ffi).map(Some)
}
fn from_ffi(ffi: Self::Ffi) -> Self {
if ffi.is_null() {
return None;
}
Some(GodotType::from_ffi(ffi))
}
fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata {
T::param_metadata()
}
fn class_name() -> ClassName {
T::class_name()
}
fn property_info(property_name: &str) -> PropertyInfo {
T::property_info(property_name)
}
fn property_hint_info() -> PropertyHintInfo {
T::property_hint_info()
}
fn argument_info(property_name: &str) -> MethodParamOrReturnInfo {
T::argument_info(property_name)
}
fn return_info() -> Option<MethodParamOrReturnInfo> {
T::return_info()
}
fn godot_type_name() -> String {
T::godot_type_name()
}
}
impl<T: GodotConvert> GodotConvert for Option<T>
where
Option<T::Via>: GodotType,
{
type Via = Option<T::Via>;
}
impl<T: ToGodot> ToGodot for Option<T>
where
Option<T::Via>: GodotType,
for<'v, 'f> T::ToVia<'v>: GodotType<
Ffi: GodotNullableFfi,
ToFfi<'f>: GodotNullableFfi,
>,
{
type ToVia<'v> = Option<T::ToVia<'v>>
where Self: 'v;
fn to_godot(&self) -> Self::ToVia<'_> {
self.as_ref().map(ToGodot::to_godot)
}
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) => {
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 ArrayElement for $T {
fn debug_validate_elements(array: &Array<Self>) -> Result<(), ConvertError> {
array.debug_validate_elements()
}
}
impl_godot_scalar!(@shared_traits; $T);
};
($T:ty as $Via:ty, $param_metadata:expr; 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 ArrayElement for $T {}
impl_godot_scalar!(@shared_traits; $T);
};
(@shared_fns; $Via:ty, $param_metadata:expr) => {
fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata {
$param_metadata
}
fn godot_type_name() -> String {
<$Via as GodotType>::godot_type_name()
}
};
(@shared_traits; $T:ty) => {
impl GodotConvert for $T {
type Via = $T;
}
impl ToGodot for $T {
type ToVia<'v> = Self::Via;
fn to_godot(&self) -> Self::ToVia<'_> {
*self
}
}
impl FromGodot for $T {
fn try_from_godot(via: Self::Via) -> Result<Self, ConvertError> {
Ok(via)
}
}
$crate::impl_asarg_by_value!($T);
};
}
crate::meta::impl_godot_as_self!(bool);
crate::meta::impl_godot_as_self!(i64);
crate::meta::impl_godot_as_self!(f64);
crate::meta::impl_godot_as_self!(());
impl_godot_scalar!(
i8 as i64,
FromFfiError::I8,
sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT8
);
impl_godot_scalar!(
u8 as i64,
FromFfiError::U8,
sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT8
);
impl_godot_scalar!(
i16 as i64,
FromFfiError::I16,
sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT16
);
impl_godot_scalar!(
u16 as i64,
FromFfiError::U16,
sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT16
);
impl_godot_scalar!(
i32 as i64,
FromFfiError::I32,
sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT32
);
impl_godot_scalar!(
u32 as i64,
FromFfiError::U32,
sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT32
);
impl_godot_scalar!(
f32 as f64,
sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_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> {
Self::try_from(ffi).map_err(|_rust_err| FromFfiError::U64.into_error(ffi))
}
impl_godot_scalar!(@shared_fns; i64, sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT64);
}
impl GodotConvert for u64 {
type Via = u64;
}
impl ToGodot for u64 {
type ToVia<'v> = u64;
fn to_godot(&self) -> Self::ToVia<'_> {
*self
}
fn to_variant(&self) -> Variant {
i64::try_from(*self)
.map(|v| v.to_variant())
.unwrap_or_else(|_| {
panic!("to_variant(): u64 value {} is not representable inside Variant, which can only store i64 integers", self)
})
}
}
impl FromGodot for u64 {
fn try_from_godot(via: Self::Via) -> Result<Self, ConvertError> {
Ok(via)
}
fn try_from_variant(variant: &Variant) -> Result<Self, ConvertError> {
let value = variant.try_to::<i64>()?;
u64::try_from(value).map_err(|_rust_err| {
FromVariantError::BadValue.into_error(value)
})
}
}
impl<T: ArrayElement> GodotConvert for Vec<T> {
type Via = Array<T>;
}
impl<T: ArrayElement> ToGodot for Vec<T> {
type ToVia<'v> = Array<T>;
fn to_godot(&self) -> Self::ToVia<'_> {
Array::from(self.as_slice())
}
}
impl<T: ArrayElement> FromGodot for Vec<T> {
fn try_from_godot(via: Self::Via) -> Result<Self, ConvertError> {
Ok(via.iter_shared().collect())
}
}
impl<T: ArrayElement, const LEN: usize> GodotConvert for [T; LEN] {
type Via = Array<T>;
}
impl<T: ArrayElement, const LEN: usize> ToGodot for [T; LEN] {
type ToVia<'v> = Array<T>;
fn to_godot(&self) -> Self::ToVia<'_> {
Array::from(self)
}
}
impl<T: ArrayElement, 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: ArrayElement> GodotConvert for &[T] {
type Via = Array<T>;
}
impl<T: ArrayElement> ToGodot for &[T] {
type ToVia<'v> = Array<T>
where Self: 'v;
fn to_godot(&self) -> Self::ToVia<'_> {
Array::from(*self)
}
}
macro_rules! impl_pointer_convert {
($Ptr:ty) => {
impl GodotConvert for $Ptr {
type Via = i64;
}
impl ToGodot for $Ptr {
type ToVia<'v> = i64;
fn to_godot(&self) -> Self::ToVia<'_> {
*self as i64
}
}
impl FromGodot for $Ptr {
fn try_from_godot(via: Self::Via) -> Result<Self, ConvertError> {
Ok(via as Self)
}
}
};
}
impl_pointer_convert!(*const std::ffi::c_void);
impl_pointer_convert!(*mut std::ffi::c_void);
impl_pointer_convert!(*mut *const u8);
impl_pointer_convert!(*mut i32);
impl_pointer_convert!(*mut f64);
impl_pointer_convert!(*mut u8);
impl_pointer_convert!(*const u8);