godot_core/meta/godot_convert/
impls.rs1use 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
19impl<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 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 T: ToGodot<Pass = meta::ByObject>,
109 T::Via: Clone,
111 for<'f> T::Via: GodotType<
113 Ffi: GodotNullableFfi,
115 ToFfi<'f>: GodotNullableFfi,
116 >,
117 {
119 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 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
180macro_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 $err.into_error(ffi)
202 })
203 }
204
205 impl_godot_scalar!(@shared_fns; $Via, $param_metadata);
206 }
207
208 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 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
275meta::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
281impl_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
318impl 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 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 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 let value = variant.try_to::<i64>()?;
370
371 u64::try_from(value).map_err(|_rust_err| {
372 FromVariantError::BadValue.into_error(value)
374 })
375 }
376}
377
378impl<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(); 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
451macro_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
483impl_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);