Skip to main content

godot_core/registry/property/
mod.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
8//! Registration support for property types.
9
10use godot_ffi::GodotNullableFfi;
11
12use crate::meta::{ClassId, FromGodot, GodotConvert, GodotType, ToGodot};
13
14mod phantom_var;
15
16pub use phantom_var::PhantomVar;
17
18// ----------------------------------------------------------------------------------------------------------------------------------------------
19// Var trait
20
21// Note: HTML link for #[var] works if this symbol is inside prelude, but not in register::property.
22/// Trait for types used in [`#[var]`](../register/derive.GodotClass.html#properties-and-exports) fields.
23///
24/// Defines how a value is passed to/from Godot's property system, through [`var_get()`][Self::var_get] and [`var_set()`][Self::var_set]
25/// associated functions. Further customizes how generated Rust getters and setters operate, in fields annotated with `#[var(pub)]`, through
26/// [`var_pub_get()`][Self::var_pub_get] and [`var_pub_set()`][Self::var_pub_set].
27///
28/// The `Var` trait does not require [`FromGodot`] or [`ToGodot`]: a value can be used as a property even if it can't be used in `#[func]`
29/// parameters or return types.
30///
31/// See also [`Export`], a subtrait for properties exported to the editor UI using `#[export]`.
32///
33/// # Implementing the trait
34/// Most godot-rust types implement `Var` out of the box, so you won't need to do anything. If a type doesn't support it, that's usually a sign
35/// that it shouldn't be used in property contexts.
36///
37/// For enums, you can use the [`#[derive(Var)]`](../derive.Var.html) macro, in combination with `GodotConvert` as `#[derive(GodotConvert, Var)]`.
38///
39/// If you need to manually implement `Var` and your field type already supports `ToGodot` and `FromGodot`, just implement the [`SimpleVar`]
40/// trait instead of `Var`. It will automatically provide a reasonable standard implementation of `Var`.
41#[doc(alias = "property")]
42//
43// on_unimplemented: we also mention #[export] here, because we can't control the order of error messages.
44// Missing Export often also means missing Var trait, and so the Var error message appears first.
45#[diagnostic::on_unimplemented(
46    message = "`#[var]` properties require `Var` trait; #[export] ones require `Export` trait",
47    label = "type cannot be used as a property",
48    note = "see also: https://godot-rust.github.io/book/register/properties.html"
49)]
50pub trait Var: GodotConvert {
51    /// Type used in generated Rust getters/setters for `#[var(pub)]`.
52    //
53    // Note: we deliberately don't require `PubType: ToGodot + FromGodot`. Many manual `Var` impls (`Option<T>`, `Array<T>`, `Dictionary<K,V>`,
54    // `OnEditor<T>`, `OnReady<T>`, `DynGd<T,D>`) have `PubType` types that don't implement those traits. As a consequence, internal (non-pub)
55    // property getters/setters go through `GodotConvert::Via` (which has `GodotType` -> `EngineToGodot` bounds), and the stricter
56    // `ToGodot`/`FromGodot` bounds check (`ensure_func_bounds`) is skipped for them. Only `#[var(pub)]` getters use `PubType` directly.
57    type PubType;
58
59    /// Get property value via FFI-level `Via` type. Called for internal (non-pub) getters registered with Godot.
60    fn var_get(field: &Self) -> Self::Via;
61
62    /// Set property value via FFI-level `Via` type. Called for internal (non-pub) setters registered with Godot.
63    fn var_set(field: &mut Self, value: Self::Via);
64
65    /// Get property value as `PubType`. Called for `#[var(pub)]` getters exposed in Rust API.
66    fn var_pub_get(field: &Self) -> Self::PubType;
67
68    /// Set property value as `PubType`. Called for `#[var(pub)]` setters exposed in Rust API.
69    fn var_pub_set(field: &mut Self, value: Self::PubType);
70}
71
72/// Simplified way to implement the `Var` trait, for godot-convertible types.
73///
74/// Implementing this trait will auto-implement [`Var`] in a standard way for types supporting [`ToGodot`] and [`FromGodot`].
75///
76/// Types implementing this trait will use `clone()` for the public getter and direct assignment for the public setter, with `PubType = Self`.
77/// This is the standard behavior for most types.
78pub trait SimpleVar: ToGodot + FromGodot + Clone {}
79
80/// Blanket impl for types with standard Godot conversion; see [`SimpleVar`] for details.
81impl<T> Var for T
82where
83    T: SimpleVar,
84    T::Via: Clone,
85{
86    type PubType = Self;
87
88    fn var_get(field: &Self) -> Self::Via {
89        <T as ToGodot>::to_godot_owned(field)
90    }
91
92    fn var_set(field: &mut Self, value: Self::Via) {
93        *field = <T as FromGodot>::from_godot(value);
94    }
95
96    fn var_pub_get(field: &Self) -> Self::PubType {
97        field.clone()
98    }
99
100    fn var_pub_set(field: &mut Self, value: Self::PubType) {
101        *field = value;
102    }
103}
104
105// ----------------------------------------------------------------------------------------------------------------------------------------------
106// Export trait
107
108// Note: HTML link for #[export] works if this symbol is inside prelude, but not in register::property.
109/// Trait implemented for types that can be used as [`#[export]`](../register/derive.GodotClass.html#properties-and-exports) fields.
110///
111/// To export objects, see the [_Exporting_ section of `Gd<T>`](../obj/struct.Gd.html#exporting).
112///
113/// For enums, this trait can be derived using the [`#[derive(Export)]`](../derive.Export.html) macro.
114#[doc(alias = "property")]
115//
116// on_unimplemented: mentioning both Var + Export; see above.
117#[diagnostic::on_unimplemented(
118    message = "`#[var]` properties require `Var` trait; #[export] ones require `Export` trait",
119    label = "type cannot be used as a property",
120    note = "see also: https://godot-rust.github.io/book/register/properties.html",
121    note = "`Gd` and `DynGd` cannot be exported directly; wrap them in `Option<...>` or `OnEditor<...>`."
122)]
123pub trait Export: Var {
124    /// If this is a class inheriting `Node`, returns the `ClassId`; otherwise `None`.
125    ///
126    /// Only overridden for `Gd<T>`, to detect erroneous exports of `Node` inside a `Resource` class.
127    #[allow(clippy::wrong_self_convention)]
128    #[doc(hidden)]
129    fn as_node_class() -> Option<ClassId> {
130        None
131    }
132}
133
134/// Marker trait to identify `GodotType`s that can be directly used with an `#[export]`.
135///
136/// Implemented pretty much for all [`GodotType`]s that are not [`GodotClass`][crate::obj::GodotClass]es.
137/// By itself, this trait has no implications for the [`Var`] or [`Export`] traits.
138///
139/// Types which don't implement the `BuiltinExport` trait can't be used directly as an `#[export]`
140/// and must be handled using associated algebraic types, such as:
141/// * [`Option<T>`], which represents optional value that can be null when used.
142/// * [`OnEditor<T>`][crate::obj::OnEditor], which represents value that must not be null when used.
143// Some Godot Types which are inherently non-nullable (e.g., `Gd<T>`),
144// might have their value set to null by the editor. Additionally, Godot must generate
145// initial, default value for such properties, causing memory leaks.
146// Such `GodotType`s don't implement `BuiltinExport`.
147//
148// Note: This marker trait is required to create a blanket implementation for `OnEditor<T>`, where `T` is anything other than `GodotClass`.
149// An alternative approach would involve introducing an extra associated type to `GodotType` trait. However, this would not be ideal --
150// `GodotType` is used in contexts unrelated to `#[export]`, and unnecessary complexity should be avoided. We can't use specialization.
151pub trait BuiltinExport {}
152
153// ----------------------------------------------------------------------------------------------------------------------------------------------
154// Doctests to test compile errors
155
156/// This function only exists as a place to add doc-tests for the `Var` trait and `#[var]` attribute.
157///
158/// The `#[var(no_get, no_set)]` combination is not allowed; if you don't want a property, omit `#[var]` entirely:
159///
160/// ```compile_fail
161/// use godot::prelude::*;
162///
163/// #[derive(GodotClass)]
164/// #[class(init)]
165/// struct Foo {
166///     #[var(no_get, no_set)]
167///     field: i32,
168/// }
169/// ```
170///
171/// Custom getter must return the correct type (matching the field's `PubType`):
172///
173/// ```compile_fail
174/// use godot::prelude::*;
175///
176/// #[derive(GodotClass)]
177/// #[class(init)]
178/// struct Foo {
179///     #[var(get = my_getter)]
180///     field: GString,
181/// }
182///
183/// #[godot_api]
184/// impl Foo {
185///     fn my_getter(&self) -> i32 { 42 }
186/// }
187/// ```
188///
189/// Custom setter must accept the correct type (matching the field's `PubType`):
190///
191/// ```compile_fail
192/// use godot::prelude::*;
193///
194/// #[derive(GodotClass)]
195/// #[class(init)]
196/// struct Foo {
197///     #[var(set = my_setter)]
198///     field: GString,
199/// }
200///
201/// #[godot_api]
202/// impl Foo {
203///     fn my_setter(&mut self, value: i32) {}
204/// }
205/// ```
206fn __var_doctests() {}
207
208/// This function only exists as a place to add doc-tests for the `Export` trait.
209///
210/// Test with export of exportable type should succeed:
211/// ```no_run
212/// use godot::prelude::*;
213///
214/// #[derive(GodotClass)]
215/// #[class(init)]
216/// struct Foo {
217///     #[export]
218///     obj: Option<Gd<Resource>>,
219///     #[export]
220///     array: Array<Gd<Resource>>,
221/// }
222/// ```
223///
224/// Tests with export of non-exportable type should fail:
225/// ```compile_fail
226/// use godot::prelude::*;
227///
228/// #[derive(GodotClass)]
229/// #[class(init)]
230/// struct Foo {
231///     #[export]
232///     obj: Option<Gd<Object>>,
233/// }
234/// ```
235///
236/// Neither `Gd<T>` nor `DynGd<T, D>` can be used with an `#[export]` directly:
237///
238/// ```compile_fail
239///  use godot::prelude::*;
240///
241/// #[derive(GodotClass)]
242/// #[class(init, base = Node)]
243/// struct MyClass {
244///     #[export]
245///     editor_property: Gd<Resource>,
246/// }
247/// ```
248///
249/// ```compile_fail
250///  use godot::prelude::*;
251///
252/// #[derive(GodotClass)]
253/// #[class(init, base = Node)]
254/// struct MyClass {
255///     #[export]
256///     editor_property: DynGd<Node, dyn Display>,
257/// }
258/// ```
259///
260/// ```compile_fail
261/// use godot::prelude::*;
262///
263/// #[derive(GodotClass)]
264/// #[class(init)]
265/// struct Foo {
266///     #[export]
267///     array: Array<Gd<Object>>,
268/// }
269/// ```
270fn __export_doctests() {}
271
272// ----------------------------------------------------------------------------------------------------------------------------------------------
273// Blanket impls for Option<T>
274
275impl<T> Var for Option<T>
276where
277    T: Var + FromGodot,
278    Option<T>: GodotConvert<Via = Option<T::Via>>,
279{
280    type PubType = Option<T::Via>; // Same as Self::Via.
281
282    fn var_get(field: &Self) -> Self::Via {
283        field.as_ref().map(T::var_get)
284    }
285
286    fn var_set(field: &mut Self, value: Self::Via) {
287        match value {
288            Some(via) => match field {
289                // If field is already set, delegate to setter (non-null) on field; otherwise assign new value.
290                Some(inner) => T::var_set(inner, via),
291                None => *field = Some(T::from_godot(via)),
292            },
293            None => *field = None,
294        }
295    }
296
297    fn var_pub_get(field: &Self) -> Self::PubType {
298        Self::var_get(field)
299    }
300
301    fn var_pub_set(field: &mut Self, value: Self::PubType) {
302        Self::var_set(field, value)
303    }
304}
305
306impl<T> Export for Option<T>
307where
308    T: Export,
309    Option<T>: Var,
310{
311}
312
313impl<T> BuiltinExport for Option<T>
314where
315    T: GodotType,
316    T::Ffi: GodotNullableFfi,
317{
318}
319
320// ----------------------------------------------------------------------------------------------------------------------------------------------
321// Export machinery
322
323/// Functions used to translate user-provided arguments into export hints.
324///
325/// You are not supposed to use these functions directly. They are used by the `#[export]` macro to generate the correct export hint.
326///
327/// Each function is named the same as the equivalent Godot annotation.  
328/// For instance, `@export_range` in Godot is `fn export_range` here.
329pub mod export_fns {
330    use godot_ffi::VariantType;
331
332    use crate::builtin::GString;
333    use crate::meta::GodotConvert;
334    use crate::meta::shape::{GodotElementShape, GodotShape};
335    use crate::obj::EngineEnum;
336    use crate::registry::info::{PropertyHint, PropertyHintInfo};
337    use crate::registry::property::Export;
338    use crate::sys;
339
340    /// Turn a list of variables into a comma separated string containing only the identifiers corresponding
341    /// to a true boolean variable.
342    macro_rules! comma_separate_boolean_idents {
343        ($( $ident:ident),* $(,)?) => {
344            {
345                let mut strings = Vec::new();
346
347                $(
348                    if $ident {
349                        strings.push(stringify!($ident));
350                    }
351                )*
352
353                strings.join(",")
354            }
355        };
356    }
357
358    // We want this to match the options available on `@export_range(..)`
359    /// Mark an exported numerical value to use the editor's range UI.
360    ///
361    /// You'll never call this function itself, but will instead use the macro `#[export(range=(...))]`, as below.  The syntax is
362    /// very similar to Godot's [`@export_range`](https://docs.godotengine.org/en/stable/classes/class_%40gdscript.html#class-gdscript-annotation-export-range).
363    /// `min`, `max`, and `step` are `f32` positional arguments, with `step` being optional and defaulting to `1.0`.  The rest of
364    /// the arguments can be written in any order.  The symbols of type `bool` just need to have those symbols written, and those of type `Option<T>` will be written as `{KEY}={VALUE}`, e.g. `suffix="px"`.
365    ///
366    /// ```
367    /// # use godot::prelude::*;
368    /// #[derive(GodotClass)]
369    /// #[class(init, base=Node)]
370    /// struct MyClassWithRangedValues {
371    ///     #[export(range=(0.0, 400.0, 1.0, or_greater, suffix="px"))]
372    ///     icon_width: i32,
373    ///     #[export(range=(-180.0, 180.0, degrees))]
374    ///     angle: f32,
375    /// }
376    /// ```
377    #[allow(clippy::too_many_arguments)]
378    pub fn export_range(
379        min: f64,
380        max: f64,
381        step: Option<f64>,
382        or_greater: bool,
383        or_less: bool,
384        exp: bool,
385        radians_as_degrees: bool,
386        degrees: bool,
387        hide_slider: bool,
388        suffix: Option<String>,
389    ) -> PropertyHintInfo {
390        // From Godot 4.4, GDScript uses `.0` for integral floats, see https://github.com/godotengine/godot/pull/47502.
391        // We still register them the old way, to test compatibility. See also property_template_test.rs.
392
393        let hint_beginning = if let Some(step) = step {
394            format!("{min},{max},{step}")
395        } else {
396            format!("{min},{max}")
397        };
398
399        let rest = comma_separate_boolean_idents!(
400            or_greater,
401            or_less,
402            exp,
403            radians_as_degrees,
404            degrees,
405            hide_slider
406        );
407
408        let mut hint_string = hint_beginning;
409        if !rest.is_empty() {
410            hint_string.push_str(&format!(",{rest}"));
411        }
412        if let Some(suffix) = suffix {
413            hint_string.push_str(&format!(",suffix:{suffix}"));
414        }
415
416        PropertyHintInfo {
417            hint: PropertyHint::RANGE,
418            hint_string: GString::from(&hint_string),
419        }
420    }
421
422    #[doc(hidden)]
423    pub struct ExportValueWithKey<T> {
424        variant: String,
425        key: Option<T>,
426    }
427
428    impl<T: std::fmt::Display> ExportValueWithKey<T> {
429        fn as_hint_string(&self) -> String {
430            crate::meta::shape::format_hint_entry(&self.variant, self.key.as_ref())
431        }
432
433        fn slice_as_hint_string<V>(values: &[V]) -> String
434        where
435            for<'a> &'a V: Into<Self>,
436        {
437            let values = values
438                .iter()
439                .map(|v| v.into().as_hint_string())
440                .collect::<Vec<_>>();
441
442            values.join(",")
443        }
444    }
445
446    impl<T, S> From<&(S, Option<T>)> for ExportValueWithKey<T>
447    where
448        T: Clone,
449        S: AsRef<str>,
450    {
451        fn from((variant, key): &(S, Option<T>)) -> Self {
452            Self {
453                variant: variant.as_ref().into(),
454                key: key.clone(),
455            }
456        }
457    }
458
459    type ExportEnumEntry = ExportValueWithKey<i64>;
460
461    /// Equivalent to `@export_enum` in Godot.
462    ///
463    /// A name without a key would be represented as `(name, None)`, and a name with a key as `(name, Some(key))`.
464    ///
465    /// # Examples
466    ///
467    /// ```no_run
468    /// # use godot::register::property::export_fns::export_enum;
469    /// export_enum(&[("a", None), ("b", Some(10))]);
470    /// ```
471    pub fn export_enum<T>(variants: &[T]) -> PropertyHintInfo
472    where
473        for<'a> &'a T: Into<ExportEnumEntry>,
474    {
475        let hint_string: String = ExportEnumEntry::slice_as_hint_string(variants);
476
477        PropertyHintInfo {
478            hint: PropertyHint::ENUM,
479            hint_string: GString::from(&hint_string),
480        }
481    }
482
483    pub fn export_exp_easing(attenuation: bool, positive_only: bool) -> PropertyHintInfo {
484        let hint_string = comma_separate_boolean_idents!(attenuation, positive_only);
485
486        PropertyHintInfo {
487            hint: PropertyHint::EXP_EASING,
488            hint_string: GString::from(&hint_string),
489        }
490    }
491
492    type BitFlag = ExportValueWithKey<u32>;
493
494    /// Equivalent to `@export_flags` in Godot.
495    ///
496    /// A flag without a key would be represented as `(flag, None)`, and a flag with a key as `(flag, Some(key))`.
497    ///
498    /// # Examples
499    ///
500    /// ```no_run
501    /// # use godot::register::property::export_fns::export_flags;
502    /// export_flags(&[("a", None), ("b", Some(10))]);
503    /// ```
504    pub fn export_flags<T>(bits: &[T]) -> PropertyHintInfo
505    where
506        for<'a> &'a T: Into<BitFlag>,
507    {
508        let hint_string = BitFlag::slice_as_hint_string(bits);
509
510        PropertyHintInfo {
511            hint: PropertyHint::FLAGS,
512            hint_string: GString::from(&hint_string),
513        }
514    }
515
516    /// Handles `@export_file`, `@export_global_file`, `@export_dir` and `@export_global_dir`.
517    pub fn export_file_or_dir<T: Export>(
518        is_file: bool,
519        is_global: bool,
520        filter: impl AsRef<str>,
521    ) -> PropertyHintInfo {
522        let filter = filter.as_ref();
523        sys::strict_assert!(is_file || filter.is_empty()); // Dir never has filter.
524
525        export_file_or_dir_inner(&T::Via::godot_shape(), is_file, is_global, filter)
526    }
527
528    fn export_file_or_dir_inner(
529        shape: &GodotShape,
530        is_file: bool,
531        is_global: bool,
532        filter: &str,
533    ) -> PropertyHintInfo {
534        let hint = match (is_file, is_global) {
535            (true, true) => PropertyHint::GLOBAL_FILE,
536            (true, false) => PropertyHint::FILE,
537            (false, true) => PropertyHint::GLOBAL_DIR,
538            (false, false) => PropertyHint::DIR,
539        };
540
541        // Returned value depends on field type.
542        match shape {
543            // GString field:
544            // { "type": 4, "hint": 13, "hint_string": "*.png" }
545            GodotShape::Builtin {
546                variant_type: VariantType::STRING,
547                ..
548            } => PropertyHintInfo {
549                hint,
550                hint_string: GString::from(filter),
551            },
552
553            // Array<GString> or PackedStringArray field:
554            // { "type": 28, "hint": 23, "hint_string": "4/13:*.png" }
555            #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
556            GodotShape::Builtin {
557                variant_type: VariantType::PACKED_STRING_ARRAY,
558                ..
559            } => to_string_array_hint(hint, filter),
560
561            #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
562            GodotShape::TypedArray {
563                element:
564                    GodotElementShape::Builtin {
565                        variant_type: VariantType::STRING,
566                    },
567            } => to_string_array_hint(hint, filter),
568
569            _ => {
570                // E.g. `global_file`.
571                let attribute_name = hint.as_str().to_lowercase();
572
573                // TODO nicer error handling.
574                // Compile time may be difficult (at least without extra traits... maybe const fn?). But at least more context info, field name etc.
575                #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
576                panic!(
577                    "#[export({attribute_name})] only supports GString, Array<String> or PackedStringArray field types\n\
578                    encountered: {shape:?}"
579                );
580
581                #[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
582                panic!(
583                    "#[export({attribute_name})] only supports GString type prior to Godot 4.3\n\
584                    encountered: {shape:?}"
585                );
586            }
587        }
588    }
589
590    /// For `Array<GString>` and `PackedStringArray` fields using one of the `@export[_global]_{file|dir}` annotations.
591    ///
592    /// Formats: `"4/13:"`, `"4/15:*.png"`, ...
593    fn to_string_array_hint(hint: PropertyHint, filter: &str) -> PropertyHintInfo {
594        PropertyHintInfo {
595            hint: PropertyHint::TYPE_STRING,
596            hint_string: GString::from(&crate::meta::shape::format_elements_typed(
597                VariantType::STRING,
598                hint,
599                filter,
600            )),
601        }
602    }
603
604    pub fn export_placeholder<S: AsRef<str>>(placeholder: S) -> PropertyHintInfo {
605        PropertyHintInfo {
606            hint: PropertyHint::PLACEHOLDER_TEXT,
607            hint_string: GString::from(placeholder.as_ref()),
608        }
609    }
610
611    macro_rules! default_export_funcs {
612        (
613            $( $function_name:ident => $property_hint:ident, )*
614        ) => {
615            $(
616                pub fn $function_name() -> PropertyHintInfo {
617                    PropertyHintInfo {
618                        hint: PropertyHint::$property_hint,
619                        hint_string: GString::new()
620                    }
621                }
622            )*
623        };
624    }
625
626    // The left side of these declarations follows the export annotation provided by GDScript, whereas the
627    // right side are the corresponding property hint. Godot is not always consistent between the two, such
628    // as `export_multiline` being `PROPERTY_HINT_MULTILINE_TEXT`.
629    default_export_funcs!(
630        export_storage => NONE, // Storage exports don't display in the editor.
631        export_flags_2d_physics => LAYERS_2D_PHYSICS,
632        export_flags_2d_render => LAYERS_2D_RENDER,
633        export_flags_2d_navigation => LAYERS_2D_NAVIGATION,
634        export_flags_3d_physics => LAYERS_3D_PHYSICS,
635        export_flags_3d_render => LAYERS_3D_RENDER,
636        export_flags_3d_navigation => LAYERS_3D_NAVIGATION,
637        export_multiline => MULTILINE_TEXT,
638        export_color_no_alpha => COLOR_NO_ALPHA,
639    );
640}
641
642mod export_impls {
643    use super::*;
644    use crate::builtin::*;
645
646    macro_rules! impl_property_by_godot_convert {
647        ($Ty:ty, no_export) => {
648            // For types without Export (Callable, Signal, Rid).
649            impl SimpleVar for $Ty {}
650        };
651
652        ($Ty:ty) => {
653            impl SimpleVar for $Ty {}
654            impl_property_by_godot_convert!(@export $Ty);
655            impl_property_by_godot_convert!(@builtin $Ty);
656        };
657
658        (@export $Ty:ty) => {
659            impl Export for $Ty {}
660        };
661
662        (@builtin $Ty:ty) => {
663            impl BuiltinExport for $Ty {}
664        }
665    }
666
667    // Bounding boxes
668    impl_property_by_godot_convert!(Aabb);
669    impl_property_by_godot_convert!(Rect2);
670    impl_property_by_godot_convert!(Rect2i);
671
672    // Matrices
673    impl_property_by_godot_convert!(Basis);
674    impl_property_by_godot_convert!(Transform2D);
675    impl_property_by_godot_convert!(Transform3D);
676    impl_property_by_godot_convert!(Projection);
677
678    // Vectors
679    impl_property_by_godot_convert!(Vector2);
680    impl_property_by_godot_convert!(Vector2i);
681    impl_property_by_godot_convert!(Vector3);
682    impl_property_by_godot_convert!(Vector3i);
683    impl_property_by_godot_convert!(Vector4);
684    impl_property_by_godot_convert!(Vector4i);
685
686    // Misc math
687    impl_property_by_godot_convert!(Quaternion);
688    impl_property_by_godot_convert!(Plane);
689
690    // Stringy types
691    impl_property_by_godot_convert!(GString);
692    impl_property_by_godot_convert!(StringName);
693    impl_property_by_godot_convert!(NodePath);
694
695    impl_property_by_godot_convert!(Color);
696    impl_property_by_godot_convert!(Variant);
697
698    // Primitives
699    impl_property_by_godot_convert!(f64);
700    impl_property_by_godot_convert!(i64);
701    impl_property_by_godot_convert!(bool);
702
703    // Godot uses f64 internally for floats, and if Godot tries to pass an invalid f32 into a rust property
704    // then the property will just round the value or become inf.
705    impl_property_by_godot_convert!(f32);
706
707    // Godot uses i64 internally for integers, and if Godot tries to pass an invalid integer into a property
708    // accepting one of the below values then rust will panic. In the editor this will appear as the property
709    // failing to be set to a value and an error printed in the console. During runtime this will crash the
710    // program and print the panic from rust stating that the property cannot store the value.
711    impl_property_by_godot_convert!(i32);
712    impl_property_by_godot_convert!(i16);
713    impl_property_by_godot_convert!(i8);
714    impl_property_by_godot_convert!(u32);
715    impl_property_by_godot_convert!(u16);
716    impl_property_by_godot_convert!(u8);
717
718    // Callables and Signals are useless when exported to the editor, so we only need to make them available as
719    // properties.
720    impl_property_by_godot_convert!(Callable, no_export);
721    impl_property_by_godot_convert!(Signal, no_export);
722
723    // RIDs when exported act slightly weird. They are largely read-only, however you can reset them to their
724    // default value. This seems to me very unintuitive. Since if we are storing an RID we would likely not
725    // want that RID to be spuriously resettable. And if used for debugging purposes we can use another
726    // mechanism than exporting the RID to the editor. Such as exporting a string containing the RID.
727    //
728    // Additionally, RIDs aren't persistent, and can sometimes behave a bit weirdly when passed from the
729    // editor to the runtime.
730    impl_property_by_godot_convert!(Rid, no_export);
731
732    // Var/Export for Array<T> and PackedArray<T> are implemented in the files of their struct declaration.
733
734    // impl_property_by_godot_convert!(Signal);
735}