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, ArgPassing, CallContext, FromGodot, GodotConvert, 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<Via: Clone> + fmt::Debug,)* {
111 fn with_variants<F, R>(self, f: F) -> R
112 where
113 F: FnOnce(&[Variant]) -> R,
114 {
115 let variant_args = [
116 $(
117 <$P::Pass as ArgPassing>::ref_to_variant(&self.$n),
118 )*
119 ];
120
121 f(&variant_args)
122 }
123
124 fn with_variant_pointers<F, R>(self, f: F) -> R
125 where
126 F: FnOnce(&[godot_ffi::GDExtensionConstVariantPtr]) -> R,
127 {
128 self.with_variants(|variants| {
129 let sys_args = [
130 $(
131 Variant::var_sys(&variants[$n]),
132 )*
133 ];
134 f(&sys_args)
135 })
136 }
137
138 fn with_type_pointers<F, R>(self, f: F) -> R
139 where
140 F: FnOnce(&[godot_ffi::GDExtensionConstTypePtr]) -> R,
141 {
142 let ffi_args = (
144 $(
145 <$P::Pass as ArgPassing>::ref_to_ffi(&self.$n),
146 )*
147 );
148
149 let ptr_args = [
150 $(
151 sys::GodotFfi::as_arg_ptr(&ffi_args.$n),
152 )*
153 ];
154
155 f(&ptr_args)
156 }
157
158 fn to_variant_array(&self) -> Vec<Variant> {
159 let ($($p,)*) = self;
160
161 vec![
162 $( $p.to_variant(), )*
163 ]
164 }
165 }
166 };
167}
168
169#[allow(unused_variables, unused_mut, clippy::unused_unit)]
170mod unit_impl {
171 use super::*;
172 unsafe_impl_param_tuple!();
173}
174unsafe_impl_param_tuple!((p0, 0): P0);
175unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1);
176unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2);
177unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3);
178unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4);
179unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5);
180unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6);
181unsafe_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);
182unsafe_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);
183unsafe_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);
184unsafe_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);
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, (p7, 7): P7, (p8, 8): P8, (p9, 9): P9, (p10, 10): P10, (p11, 11): P11);
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, (p8, 8): P8, (p9, 9): P9, (p10, 10): P10, (p11, 11): P11, (p12, 12): P12);
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, (p9, 9): P9, (p10, 10): P10, (p11, 11): P11, (p12, 12): P12, (p13, 13): P13);
188
189pub(super) unsafe fn ptrcall_arg<P: FromGodot, const N: isize>(
196 args_ptr: *const sys::GDExtensionConstTypePtr,
197 call_ctx: &CallContext,
198 call_type: sys::PtrcallType,
199) -> P {
200 let offset_ptr = unsafe { *args_ptr.offset(N) };
202
203 let ffi = unsafe {
205 <P::Via as GodotType>::Ffi::from_arg_ptr(sys::force_mut_ptr(offset_ptr), call_type)
206 };
207
208 <P::Via as GodotType>::try_from_ffi(ffi)
209 .and_then(P::try_from_godot)
210 .unwrap_or_else(|err| param_error::<P>(call_ctx, N as i32, err))
211}
212
213pub(super) unsafe fn varcall_arg<P: FromGodot>(
219 arg: sys::GDExtensionConstVariantPtr,
220 call_ctx: &CallContext,
221 param_index: isize,
222) -> Result<P, CallError> {
223 let variant_ref = unsafe { Variant::borrow_var_sys(arg) };
225
226 variant_ref
227 .try_to_relaxed::<P>()
228 .map_err(|err| CallError::failed_param_conversion::<P>(call_ctx, param_index, err))
229}
230
231fn param_error<P>(call_ctx: &CallContext, index: i32, err: ConvertError) -> ! {
232 let param_ty = std::any::type_name::<P>();
233 panic!("in function `{call_ctx}` at parameter [{index}] of type {param_ty}: {err}");
234}
235
236fn assert_array_length<P: ParamTuple>(array: &[&Variant]) {
237 assert_eq!(
238 array.len(),
239 P::LEN,
240 "array {array:?} has wrong length, expected {} got {}",
241 P::LEN,
242 array.len()
243 );
244}
245
246#[cfg(test)] #[cfg_attr(published_docs, doc(cfg(test)))]
249mod test {
250 use super::*;
251
252 #[test]
253 fn format_args_test() {
254 assert_eq!(&().format_args(), "");
255 assert_eq!(&(1, 2, 3).format_args(), "1, 2, 3");
256 }
257
258 #[test]
259 fn count_idents_test() {
260 assert_eq!(2, count_idents!(a b));
261 assert_eq!(0, count_idents!());
262 assert_eq!(5, count_idents!(a b b a d));
263 }
264}