1#![deny(unsafe_op_in_unsafe_fn)]
9#![allow(unused_attributes)]
10
11use std::fmt;
12
13use godot_ffi as sys;
14use sys::GodotFfi;
15
16use crate::builtin::Variant;
17use crate::meta::error::{CallError, ConvertError};
18use crate::meta::{
19 signature, CallContext, FromGodot, GodotConvert, GodotFfiVariant, GodotType, InParamTuple,
20 OutParamTuple, ParamTuple, ToGodot,
21};
22
23macro_rules! count_idents {
24 () => { 0 };
25 ($id:ident $($rest:ident)*) => { 1 + count_idents!($($rest)*)};
26}
27
28macro_rules! unsafe_impl_param_tuple {
29 ($(($p:ident, $n:tt): $P:ident),*) => {
30 impl<$($P),*> ParamTuple for ($($P,)*) where $($P: GodotConvert + fmt::Debug),* {
31 const LEN: usize = count_idents!($($P)*);
32
33 #[doc(hidden)]
34 fn param_info(
35 index: usize,
36 param_name: &str,
37 ) -> Option<crate::registry::method::MethodParamOrReturnInfo> {
38 match index {
39 $(
40 $n => Some($P::Via::argument_info(param_name)),
41 )*
42 _ => None,
43 }
44 }
45
46 fn format_args(&self) -> String {
47 format!(
48 concat!("" $(, "{", $n, ":?}",)", "*),
51 $(self.$n),*
52 )
53 }
54 }
55
56 impl<$($P),*> InParamTuple for ($($P,)*) where $($P: FromGodot + fmt::Debug),* {
57 unsafe fn from_varcall_args(
58 args_ptr: *const sys::GDExtensionConstVariantPtr,
59 call_ctx: &crate::meta::CallContext,
60 ) -> signature::CallResult<Self> {
61 let args = (
62 $(
63 unsafe { *args_ptr.offset($n) },
66 )*
67 );
68
69 let param_tuple = (
70 $(
71 unsafe { varcall_arg::<$P>(args.$n, call_ctx, $n)? },
73 )*
74 );
75
76 Ok(param_tuple)
77 }
78
79 unsafe fn from_ptrcall_args(
80 args_ptr: *const sys::GDExtensionConstTypePtr,
81 call_type: sys::PtrcallType,
82 call_ctx: &crate::meta::CallContext,
83 ) -> Self {
84 (
85 $(
86 unsafe { ptrcall_arg::<$P, $n>(args_ptr, call_ctx, call_type) },
89 )*
90 )
91 }
92
93 fn from_variant_array(array: &[&Variant]) -> Self {
94 assert_array_length::<Self>(array);
95 let mut iter = array.iter();
96 (
97 $(
98 {
99 let variant = iter.next().unwrap_or_else(
100 || panic!("ParamTuple: {} access out-of-bounds (len {})", stringify!($p), array.len()));
101
102 variant.to_relaxed_or_panic(
103 || format!("ParamTuple: failed to convert parameter {}", stringify!($p)))
104 },
105 )*
106 )
107 }
108 }
109
110 impl<$($P),*> OutParamTuple for ($($P,)*) where $($P: ToGodot + fmt::Debug),* {
111 fn with_variants<F, R>(self, f: F) -> R
112 where
113 F: FnOnce(&[Variant]) -> R,
114 {
115 let ffi_args = (
116 $(
117 GodotType::into_ffi(ToGodot::to_godot(&self.$n)),
118 )*
119 );
120
121 let variant_args = [
122 $(
123 GodotFfiVariant::ffi_to_variant(&ffi_args.$n),
124 )*
125 ];
126
127 f(&variant_args)
128 }
129
130 fn with_variant_pointers<F, R>(self, f: F) -> R
131 where
132 F: FnOnce(&[godot_ffi::GDExtensionConstVariantPtr]) -> R,
133 {
134 self.with_variants(|variants| {
135 let sys_args = [
136 $(
137 Variant::var_sys(&variants[$n]),
138 )*
139 ];
140 f(&sys_args)
141 })
142 }
143
144 fn with_type_pointers<F, R>(self, f: F) -> R
145 where
146 F: FnOnce(&[godot_ffi::GDExtensionConstTypePtr]) -> R,
147 {
148 let ffi_args = (
149 $(
150 GodotType::into_ffi(ToGodot::to_godot(&self.$n)),
151 )*
152 );
153
154 let ptr_args = [
155 $(
156 sys::GodotFfi::as_arg_ptr(&ffi_args.$n),
157 )*
158 ];
159
160 f(&ptr_args)
161 }
162
163 fn to_variant_array(&self) -> Vec<Variant> {
164 let ($($p,)*) = self;
165
166 vec![
167 $( $p.to_variant(), )*
168 ]
169 }
170 }
171 };
172}
173
174#[allow(unused_variables, unused_mut, clippy::unused_unit)]
175mod unit_impl {
176 use super::*;
177 unsafe_impl_param_tuple!();
178}
179unsafe_impl_param_tuple!((p0, 0): P0);
180unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1);
181unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2);
182unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3);
183unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4);
184unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5);
185unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6);
186unsafe_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);
187unsafe_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);
188unsafe_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);
189unsafe_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);
190unsafe_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);
191unsafe_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);
192unsafe_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);
193
194pub(super) unsafe fn ptrcall_arg<P: FromGodot, const N: isize>(
201 args_ptr: *const sys::GDExtensionConstTypePtr,
202 call_ctx: &CallContext,
203 call_type: sys::PtrcallType,
204) -> P {
205 let offset_ptr = unsafe { *args_ptr.offset(N) };
207
208 let ffi = unsafe {
210 <P::Via as GodotType>::Ffi::from_arg_ptr(sys::force_mut_ptr(offset_ptr), call_type)
211 };
212
213 <P::Via as GodotType>::try_from_ffi(ffi)
214 .and_then(P::try_from_godot)
215 .unwrap_or_else(|err| param_error::<P>(call_ctx, N as i32, err))
216}
217
218pub(super) unsafe fn varcall_arg<P: FromGodot>(
224 arg: sys::GDExtensionConstVariantPtr,
225 call_ctx: &CallContext,
226 param_index: isize,
227) -> Result<P, CallError> {
228 let variant_ref = unsafe { Variant::borrow_var_sys(arg) };
230
231 variant_ref
232 .try_to_relaxed::<P>()
233 .map_err(|err| CallError::failed_param_conversion::<P>(call_ctx, param_index, err))
234}
235
236fn param_error<P>(call_ctx: &CallContext, index: i32, err: ConvertError) -> ! {
237 let param_ty = std::any::type_name::<P>();
238 panic!("in function `{call_ctx}` at parameter [{index}] of type {param_ty}: {err}");
239}
240
241fn assert_array_length<P: ParamTuple>(array: &[&Variant]) {
242 assert_eq!(
243 array.len(),
244 P::LEN,
245 "array {array:?} has wrong length, expected {} got {}",
246 P::LEN,
247 array.len()
248 );
249}
250
251#[cfg(test)] #[cfg_attr(published_docs, doc(cfg(test)))]
252mod test {
253 use super::*;
254
255 #[test]
256 fn format_args_test() {
257 assert_eq!(&().format_args(), "");
258 assert_eq!(&(1, 2, 3).format_args(), "1, 2, 3");
259 }
260
261 #[test]
262 fn count_idents_test() {
263 assert_eq!(2, count_idents!(a b));
264 assert_eq!(0, count_idents!());
265 assert_eq!(5, count_idents!(a b b a d));
266 }
267}