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, CallResult};
18use crate::meta::{
19 ArgPassing, CallContext, FromGodot, GodotConvert, GodotType, InParamTuple, OutParamTuple,
20 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 arg_count: usize,
60 default_values: &[Variant],
61 call_ctx: &crate::meta::CallContext,
62 ) -> CallResult<Self> {
63 if arg_count == Self::LEN {
65 let param_tuple = (
66 $(
67 unsafe { varcall_arg::<$P>(*args_ptr.add($n), call_ctx, $n)? },
68 )*
69 );
70 return Ok(param_tuple);
71 }
72
73 let mut all_args = Vec::with_capacity(Self::LEN);
75
76 for i in 0..arg_count {
78 all_args.push(unsafe { *args_ptr.add(i) });
79 }
80
81 let required_param_count = Self::LEN - default_values.len();
83 let first_missing_index = arg_count - required_param_count;
84 for i in first_missing_index..default_values.len() {
85 all_args.push(default_values[i].var_sys());
86 }
87
88 let param_tuple = (
90 $(
91 unsafe { varcall_arg::<$P>(all_args[$n], call_ctx, $n)? },
93 )*
94 );
95
96 Ok(param_tuple)
97 }
98
99 unsafe fn from_ptrcall_args(
100 args_ptr: *const sys::GDExtensionConstTypePtr,
101 call_type: sys::PtrcallType,
102 call_ctx: &crate::meta::CallContext,
103 ) -> CallResult<Self> {
104 let tuple = (
105 $(
106 unsafe { ptrcall_arg::<$P, $n>(args_ptr, call_ctx, call_type)? },
109 )*
110 );
111
112 Ok(tuple) }
114
115 fn from_variant_array(array: &[&Variant]) -> Self {
116 assert_array_length::<Self>(array);
117 let mut iter = array.iter();
118 (
119 $(
120 {
121 let variant = iter.next().unwrap_or_else(
122 || panic!("ParamTuple: {} access out-of-bounds (len {})", stringify!($p), array.len()));
123
124 variant.to_relaxed_or_panic(
125 || format!("ParamTuple: failed to convert parameter {}", stringify!($p)))
126 },
127 )*
128 )
129 }
130 }
131
132 impl<$($P),*> OutParamTuple for ($($P,)*) where $($P: ToGodot<Via: Clone> + fmt::Debug,)* {
133 fn with_variants<F, R>(self, f: F) -> R
134 where
135 F: FnOnce(&[Variant]) -> R,
136 {
137 let variant_args = [
138 $(
139 <$P::Pass as ArgPassing>::ref_to_variant(&self.$n),
140 )*
141 ];
142
143 f(&variant_args)
144 }
145
146 fn with_variant_pointers<F, R>(self, f: F) -> R
147 where
148 F: FnOnce(&[godot_ffi::GDExtensionConstVariantPtr]) -> R,
149 {
150 self.with_variants(|variants| {
151 let sys_args = [
152 $(
153 Variant::var_sys(&variants[$n]),
154 )*
155 ];
156 f(&sys_args)
157 })
158 }
159
160 fn with_type_pointers<F, R>(self, f: F) -> R
161 where
162 F: FnOnce(&[godot_ffi::GDExtensionConstTypePtr]) -> R,
163 {
164 let ffi_args = (
166 $(
167 <$P::Pass as ArgPassing>::ref_to_ffi(&self.$n),
168 )*
169 );
170
171 let ptr_args = [
172 $(
173 sys::GodotFfi::as_arg_ptr(&ffi_args.$n),
174 )*
175 ];
176
177 f(&ptr_args)
178 }
179
180 fn to_variant_array(&self) -> Vec<Variant> {
181 let ($($p,)*) = self;
182
183 vec![
184 $( $p.to_variant(), )*
185 ]
186 }
187 }
188 };
189}
190
191#[allow(unused_variables, unused_mut, clippy::unused_unit)]
192mod unit_impl {
193 use super::*;
194 unsafe_impl_param_tuple!();
195}
196unsafe_impl_param_tuple!((p0, 0): P0);
197unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1);
198unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2);
199unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3);
200unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4);
201unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5);
202unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5, (p6, 6): P6);
203unsafe_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);
204unsafe_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);
205unsafe_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);
206unsafe_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);
207unsafe_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);
208unsafe_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);
209unsafe_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);
210
211pub(super) unsafe fn ptrcall_arg<P: FromGodot, const N: usize>(
218 args_ptr: *const sys::GDExtensionConstTypePtr,
219 call_ctx: &CallContext,
220 call_type: sys::PtrcallType,
221) -> CallResult<P> {
222 let offset_ptr = unsafe { *args_ptr.add(N) };
224
225 let ffi = unsafe {
227 <P::Via as GodotType>::Ffi::from_arg_ptr(sys::force_mut_ptr(offset_ptr), call_type)
228 };
229
230 <P::Via as GodotType>::try_from_ffi(ffi)
231 .and_then(P::try_from_godot)
232 .map_err(|err| CallError::failed_param_conversion::<P>(call_ctx, N, err))
233}
234
235pub(super) unsafe fn varcall_arg<P: FromGodot>(
241 arg: sys::GDExtensionConstVariantPtr,
242 call_ctx: &CallContext,
243 param_index: usize,
244) -> CallResult<P> {
245 let variant_ref = unsafe { Variant::borrow_var_sys(arg) };
247
248 variant_ref
249 .try_to_relaxed::<P>()
250 .map_err(|err| CallError::failed_param_conversion::<P>(call_ctx, param_index, err))
251}
252
253fn assert_array_length<P: ParamTuple>(array: &[&Variant]) {
254 assert_eq!(
255 array.len(),
256 P::LEN,
257 "array {array:?} has wrong length, expected {} got {}",
258 P::LEN,
259 array.len()
260 );
261}
262
263#[cfg(test)] #[cfg_attr(published_docs, doc(cfg(test)))]
266mod test {
267 use super::*;
268
269 #[test]
270 fn format_args_test() {
271 assert_eq!(&().format_args(), "");
272 assert_eq!(&(1, 2, 3).format_args(), "1, 2, 3");
273 }
274
275 #[test]
276 fn count_idents_test() {
277 assert_eq!(2, count_idents!(a b));
278 assert_eq!(0, count_idents!());
279 assert_eq!(5, count_idents!(a b b a d));
280 }
281}