use std::fmt;
use godot_ffi::{ExtVariantType, GodotFfi, PtrcallType};
use crate::builtin::Variant;
use crate::meta::error::ConvertError;
use crate::meta::shape::GodotShape;
use crate::meta::{FromGodot, GodotConvert, GodotFfiVariant, ToGodot};
use crate::sys;
#[doc(hidden)]
pub struct RefArg<'r, T> {
shared_ref: Option<&'r T>,
}
impl<'r, T> RefArg<'r, T> {
pub fn new(shared_ref: &'r T) -> Self {
RefArg {
shared_ref: Some(shared_ref),
}
}
pub(crate) fn null_ref() -> Self {
RefArg { shared_ref: None }
}
pub fn get_ref(&self) -> &T {
self.shared_ref.expect("RefArg is null")
}
pub fn to_owned(&self) -> T
where
T: Clone,
{
self.get_ref().clone()
}
}
macro_rules! wrong_direction {
($fn:ident) => {
unreachable!(concat!(
stringify!($fn),
": RefArg should only be passed *to* Godot, not *from*."
))
};
}
impl<T> GodotConvert for RefArg<'_, T>
where
T: GodotConvert,
{
type Via = T::Via;
fn godot_shape() -> GodotShape {
T::godot_shape()
}
}
impl<T> ToGodot for RefArg<'_, T>
where
T: ToGodot,
{
type Pass = T::Pass;
fn to_godot(&self) -> crate::meta::ToArg<'_, Self::Via, Self::Pass> {
let shared_ref = self
.shared_ref
.expect("Objects are currently mapped through ObjectArg; RefArg shouldn't be null");
shared_ref.to_godot()
}
fn to_godot_owned(&self) -> Self::Via {
let shared_ref = self
.shared_ref
.expect("Objects are currently mapped through ObjectArg; RefArg shouldn't be null");
shared_ref.to_godot_owned()
}
}
impl<T> FromGodot for RefArg<'_, T>
where
T: FromGodot,
{
fn try_from_godot(_via: Self::Via) -> Result<Self, ConvertError> {
wrong_direction!(try_from_godot)
}
}
impl<T> fmt::Debug for RefArg<'_, T>
where
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "&{:?}", self.shared_ref)
}
}
unsafe impl<T> GodotFfi for RefArg<'_, T>
where
T: GodotFfi,
{
const VARIANT_TYPE: ExtVariantType = T::VARIANT_TYPE;
unsafe fn new_from_sys(_ptr: sys::GDExtensionConstTypePtr) -> Self {
wrong_direction!(new_from_sys)
}
unsafe fn new_with_uninit(_init_fn: impl FnOnce(sys::GDExtensionUninitializedTypePtr)) -> Self {
wrong_direction!(new_with_uninit)
}
unsafe fn new_with_init(_init_fn: impl FnOnce(sys::GDExtensionTypePtr)) -> Self {
wrong_direction!(new_with_init)
}
fn sys(&self) -> sys::GDExtensionConstTypePtr {
match self.shared_ref {
Some(r) => r.sys(),
None => std::ptr::null(),
}
}
fn sys_mut(&mut self) -> sys::GDExtensionTypePtr {
unreachable!(
"RefArg::sys_mut() currently not used by FFI marshalling layer, but only by specific functions"
);
}
fn as_arg_ptr(&self) -> sys::GDExtensionConstTypePtr {
match self.shared_ref {
Some(r) => r.as_arg_ptr(),
None => std::ptr::null(),
}
}
unsafe fn from_arg_ptr(_ptr: sys::GDExtensionTypePtr, _call_type: PtrcallType) -> Self {
wrong_direction!(from_arg_ptr)
}
unsafe fn move_return_ptr(self, _dst: sys::GDExtensionTypePtr, _call_type: PtrcallType) {
unreachable!(
"Calling RefArg::move_return_ptr is a mistake, as RefArg is intended only for arguments. Use the underlying value type."
);
}
}
impl<T> GodotFfiVariant for RefArg<'_, T>
where
T: GodotFfiVariant,
{
fn ffi_to_variant(&self) -> Variant {
match self.shared_ref {
Some(r) => r.ffi_to_variant(),
None => Variant::nil(),
}
}
fn ffi_from_variant(_variant: &Variant) -> Result<Self, ConvertError> {
wrong_direction!(ffi_from_variant)
}
}