godot_core/meta/godot_convert/
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;
9
10use crate::builtin::{Array, Variant};
11use crate::meta;
12use crate::meta::error::{ConvertError, ErrorKind, FromFfiError, FromVariantError};
13use crate::meta::{
14    ArrayElement, ClassId, FromGodot, GodotConvert, GodotNullableFfi, GodotType, PropertyHintInfo,
15    PropertyInfo, ToGodot,
16};
17use crate::registry::method::MethodParamOrReturnInfo;
18
19// The following ToGodot/FromGodot/Convert impls are auto-generated for each engine type, co-located with their definitions:
20// - enum
21// - const/mut pointer to native struct
22
23// ----------------------------------------------------------------------------------------------------------------------------------------------
24// Option<T>
25
26impl<T> GodotType for Option<T>
27where
28    T: GodotType,
29    T::Ffi: GodotNullableFfi,
30    for<'f> T::ToFfi<'f>: GodotNullableFfi,
31{
32    type Ffi = T::Ffi;
33
34    type ToFfi<'f> = T::ToFfi<'f>;
35
36    fn to_ffi(&self) -> Self::ToFfi<'_> {
37        GodotNullableFfi::flatten_option(self.as_ref().map(|t| t.to_ffi()))
38    }
39
40    fn into_ffi(self) -> Self::Ffi {
41        GodotNullableFfi::flatten_option(self.map(|t| t.into_ffi()))
42    }
43
44    fn try_from_ffi(ffi: Self::Ffi) -> Result<Self, ConvertError> {
45        if ffi.is_null() {
46            return Ok(None);
47        }
48
49        GodotType::try_from_ffi(ffi).map(Some)
50    }
51
52    fn from_ffi(ffi: Self::Ffi) -> Self {
53        if ffi.is_null() {
54            return None;
55        }
56
57        Some(GodotType::from_ffi(ffi))
58    }
59
60    fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata {
61        T::param_metadata()
62    }
63
64    fn class_id() -> ClassId {
65        T::class_id()
66    }
67
68    fn property_info(property_name: &str) -> PropertyInfo {
69        T::property_info(property_name)
70    }
71
72    fn property_hint_info() -> PropertyHintInfo {
73        T::property_hint_info()
74    }
75
76    fn argument_info(property_name: &str) -> MethodParamOrReturnInfo {
77        T::argument_info(property_name)
78    }
79
80    fn return_info() -> Option<MethodParamOrReturnInfo> {
81        T::return_info()
82    }
83
84    fn godot_type_name() -> String {
85        T::godot_type_name()
86    }
87
88    // Only relevant for object types T.
89    fn as_object_arg(&self) -> meta::ObjectArg<'_> {
90        match self {
91            Some(inner) => inner.as_object_arg(),
92            None => meta::ObjectArg::null(),
93        }
94    }
95}
96
97impl<T> GodotConvert for Option<T>
98where
99    T: GodotConvert,
100    Option<T::Via>: GodotType,
101{
102    type Via = Option<T::Via>;
103}
104
105impl<T> ToGodot for Option<T>
106where
107    // Currently limited to holding objects -> needed to establish to_godot() relation T::to_godot() = Option<&T::Via>.
108    T: ToGodot<Pass = meta::ByObject>,
109    // Extra Clone bound for to_godot_owned(); might be extracted in the future.
110    T::Via: Clone,
111    // T::Via must be a Godot nullable type (to support the None case).
112    for<'f> T::Via: GodotType<
113        // Associated types need to be nullable.
114        Ffi: GodotNullableFfi,
115        ToFfi<'f>: GodotNullableFfi,
116    >,
117    // Previously used bound, not needed right now but don't remove: Option<T::Via>: GodotType,
118{
119    // Basically ByRef, but allows Option<T> -> Option<&T::Via> conversion.
120    type Pass = meta::ByOption<T::Via>;
121
122    fn to_godot(&self) -> Option<&T::Via> {
123        self.as_ref().map(T::to_godot)
124    }
125
126    fn to_godot_owned(&self) -> Option<T::Via>
127    where
128        Self::Via: Clone,
129    {
130        self.as_ref().map(T::to_godot_owned)
131    }
132
133    fn to_variant(&self) -> Variant {
134        match self {
135            Some(inner) => inner.to_variant(),
136            None => Variant::nil(),
137        }
138    }
139}
140
141impl<T: FromGodot> FromGodot for Option<T>
142where
143    Option<T::Via>: GodotType,
144{
145    fn try_from_godot(via: Self::Via) -> Result<Self, ConvertError> {
146        match via {
147            Some(via) => T::try_from_godot(via).map(Some),
148            None => Ok(None),
149        }
150    }
151
152    fn from_godot(via: Self::Via) -> Self {
153        via.map(T::from_godot)
154    }
155
156    fn try_from_variant(variant: &Variant) -> Result<Self, ConvertError> {
157        // Note: this forwards to T::Via, not Self::Via (= Option<T>::Via).
158        // For Option<T>, there is a blanket impl GodotType, so case differentiations are not possible.
159        if T::Via::qualifies_as_special_none(variant) {
160            return Ok(None);
161        }
162
163        if variant.is_nil() {
164            return Ok(None);
165        }
166
167        let value = T::try_from_variant(variant)?;
168        Ok(Some(value))
169    }
170
171    fn from_variant(variant: &Variant) -> Self {
172        if variant.is_nil() {
173            return None;
174        }
175
176        Some(T::from_variant(variant))
177    }
178}
179
180// ----------------------------------------------------------------------------------------------------------------------------------------------
181// Scalars
182
183macro_rules! impl_godot_scalar {
184    ($T:ty as $Via:ty, $err:path, $param_metadata:expr) => {
185        impl GodotType for $T {
186            type Ffi = $Via;
187            type ToFfi<'f> = $Via;
188
189            fn to_ffi(&self) -> Self::ToFfi<'_> {
190                (*self).into()
191            }
192
193            fn into_ffi(self) -> Self::Ffi {
194                self.into()
195            }
196
197            fn try_from_ffi(ffi: Self::Ffi) -> Result<Self, ConvertError> {
198                Self::try_from(ffi).map_err(|_rust_err| {
199                    // rust_err is something like "out of range integral type conversion attempted", not adding extra information.
200                    // TODO consider passing value into error message, but how thread-safely? don't eagerly convert to string.
201                    $err.into_error(ffi)
202                })
203            }
204
205            impl_godot_scalar!(@shared_fns; $Via, $param_metadata);
206        }
207
208        // For integer types, we can validate the conversion.
209        impl ArrayElement for $T {
210            fn debug_validate_elements(array: &Array<Self>) -> Result<(), ConvertError> {
211                array.debug_validate_int_elements()
212            }
213        }
214
215        impl_godot_scalar!(@shared_traits; $T);
216    };
217
218    ($T:ty as $Via:ty, $param_metadata:expr; lossy) => {
219        impl GodotType for $T {
220            type Ffi = $Via;
221            type ToFfi<'f> = $Via;
222
223            fn to_ffi(&self) -> Self::ToFfi<'_> {
224                *self as $Via
225            }
226
227            fn into_ffi(self) -> Self::Ffi {
228                self as $Via
229            }
230
231            fn try_from_ffi(ffi: Self::Ffi) -> Result<Self, ConvertError> {
232                Ok(ffi as $T)
233            }
234
235            impl_godot_scalar!(@shared_fns; $Via, $param_metadata);
236        }
237
238        // For f32, conversion from f64 is lossy but will always succeed. Thus no debug validation needed.
239        impl ArrayElement for $T {}
240
241        impl_godot_scalar!(@shared_traits; $T);
242    };
243
244    (@shared_fns; $Via:ty, $param_metadata:expr) => {
245        fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata {
246            $param_metadata
247        }
248
249        fn godot_type_name() -> String {
250            <$Via as GodotType>::godot_type_name()
251        }
252    };
253
254    (@shared_traits; $T:ty) => {
255        impl GodotConvert for $T {
256            type Via = $T;
257        }
258
259        impl ToGodot for $T {
260            type Pass = meta::ByValue;
261
262            fn to_godot(&self) -> Self::Via {
263               *self
264            }
265        }
266
267        impl FromGodot for $T {
268            fn try_from_godot(via: Self::Via) -> Result<Self, ConvertError> {
269                Ok(via)
270            }
271        }
272    };
273}
274
275// `GodotType` for these three is implemented in `godot-core/src/builtin/variant/impls.rs`.
276meta::impl_godot_as_self!(bool: ByValue);
277meta::impl_godot_as_self!(i64: ByValue);
278meta::impl_godot_as_self!(f64: ByValue);
279meta::impl_godot_as_self!((): ByValue);
280
281// Also implements ArrayElement.
282impl_godot_scalar!(
283    i8 as i64,
284    FromFfiError::I8,
285    sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT8
286);
287impl_godot_scalar!(
288    u8 as i64,
289    FromFfiError::U8,
290    sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT8
291);
292impl_godot_scalar!(
293    i16 as i64,
294    FromFfiError::I16,
295    sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT16
296);
297impl_godot_scalar!(
298    u16 as i64,
299    FromFfiError::U16,
300    sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT16
301);
302impl_godot_scalar!(
303    i32 as i64,
304    FromFfiError::I32,
305    sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_INT32
306);
307impl_godot_scalar!(
308    u32 as i64,
309    FromFfiError::U32,
310    sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT32
311);
312impl_godot_scalar!(
313    f32 as f64,
314    sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_FLOAT;
315    lossy
316);
317
318// ----------------------------------------------------------------------------------------------------------------------------------------------
319// u64: manually implemented, to ensure that type is not altered during conversion.
320
321impl GodotType for u64 {
322    type Ffi = i64;
323    type ToFfi<'f> = i64;
324
325    fn to_ffi(&self) -> Self::ToFfi<'_> {
326        *self as i64
327    }
328
329    fn into_ffi(self) -> Self::Ffi {
330        self as i64
331    }
332
333    fn try_from_ffi(ffi: Self::Ffi) -> Result<Self, ConvertError> {
334        // Ok(ffi as u64)
335        Self::try_from(ffi).map_err(|_rust_err| FromFfiError::U64.into_error(ffi))
336    }
337
338    impl_godot_scalar!(@shared_fns; i64, sys::GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_UINT64);
339}
340
341impl GodotConvert for u64 {
342    type Via = u64;
343}
344
345impl ToGodot for u64 {
346    type Pass = meta::ByValue;
347
348    fn to_godot(&self) -> Self::Via {
349        *self
350    }
351
352    fn to_variant(&self) -> Variant {
353        // TODO panic doesn't fit the trait's infallibility too well; maybe in the future try_to_godot/try_to_variant() methods are possible.
354        i64::try_from(*self)
355            .map(|v| v.to_variant())
356            .unwrap_or_else(|_| {
357                panic!("to_variant(): u64 value {self} is not representable inside Variant, which can only store i64 integers")
358            })
359    }
360}
361
362impl FromGodot for u64 {
363    fn try_from_godot(via: Self::Via) -> Result<Self, ConvertError> {
364        Ok(via)
365    }
366
367    fn try_from_variant(variant: &Variant) -> Result<Self, ConvertError> {
368        // Fail for values that are not representable as u64.
369        let value = variant.try_to::<i64>()?;
370
371        u64::try_from(value).map_err(|_rust_err| {
372            // TODO maybe use better error enumerator
373            FromVariantError::BadValue.into_error(value)
374        })
375    }
376}
377
378// ----------------------------------------------------------------------------------------------------------------------------------------------
379// Collections
380
381impl<T: ArrayElement> GodotConvert for Vec<T> {
382    type Via = Array<T>;
383}
384
385impl<T: ArrayElement> ToGodot for Vec<T> {
386    type Pass = meta::ByValue;
387
388    fn to_godot(&self) -> Self::Via {
389        Array::from(self.as_slice())
390    }
391}
392
393impl<T: ArrayElement> FromGodot for Vec<T> {
394    fn try_from_godot(via: Self::Via) -> Result<Self, ConvertError> {
395        Ok(via.iter_shared().collect())
396    }
397}
398
399impl<T: ArrayElement, const LEN: usize> GodotConvert for [T; LEN] {
400    type Via = Array<T>;
401}
402
403impl<T: ArrayElement, const LEN: usize> ToGodot for [T; LEN] {
404    type Pass = meta::ByValue;
405
406    fn to_godot(&self) -> Self::Via {
407        Array::from(self)
408    }
409}
410
411impl<T: ArrayElement, const LEN: usize> FromGodot for [T; LEN] {
412    fn try_from_godot(via: Self::Via) -> Result<Self, ConvertError> {
413        let via_len = via.len(); // Caching this avoids an FFI call
414        if via_len != LEN {
415            let message =
416                format!("Array<T> of length {via_len} cannot be stored in [T; {LEN}] Rust array");
417            return Err(ConvertError::with_kind_value(
418                ErrorKind::Custom(Some(message.into())),
419                via,
420            ));
421        }
422
423        let mut option_array = [const { None }; LEN];
424
425        for (element, destination) in via.iter_shared().zip(&mut option_array) {
426            *destination = Some(element);
427        }
428
429        let array = option_array.map(|some| {
430            some.expect(
431                "Elements were removed from Array during `iter_shared()`, this is not allowed",
432            )
433        });
434
435        Ok(array)
436    }
437}
438
439impl<T: ArrayElement> GodotConvert for &[T] {
440    type Via = Array<T>;
441}
442
443impl<T: ArrayElement> ToGodot for &[T] {
444    type Pass = meta::ByValue;
445
446    fn to_godot(&self) -> Self::Via {
447        Array::from(*self)
448    }
449}
450
451// ----------------------------------------------------------------------------------------------------------------------------------------------
452// Raw pointers
453
454// const void* is used in some APIs like OpenXrApiExtension::transform_from_pose().
455// void* is used by ScriptExtension::instance_create().
456// Other impls for raw pointers are generated for native structures.
457
458macro_rules! impl_pointer_convert {
459    ($Ptr:ty) => {
460        impl GodotConvert for $Ptr {
461            type Via = i64;
462        }
463
464        impl ToGodot for $Ptr {
465            type Pass = meta::ByValue;
466
467            fn to_godot(&self) -> Self::Via {
468                *self as i64
469            }
470        }
471
472        impl FromGodot for $Ptr {
473            fn try_from_godot(via: Self::Via) -> Result<Self, ConvertError> {
474                Ok(via as Self)
475            }
476        }
477    };
478}
479
480impl_pointer_convert!(*const std::ffi::c_void);
481impl_pointer_convert!(*mut std::ffi::c_void);
482
483// Some other pointer types are used by various other methods, see https://github.com/godot-rust/gdext/issues/677
484// TODO: Find better solution to this, this may easily break still if godot decides to add more pointer arguments.
485
486impl_pointer_convert!(*mut *const u8);
487impl_pointer_convert!(*mut i32);
488impl_pointer_convert!(*mut f64);
489impl_pointer_convert!(*mut u8);
490impl_pointer_convert!(*const u8);