#![deny(unsafe_op_in_unsafe_fn)]
#![allow(unused_attributes)]
use std::fmt;
use godot_ffi as sys;
use sys::GodotFfi;
use crate::builtin::Variant;
use crate::meta::error::{CallError, CallResult};
use crate::meta::{
ArgPassing, CallContext, EngineFromGodot, EngineToGodot, FromGodot, GodotConvert, GodotType,
InParamTuple, OutParamTuple, ParamTuple, TupleFromGodot,
};
use crate::registry::method::MethodParamOrReturnInfo;
macro_rules! count_idents {
() => { 0 };
($id:ident $($rest:ident)*) => { 1 + count_idents!($($rest)*)};
}
macro_rules! unsafe_impl_param_tuple {
($(($p:ident, $n:tt): $P:ident),*) => {
impl<$($P: FromGodot + fmt::Debug),*> TupleFromGodot for ($($P,)*) {}
impl<$($P),*> ParamTuple for ($($P,)*) where $($P: GodotConvert + fmt::Debug),* {
const LEN: usize = count_idents!($($P)*);
#[doc(hidden)]
fn param_info(
index: usize,
param_name: &str,
) -> Option<MethodParamOrReturnInfo> {
match index {
$(
$n => Some(MethodParamOrReturnInfo::for_parameter::<$P>(param_name)),
)*
_ => None,
}
}
fn format_args(&self) -> String {
format!(
concat!("" $(, "{", $n, ":?}",)", "*),
$(self.$n),*
)
}
}
impl<$($P),*> InParamTuple for ($($P,)*) where $($P: EngineFromGodot + fmt::Debug),* {
unsafe fn from_varcall_args(
args_ptr: *const sys::GDExtensionConstVariantPtr,
arg_count: usize,
default_values: &[Variant],
call_ctx: &crate::meta::CallContext,
) -> CallResult<Self> {
if arg_count == Self::LEN {
let param_tuple = (
$(
unsafe { varcall_arg::<$P>(*args_ptr.add($n), call_ctx, $n)? },
)*
);
return Ok(param_tuple);
}
let mut all_args = Vec::with_capacity(Self::LEN);
for i in 0..arg_count {
all_args.push(unsafe { *args_ptr.add(i) });
}
let required_param_count = Self::LEN - default_values.len();
let first_missing_index = arg_count - required_param_count;
for i in first_missing_index..default_values.len() {
all_args.push(default_values[i].var_sys());
}
let param_tuple = (
$(
unsafe { varcall_arg::<$P>(all_args[$n], call_ctx, $n)? },
)*
);
Ok(param_tuple)
}
unsafe fn from_ptrcall_args(
args_ptr: *const sys::GDExtensionConstTypePtr,
call_type: sys::PtrcallType,
call_ctx: &crate::meta::CallContext,
) -> CallResult<Self>
where
$($P: EngineFromGodot,)*
{
let tuple = (
$(
unsafe { ptrcall_arg::<$P, $n>(args_ptr, call_ctx, call_type)? },
)*
);
Ok(tuple) }
fn from_variant_array(array: &[&Variant]) -> Self {
assert_array_length::<Self>(array);
let mut iter = array.iter();
(
$(
{
let variant = iter.next().unwrap_or_else(
|| panic!("ParamTuple: {} access out-of-bounds (len {})", stringify!($p), array.len()));
variant.to_relaxed_or_panic(
|| format!("ParamTuple: failed to convert parameter {}", stringify!($p)))
},
)*
)
}
}
impl<$($P),*> OutParamTuple for ($($P,)*) where $($P: EngineToGodot + fmt::Debug,)* {
fn with_variants<F, R>(self, f: F) -> R
where
F: FnOnce(&[Variant]) -> R,
{
let variant_args = [
$(
<$P::Pass as ArgPassing>::ref_to_variant(&self.$n),
)*
];
f(&variant_args)
}
fn with_variant_pointers<F, R>(self, f: F) -> R
where
F: FnOnce(&[godot_ffi::GDExtensionConstVariantPtr]) -> R,
{
self.with_variants(|variants| {
let sys_args = [
$(
Variant::var_sys(&variants[$n]),
)*
];
f(&sys_args)
})
}
fn with_type_pointers<F, R>(self, f: F) -> R
where
F: FnOnce(&[godot_ffi::GDExtensionConstTypePtr]) -> R,
{
let ffi_args = (
$(
<$P::Pass as ArgPassing>::ref_to_ffi(&self.$n),
)*
);
let ptr_args = [
$(
sys::GodotFfi::as_arg_ptr(&ffi_args.$n),
)*
];
f(&ptr_args)
}
fn to_variant_array(&self) -> Vec<Variant> {
vec![
$(
<$P::Pass as ArgPassing>::ref_to_variant(&self.$n),
)*
]
}
}
};
}
#[allow(unused_variables, unused_mut, clippy::unused_unit)]
mod unit_impl {
use super::*;
unsafe_impl_param_tuple!();
}
unsafe_impl_param_tuple!((p0, 0): P0);
unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1);
unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2);
unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3);
unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4);
unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5);
unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6);
unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7);
unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8);
unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8, (p9, 9): P9);
unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8, (p9, 9): P9, (p10, 10): P10);
unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8, (p9, 9): P9, (p10, 10): P10, (p11, 11): P11);
unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8, (p9, 9): P9, (p10, 10): P10, (p11, 11): P11, (p12, 12): P12);
unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6, (p7, 7): P7, (p8, 8): P8, (p9, 9): P9, (p10, 10): P10, (p11, 11): P11, (p12, 12): P12, (p13, 13): P13);
pub(super) unsafe fn ptrcall_arg<P: EngineFromGodot, const N: usize>(
args_ptr: *const sys::GDExtensionConstTypePtr,
call_ctx: &CallContext,
call_type: sys::PtrcallType,
) -> CallResult<P> {
let offset_ptr = unsafe { *args_ptr.add(N) };
let ffi = unsafe {
<P::Via as GodotType>::Ffi::from_arg_ptr(sys::force_mut_ptr(offset_ptr), call_type)
};
<P::Via as GodotType>::try_from_ffi(ffi)
.and_then(P::engine_try_from_godot)
.map_err(|err| CallError::failed_param_conversion::<P>(call_ctx, N, err))
}
pub(super) unsafe fn varcall_arg<P: EngineFromGodot>(
arg: sys::GDExtensionConstVariantPtr,
call_ctx: &CallContext,
param_index: usize,
) -> CallResult<P> {
let variant_ref = unsafe { Variant::borrow_var_sys(arg) };
variant_ref
.engine_try_to_relaxed::<P>()
.map_err(|err| CallError::failed_param_conversion::<P>(call_ctx, param_index, err))
}
fn assert_array_length<P: ParamTuple>(array: &[&Variant]) {
assert_eq!(
array.len(),
P::LEN,
"array {array:?} has wrong length, expected {} got {}",
P::LEN,
array.len()
);
}
#[cfg(test)] #[cfg_attr(published_docs, doc(cfg(test)))]
mod test {
use super::*;
#[test]
fn format_args_test() {
assert_eq!(&().format_args(), "");
assert_eq!(&(1, 2, 3).format_args(), "1, 2, 3");
}
#[test]
fn count_idents_test() {
assert_eq!(2, count_idents!(a b));
assert_eq!(0, count_idents!());
assert_eq!(5, count_idents!(a b b a d));
}
}