godot_core/builtin/variant/
impls.rs

1/*
2 * Copyright (c) godot-rust; Bromeon and contributors.
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
6 */
7
8use godot_ffi as sys;
9use sys::GodotFfi;
10
11use crate::builtin::*;
12use crate::global;
13use crate::meta::error::{ConvertError, FromVariantError};
14use crate::meta::sealed::Sealed;
15use crate::meta::{
16    ArrayElement, GodotFfiVariant, GodotType, PropertyHintInfo, PropertyInfo, RefArg,
17};
18use crate::task::{impl_dynamic_send, DynamicSend, IntoDynamicSend, ThreadConfined};
19
20// For godot-cpp, see https://github.com/godotengine/godot-cpp/blob/master/include/godot_cpp/core/type_info.hpp.
21
22// ----------------------------------------------------------------------------------------------------------------------------------------------
23// Macro definitions
24
25// Historical note: In Godot 4.0, certain types needed to be passed as initialized pointers in their from_variant implementations, because
26// 4.0 used `*ptr = value` to return the type, and some types in C++ override `operator=` in a way that requires the pointer to be initialized.
27// However, those same types would cause memory leaks in Godot 4.1 if pre-initialized. A compat layer `new_with_uninit_or_init()` addressed this.
28// As these Godot versions are no longer supported, the current implementation uses `new_with_uninit()` uniformly for all versions.
29macro_rules! impl_ffi_variant {
30    (ref $T:ty, $from_fn:ident, $to_fn:ident $(; $GodotTy:ident)?) => {
31        impl_ffi_variant!(@impls by_ref; $T, $from_fn, $to_fn $(; $GodotTy)?);
32    };
33    ($T:ty, $from_fn:ident, $to_fn:ident $(; $GodotTy:ident)?) => {
34        impl_ffi_variant!(@impls by_val; $T, $from_fn, $to_fn $(; $GodotTy)?);
35    };
36
37    // Implementations
38    (@impls $by_ref_or_val:ident; $T:ty, $from_fn:ident, $to_fn:ident $(; $GodotTy:ident)?) => {
39        impl GodotFfiVariant for $T {
40            fn ffi_to_variant(&self) -> Variant {
41                let variant = unsafe {
42                    Variant::new_with_var_uninit(|variant_ptr| {
43                        let converter = sys::builtin_fn!($from_fn);
44                        converter(variant_ptr, sys::SysPtr::force_mut(self.sys()));
45                    })
46                };
47
48                variant
49            }
50
51            fn ffi_from_variant(variant: &Variant) -> Result<Self, ConvertError> {
52                // Type check -- at the moment, a strict match is required.
53                if variant.get_type() != Self::VARIANT_TYPE.variant_as_nil() {
54                    return Err(FromVariantError::BadType {
55                        expected: Self::VARIANT_TYPE.variant_as_nil(),
56                        actual: variant.get_type(),
57                    }
58                    .into_error(variant.clone()));
59                }
60
61                let result = unsafe {
62                    Self::new_with_uninit(|self_ptr| {
63                        let converter = sys::builtin_fn!($to_fn);
64                        converter(self_ptr, sys::SysPtr::force_mut(variant.var_sys()));
65                    })
66                };
67
68                Ok(result)
69            }
70        }
71
72        impl GodotType for $T {
73            type Ffi = Self;
74            impl_ffi_variant!(@assoc_to_ffi $by_ref_or_val);
75
76            fn into_ffi(self) -> Self::Ffi {
77                self
78            }
79
80            fn try_from_ffi(ffi: Self::Ffi) -> Result<Self, ConvertError> {
81                Ok(ffi)
82            }
83
84            impl_ffi_variant!(@godot_type_name $T $(, $GodotTy)?);
85        }
86
87        impl ArrayElement for $T {}
88    };
89
90    (@godot_type_name $T:ty) => {
91        fn godot_type_name() -> String {
92            stringify!($T).into()
93        }
94    };
95
96    (@godot_type_name $T:ty, $godot_type_name:ident) => {
97        fn godot_type_name() -> String {
98            stringify!($godot_type_name).into()
99        }
100    };
101
102    (@assoc_to_ffi by_ref) => {
103        type ToFfi<'a> =  RefArg<'a, Self>;
104
105        fn to_ffi(&self) -> Self::ToFfi<'_> {
106            RefArg::new(self)
107        }
108    };
109
110    (@assoc_to_ffi by_val) => {
111        type ToFfi<'a> = Self;
112
113        fn to_ffi(&self) -> Self::ToFfi<'_> {
114            self.clone()
115        }
116    };
117}
118
119// ----------------------------------------------------------------------------------------------------------------------------------------------
120// General impls
121
122#[rustfmt::skip]
123#[allow(clippy::module_inception)]
124mod impls {
125    use super::*;
126
127    // IMPORTANT: the presence/absence of `ref` here should be aligned with the ArgPassing variant
128    // used in codegen get_builtin_arg_passing().
129
130    impl_ffi_variant!(bool, bool_to_variant, bool_from_variant);
131    impl_ffi_variant!(i64, int_to_variant, int_from_variant; int);
132    impl_ffi_variant!(f64, float_to_variant, float_from_variant; float);
133    impl_ffi_variant!(Vector2, vector2_to_variant, vector2_from_variant);
134    impl_ffi_variant!(Vector3, vector3_to_variant, vector3_from_variant);
135    impl_ffi_variant!(Vector4, vector4_to_variant, vector4_from_variant);
136    impl_ffi_variant!(Vector2i, vector2i_to_variant, vector2i_from_variant);
137    impl_ffi_variant!(Vector3i, vector3i_to_variant, vector3i_from_variant);
138    impl_ffi_variant!(Vector4i, vector4i_to_variant, vector4i_from_variant);
139    impl_ffi_variant!(Quaternion, quaternion_to_variant, quaternion_from_variant);
140    impl_ffi_variant!(Transform2D, transform_2d_to_variant, transform_2d_from_variant);
141    impl_ffi_variant!(Transform3D, transform_3d_to_variant, transform_3d_from_variant);
142    impl_ffi_variant!(Basis, basis_to_variant, basis_from_variant);
143    impl_ffi_variant!(Projection, projection_to_variant, projection_from_variant);
144    impl_ffi_variant!(Plane, plane_to_variant, plane_from_variant);
145    impl_ffi_variant!(Rect2, rect2_to_variant, rect2_from_variant);
146    impl_ffi_variant!(Rect2i, rect2i_to_variant, rect2i_from_variant);
147    impl_ffi_variant!(Aabb, aabb_to_variant, aabb_from_variant; AABB);
148    impl_ffi_variant!(Color, color_to_variant, color_from_variant);
149    impl_ffi_variant!(Rid, rid_to_variant, rid_from_variant; RID);
150    impl_ffi_variant!(ref GString, string_to_variant, string_from_variant; String);
151    impl_ffi_variant!(ref StringName, string_name_to_variant, string_name_from_variant);
152    impl_ffi_variant!(ref NodePath, node_path_to_variant, node_path_from_variant);
153    impl_ffi_variant!(ref Dictionary, dictionary_to_variant, dictionary_from_variant);
154    impl_ffi_variant!(ref Signal, signal_to_variant, signal_from_variant);
155    impl_ffi_variant!(ref Callable, callable_to_variant, callable_from_variant);
156}
157
158// ----------------------------------------------------------------------------------------------------------------------------------------------
159// Async trait support
160
161impl<T: ArrayElement> Sealed for ThreadConfined<Array<T>> {}
162
163unsafe impl<T: ArrayElement> DynamicSend for ThreadConfined<Array<T>> {
164    type Inner = Array<T>;
165    fn extract_if_safe(self) -> Option<Self::Inner> {
166        self.extract()
167    }
168}
169
170impl<T: ArrayElement> IntoDynamicSend for Array<T> {
171    type Target = ThreadConfined<Array<T>>;
172    fn into_dynamic_send(self) -> Self::Target {
173        ThreadConfined::new(self)
174    }
175}
176
177impl_dynamic_send!(
178    Send;
179    bool, u8, u16, u32, u64, i8, i16, i32, i64, f32, f64
180);
181
182impl_dynamic_send!(
183    Send;
184    StringName, Color, Rid,
185    Vector2, Vector2i, Vector2Axis,
186    Vector3, Vector3i, Vector3Axis,
187    Vector4, Vector4i,
188    Rect2, Rect2i, Aabb,
189    Transform2D, Transform3D, Basis,
190    Plane, Quaternion, Projection
191);
192
193impl_dynamic_send!(
194    !Send;
195    Variant, NodePath, GString, Dictionary, Callable, Signal,
196    PackedByteArray, PackedInt32Array, PackedInt64Array, PackedFloat32Array, PackedFloat64Array, PackedStringArray,
197    PackedVector2Array, PackedVector3Array, PackedColorArray
198);
199
200// This should be kept in sync with crate::registry::signal::variadic.
201impl_dynamic_send!(tuple; );
202impl_dynamic_send!(tuple; arg1: A1);
203impl_dynamic_send!(tuple; arg1: A1, arg2: A2);
204impl_dynamic_send!(tuple; arg1: A1, arg2: A2, arg3: A3);
205impl_dynamic_send!(tuple; arg1: A1, arg2: A2, arg3: A3, arg4: A4);
206impl_dynamic_send!(tuple; arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5);
207impl_dynamic_send!(tuple; arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, arg6: A6);
208impl_dynamic_send!(tuple; arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, arg6: A6, arg7: A7);
209impl_dynamic_send!(tuple; arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, arg6: A6, arg7: A7, arg8: A8);
210impl_dynamic_send!(tuple; arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, arg6: A6, arg7: A7, arg8: A8, arg9: A9);
211
212#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
213mod api_4_3 {
214    use crate::task::impl_dynamic_send;
215
216    impl_dynamic_send!(!Send; PackedVector4Array);
217}
218
219// ----------------------------------------------------------------------------------------------------------------------------------------------
220// Internal verification
221
222// Compile time check that we cover all the Variant types with trait implementations for:
223// - IntoDynamicSend
224// - DynamicSend
225// - GodotType
226// - ArrayElement
227const _: () = {
228    use crate::classes::Object;
229    use crate::obj::{Gd, IndexEnum};
230
231    const fn variant_type<T: crate::task::IntoDynamicSend + GodotType + ArrayElement>(
232    ) -> VariantType {
233        <T::Ffi as sys::GodotFfi>::VARIANT_TYPE.variant_as_nil()
234    }
235
236    const NIL: VariantType = variant_type::<Variant>();
237    const BOOL: VariantType = variant_type::<bool>();
238    const I64: VariantType = variant_type::<i64>();
239    const F64: VariantType = variant_type::<f64>();
240    const GSTRING: VariantType = variant_type::<GString>();
241
242    const VECTOR2: VariantType = variant_type::<Vector2>();
243    const VECTOR2I: VariantType = variant_type::<Vector2i>();
244    const RECT2: VariantType = variant_type::<Rect2>();
245    const RECT2I: VariantType = variant_type::<Rect2i>();
246    const VECTOR3: VariantType = variant_type::<Vector3>();
247    const VECTOR3I: VariantType = variant_type::<Vector3i>();
248    const TRANSFORM2D: VariantType = variant_type::<Transform2D>();
249    const TRANSFORM3D: VariantType = variant_type::<Transform3D>();
250    const VECTOR4: VariantType = variant_type::<Vector4>();
251    const VECTOR4I: VariantType = variant_type::<Vector4i>();
252    const PLANE: VariantType = variant_type::<Plane>();
253    const QUATERNION: VariantType = variant_type::<Quaternion>();
254    const AABB: VariantType = variant_type::<Aabb>();
255    const BASIS: VariantType = variant_type::<Basis>();
256    const PROJECTION: VariantType = variant_type::<Projection>();
257    const COLOR: VariantType = variant_type::<Color>();
258    const STRING_NAME: VariantType = variant_type::<StringName>();
259    const NODE_PATH: VariantType = variant_type::<NodePath>();
260    const RID: VariantType = variant_type::<Rid>();
261    const OBJECT: VariantType = variant_type::<Gd<Object>>();
262    const CALLABLE: VariantType = variant_type::<Callable>();
263    const SIGNAL: VariantType = variant_type::<Signal>();
264    const DICTIONARY: VariantType = variant_type::<Dictionary>();
265    const ARRAY: VariantType = variant_type::<VariantArray>();
266    const PACKED_BYTE_ARRAY: VariantType = variant_type::<PackedByteArray>();
267    const PACKED_INT32_ARRAY: VariantType = variant_type::<PackedInt32Array>();
268    const PACKED_INT64_ARRAY: VariantType = variant_type::<PackedInt64Array>();
269    const PACKED_FLOAT32_ARRAY: VariantType = variant_type::<PackedFloat32Array>();
270    const PACKED_FLOAT64_ARRAY: VariantType = variant_type::<PackedFloat64Array>();
271    const PACKED_STRING_ARRAY: VariantType = variant_type::<PackedStringArray>();
272    const PACKED_VECTOR2_ARRAY: VariantType = variant_type::<PackedVector2Array>();
273    const PACKED_VECTOR3_ARRAY: VariantType = variant_type::<PackedVector3Array>();
274    const PACKED_COLOR_ARRAY: VariantType = variant_type::<PackedColorArray>();
275
276    #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
277    const PACKED_VECTOR4_ARRAY: VariantType = variant_type::<PackedVector4Array>();
278
279    const MAX: i32 = VariantType::ENUMERATOR_COUNT as i32;
280
281    // The matched value is not relevant, we just want to ensure that the full list from 0 to MAX is covered.
282    #[deny(unreachable_patterns)]
283    match VariantType::STRING {
284        VariantType { ord: i32::MIN..0 } => panic!("ord is out of defined range!"),
285        NIL => (),
286        BOOL => (),
287        I64 => (),
288        F64 => (),
289        GSTRING => (),
290        VECTOR2 => (),
291        VECTOR2I => (),
292        RECT2 => (),
293        RECT2I => (),
294        VECTOR3 => (),
295        VECTOR3I => (),
296        TRANSFORM2D => (),
297        VECTOR4 => (),
298        VECTOR4I => (),
299        PLANE => (),
300        QUATERNION => (),
301        AABB => (),
302        BASIS => (),
303        TRANSFORM3D => (),
304        PROJECTION => (),
305        COLOR => (),
306        STRING_NAME => (),
307        NODE_PATH => (),
308        RID => (),
309        OBJECT => (),
310        CALLABLE => (),
311        SIGNAL => (),
312        DICTIONARY => (),
313        ARRAY => (),
314        PACKED_BYTE_ARRAY => (),
315        PACKED_INT32_ARRAY => (),
316        PACKED_INT64_ARRAY => (),
317        PACKED_FLOAT32_ARRAY => (),
318        PACKED_FLOAT64_ARRAY => (),
319        PACKED_STRING_ARRAY => (),
320        PACKED_VECTOR2_ARRAY => (),
321        PACKED_VECTOR3_ARRAY => (),
322        PACKED_COLOR_ARRAY => (),
323
324        #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
325        PACKED_VECTOR4_ARRAY => (),
326        VariantType { ord: MAX.. } => panic!("ord is out of defined range!"),
327    }
328};
329
330// ----------------------------------------------------------------------------------------------------------------------------------------------
331// Explicit impls
332
333// Unit
334impl GodotFfiVariant for () {
335    fn ffi_to_variant(&self) -> Variant {
336        Variant::nil()
337    }
338
339    fn ffi_from_variant(variant: &Variant) -> Result<Self, ConvertError> {
340        if variant.is_nil() {
341            return Ok(());
342        }
343
344        Err(FromVariantError::BadType {
345            expected: VariantType::NIL,
346            actual: variant.get_type(),
347        }
348        .into_error(variant.clone()))
349    }
350}
351
352impl GodotType for () {
353    type Ffi = ();
354    type ToFfi<'a> = ();
355
356    fn to_ffi(&self) -> Self::ToFfi<'_> {}
357
358    fn into_ffi(self) -> Self::Ffi {}
359
360    fn try_from_ffi(_: Self::Ffi) -> Result<Self, ConvertError> {
361        Ok(())
362    }
363
364    fn godot_type_name() -> String {
365        "Variant".to_string()
366    }
367}
368
369impl GodotFfiVariant for Variant {
370    fn ffi_to_variant(&self) -> Variant {
371        self.clone()
372    }
373
374    fn ffi_from_variant(variant: &Variant) -> Result<Self, ConvertError> {
375        Ok(variant.clone())
376    }
377}
378
379impl GodotType for Variant {
380    type Ffi = Variant;
381    type ToFfi<'a> = RefArg<'a, Variant>;
382
383    fn to_ffi(&self) -> Self::ToFfi<'_> {
384        RefArg::new(self)
385    }
386
387    fn into_ffi(self) -> Self::Ffi {
388        self
389    }
390
391    fn try_from_ffi(ffi: Self::Ffi) -> Result<Self, ConvertError> {
392        Ok(ffi)
393    }
394
395    fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata {
396        sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_NONE
397    }
398
399    fn property_info(property_name: &str) -> PropertyInfo {
400        PropertyInfo {
401            variant_type: Self::VARIANT_TYPE.variant_as_nil(),
402            class_id: Self::class_id(),
403            property_name: StringName::from(property_name),
404            hint_info: PropertyHintInfo::none(),
405            usage: global::PropertyUsageFlags::DEFAULT | global::PropertyUsageFlags::NIL_IS_VARIANT,
406        }
407    }
408
409    fn godot_type_name() -> String {
410        "Variant".to_string()
411    }
412}