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, EngineFromGodot, EngineToGodot, FromGodot, GodotConvert, GodotType,
20 InParamTuple, OutParamTuple, ParamTuple, TupleFromGodot,
21};
22use crate::registry::method::MethodParamOrReturnInfo;
23
24macro_rules! count_idents {
25 () => { 0 };
26 ($id:ident $($rest:ident)*) => { 1 + count_idents!($($rest)*)};
27}
28
29macro_rules! unsafe_impl_param_tuple {
30 ($(($p:ident, $n:tt): $P:ident),*) => {
31 impl<$($P: FromGodot + fmt::Debug),*> TupleFromGodot for ($($P,)*) {}
32
33 impl<$($P),*> ParamTuple for ($($P,)*) where $($P: GodotConvert + fmt::Debug),* {
34 const LEN: usize = count_idents!($($P)*);
35
36 #[doc(hidden)]
37 fn param_info(
38 index: usize,
39 param_name: &str,
40 ) -> Option<MethodParamOrReturnInfo> {
41 match index {
42 $(
43 $n => Some(MethodParamOrReturnInfo::for_parameter::<$P>(param_name)),
44 )*
45 _ => None,
46 }
47 }
48
49 fn format_args(&self) -> String {
50 format!(
51 concat!("" $(, "{", $n, ":?}",)", "*),
54 $(self.$n),*
55 )
56 }
57 }
58
59 impl<$($P),*> InParamTuple for ($($P,)*) where $($P: EngineFromGodot + fmt::Debug),* {
60 unsafe fn from_varcall_args(
61 args_ptr: *const sys::GDExtensionConstVariantPtr,
62 arg_count: usize,
63 default_values: &[Variant],
64 call_ctx: &crate::meta::CallContext,
65 ) -> CallResult<Self> {
66 if arg_count == Self::LEN {
68 let param_tuple = (
69 $(
70 unsafe { varcall_arg::<$P>(*args_ptr.add($n), call_ctx, $n)? },
71 )*
72 );
73 return Ok(param_tuple);
74 }
75
76 let mut all_args = Vec::with_capacity(Self::LEN);
78
79 for i in 0..arg_count {
81 all_args.push(unsafe { *args_ptr.add(i) });
82 }
83
84 let required_param_count = Self::LEN - default_values.len();
86 let first_missing_index = arg_count - required_param_count;
87 for i in first_missing_index..default_values.len() {
88 all_args.push(default_values[i].var_sys());
89 }
90
91 let param_tuple = (
93 $(
94 unsafe { varcall_arg::<$P>(all_args[$n], call_ctx, $n)? },
96 )*
97 );
98
99 Ok(param_tuple)
100 }
101
102 unsafe fn from_ptrcall_args(
103 args_ptr: *const sys::GDExtensionConstTypePtr,
104 call_type: sys::PtrcallType,
105 call_ctx: &crate::meta::CallContext,
106 ) -> CallResult<Self>
107 where
108 $($P: EngineFromGodot,)*
109 {
110 let tuple = (
111 $(
112 unsafe { ptrcall_arg::<$P, $n>(args_ptr, call_ctx, call_type)? },
115 )*
116 );
117
118 Ok(tuple) }
120
121 fn from_variant_array(array: &[&Variant]) -> Self {
122 assert_array_length::<Self>(array);
123 let mut iter = array.iter();
124 (
125 $(
126 {
127 let variant = iter.next().unwrap_or_else(
128 || panic!("ParamTuple: {} access out-of-bounds (len {})", stringify!($p), array.len()));
129
130 variant.to_relaxed_or_panic(
131 || format!("ParamTuple: failed to convert parameter {}", stringify!($p)))
132 },
133 )*
134 )
135 }
136 }
137
138 impl<$($P),*> OutParamTuple for ($($P,)*) where $($P: EngineToGodot + fmt::Debug,)* {
139 fn with_variants<F, R>(self, f: F) -> R
140 where
141 F: FnOnce(&[Variant]) -> R,
142 {
143 let variant_args = [
144 $(
145 <$P::Pass as ArgPassing>::ref_to_variant(&self.$n),
146 )*
147 ];
148
149 f(&variant_args)
150 }
151
152 fn with_variant_pointers<F, R>(self, f: F) -> R
153 where
154 F: FnOnce(&[godot_ffi::GDExtensionConstVariantPtr]) -> R,
155 {
156 self.with_variants(|variants| {
157 let sys_args = [
158 $(
159 Variant::var_sys(&variants[$n]),
160 )*
161 ];
162 f(&sys_args)
163 })
164 }
165
166 fn with_type_pointers<F, R>(self, f: F) -> R
167 where
168 F: FnOnce(&[godot_ffi::GDExtensionConstTypePtr]) -> R,
169 {
170 let ffi_args = (
172 $(
173 <$P::Pass as ArgPassing>::ref_to_ffi(&self.$n),
174 )*
175 );
176
177 let ptr_args = [
178 $(
179 sys::GodotFfi::as_arg_ptr(&ffi_args.$n),
180 )*
181 ];
182
183 f(&ptr_args)
184 }
185
186 fn to_variant_array(&self) -> Vec<Variant> {
187 vec![
189 $(
190 <$P::Pass as ArgPassing>::ref_to_variant(&self.$n),
191 )*
192 ]
193 }
194 }
195 };
196}
197
198#[allow(unused_variables, unused_mut, clippy::unused_unit)]
199mod unit_impl {
200 use super::*;
201 unsafe_impl_param_tuple!();
202}
203unsafe_impl_param_tuple!((p0, 0): P0);
204unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1);
205unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2);
206unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3);
207unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4);
208unsafe_impl_param_tuple!((p0, 0): P0, (p1, 1): P1, (p2, 2): P2, (p3, 3): P3, (p4, 4): P4, (p5, 5): P5);
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);
210unsafe_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);
211unsafe_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);
212unsafe_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);
213unsafe_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);
214unsafe_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);
215unsafe_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);
216unsafe_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);
217
218pub(super) unsafe fn ptrcall_arg<P: EngineFromGodot, const N: usize>(
225 args_ptr: *const sys::GDExtensionConstTypePtr,
226 call_ctx: &CallContext,
227 call_type: sys::PtrcallType,
228) -> CallResult<P> {
229 let offset_ptr = unsafe { *args_ptr.add(N) };
231
232 let ffi = unsafe {
234 <P::Via as GodotType>::Ffi::from_arg_ptr(sys::force_mut_ptr(offset_ptr), call_type)
235 };
236
237 <P::Via as GodotType>::try_from_ffi(ffi)
238 .and_then(P::engine_try_from_godot)
239 .map_err(|err| CallError::failed_param_conversion::<P>(call_ctx, N, err))
240}
241
242pub(super) unsafe fn varcall_arg<P: EngineFromGodot>(
248 arg: sys::GDExtensionConstVariantPtr,
249 call_ctx: &CallContext,
250 param_index: usize,
251) -> CallResult<P> {
252 let variant_ref = unsafe { Variant::borrow_var_sys(arg) };
254
255 variant_ref
256 .engine_try_to_relaxed::<P>()
257 .map_err(|err| CallError::failed_param_conversion::<P>(call_ctx, param_index, err))
258}
259
260fn assert_array_length<P: ParamTuple>(array: &[&Variant]) {
261 assert_eq!(
262 array.len(),
263 P::LEN,
264 "array {array:?} has wrong length, expected {} got {}",
265 P::LEN,
266 array.len()
267 );
268}
269
270#[cfg(test)] #[cfg_attr(published_docs, doc(cfg(test)))]
273mod test {
274 use super::*;
275
276 #[test]
277 fn format_args_test() {
278 assert_eq!(&().format_args(), "");
279 assert_eq!(&(1, 2, 3).format_args(), "1, 2, 3");
280 }
281
282 #[test]
283 fn count_idents_test() {
284 assert_eq!(2, count_idents!(a b));
285 assert_eq!(0, count_idents!());
286 assert_eq!(5, count_idents!(a b b a d));
287 }
288}