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