diff --git a/godot-core/src/builtin/array.rs b/godot-core/src/builtin/array.rs index dbf601cfa..54bf333a4 100644 --- a/godot-core/src/builtin/array.rs +++ b/godot-core/src/builtin/array.rs @@ -682,6 +682,12 @@ impl TypeStringHint for Array { } } +impl TypeStringHint for VariantArray { + fn type_string() -> String { + format!("{}:Array", VariantType::Array as i32) + } +} + impl Property for Array { type Intermediate = Self; diff --git a/godot-core/src/builtin/dictionary.rs b/godot-core/src/builtin/dictionary.rs index 25ff9c4d0..32a026e7f 100644 --- a/godot-core/src/builtin/dictionary.rs +++ b/godot-core/src/builtin/dictionary.rs @@ -9,12 +9,12 @@ use godot_ffi as sys; use crate::builtin::meta::{FromGodot, ToGodot}; use crate::builtin::{inner, Variant}; use crate::obj::Share; -use crate::property::{Export, ExportInfo, Property}; +use crate::property::{Export, ExportInfo, Property, TypeStringHint}; use std::fmt; use std::marker::PhantomData; use std::ptr::addr_of_mut; use sys::types::OpaqueDictionary; -use sys::{ffi_methods, interface_fn, AsUninit, GodotFfi}; +use sys::{ffi_methods, interface_fn, AsUninit, GodotFfi, VariantType}; use super::meta::impl_godot_as_self; use super::VariantArray; @@ -344,6 +344,12 @@ impl Property for Dictionary { } } +impl TypeStringHint for Dictionary { + fn type_string() -> String { + format!("{}:Dictionary", VariantType::Dictionary as i32) + } +} + impl Export for Dictionary { fn default_export_info() -> ExportInfo { ExportInfo::with_hint_none() diff --git a/godot-core/src/property.rs b/godot-core/src/property.rs index f97ebd368..ff9d8b8c8 100644 --- a/godot-core/src/property.rs +++ b/godot-core/src/property.rs @@ -312,15 +312,29 @@ pub mod export_info_functions { mod export_impls { use super::*; use crate::builtin::*; + use godot_ffi as sys; macro_rules! impl_property_by_clone { - ($Ty:ty => $variant_type:ident, no_export) => { + ($Ty:ty => $variant_type:ident; no_export) => { impl_property_by_clone!(@property $Ty => $variant_type); + impl_property_by_clone!(@type_string_hint $Ty, $variant_type); + }; + + ($Ty:ty => $variant_type:ident, $type_string_name:ident; no_export) => { + impl_property_by_clone!(@property $Ty => $variant_type); + impl_property_by_clone!(@type_string_hint $Ty, $type_string_name); }; ($Ty:ty => $variant_type:ident) => { impl_property_by_clone!(@property $Ty => $variant_type); impl_property_by_clone!(@export $Ty); + impl_property_by_clone!(@type_string_hint $Ty, $variant_type); + }; + + ($Ty:ty => $variant_type:ident, $type_string_name:ident) => { + impl_property_by_clone!(@property $Ty => $variant_type); + impl_property_by_clone!(@export $Ty); + impl_property_by_clone!(@type_string_hint $Ty, $type_string_name); }; (@property $Ty:ty => $variant_type:ident) => { @@ -344,10 +358,20 @@ mod export_impls { } } }; + + (@type_string_hint $Ty:ty, $type_string_name:ident) => { + impl TypeStringHint for $Ty { + fn type_string() -> String { + use sys::GodotFfi; + let variant_type = <$Ty as $crate::builtin::meta::GodotType>::Ffi::variant_type(); + format!("{}:{}", variant_type as i32, stringify!($type_string_name)) + } + } + } } // Bounding Boxes - impl_property_by_clone!(Aabb => Aabb); + impl_property_by_clone!(Aabb => Aabb, AABB); impl_property_by_clone!(Rect2 => Rect2); impl_property_by_clone!(Rect2i => Rect2i); @@ -388,28 +412,28 @@ mod export_impls { impl_property_by_clone!(PackedColorArray => PackedColorArray); // Primitives - impl_property_by_clone!(f64 => Float); - impl_property_by_clone!(i64 => Int); - impl_property_by_clone!(bool => Bool); + impl_property_by_clone!(f64 => Float, float); + impl_property_by_clone!(i64 => Int, int); + impl_property_by_clone!(bool => Bool, bool); // Godot uses f64 internally for floats, and if Godot tries to pass an invalid f32 into a rust property // then the property will just round the value or become inf. - impl_property_by_clone!(f32 => Float); + impl_property_by_clone!(f32 => Float, float); // Godot uses i64 internally for integers, and if Godot tries to pass an invalid integer into a property // accepting one of the below values then rust will panic. In the editor this will appear as the property // failing to be set to a value and an error printed in the console. During runtime this will crash the // program and print the panic from rust stating that the property cannot store the value. - impl_property_by_clone!(i32 => Int); - impl_property_by_clone!(i16 => Int); - impl_property_by_clone!(i8 => Int); - impl_property_by_clone!(u32 => Int); - impl_property_by_clone!(u16 => Int); - impl_property_by_clone!(u8 => Int); + impl_property_by_clone!(i32 => Int, int); + impl_property_by_clone!(i16 => Int, int); + impl_property_by_clone!(i8 => Int, int); + impl_property_by_clone!(u32 => Int, int); + impl_property_by_clone!(u16 => Int, int); + impl_property_by_clone!(u8 => Int, int); // Callables are useless when exported to the editor, so we only need to make them available as // properties. - impl_property_by_clone!(Callable => Callable, no_export); + impl_property_by_clone!(Callable => Callable; no_export); // RIDs when exported act slightly weird. They are largely read-only, however you can reset them to their // default value. This seems to me very unintuitive. Since if we are storing an RID we would likely not @@ -418,7 +442,7 @@ mod export_impls { // // Additionally, RIDs aren't persistent, and can sometimes behave a bit weirdly when passed from the // editor to the runtime. - impl_property_by_clone!(Rid => Rid, no_export); + impl_property_by_clone!(Rid => Rid, RID; no_export); // impl_property_by_clone!(Signal => Signal); } diff --git a/itest/godot/.godot/global_script_class_cache.cfg b/itest/godot/.godot/global_script_class_cache.cfg index fa91f4c7a..2deaa60ca 100644 --- a/itest/godot/.godot/global_script_class_cache.cfg +++ b/itest/godot/.godot/global_script_class_cache.cfg @@ -5,6 +5,12 @@ list=Array[Dictionary]([{ "language": &"GDScript", "path": "res://TestRunner.gd" }, { +"base": &"Node", +"class": &"PropertyTemplateGDScript", +"icon": "", +"language": &"GDScript", +"path": "res://PropertyTemplate.gd" +}, { "base": &"RefCounted", "class": &"TestStats", "icon": "", diff --git a/itest/godot/PropertyTemplate.gd b/itest/godot/PropertyTemplate.gd new file mode 100644 index 000000000..caf39d710 --- /dev/null +++ b/itest/godot/PropertyTemplate.gd @@ -0,0 +1,221 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +class_name PropertyTemplateGDScript +extends Node + +# Base types + +var property_bool: bool +var property_i64: int +var property_i32: int +var property_i16: int +var property_i8: int +var property_u32: int +var property_u16: int +var property_u8: int +var property_f64: float +var property_f32: float +var property_GodotString: String +var property_Vector2: Vector2 +var property_Vector2i: Vector2i +var property_Rect2: Rect2 +var property_Rect2i: Rect2i +var property_Vector3: Vector3 +var property_Vector3i: Vector3i +var property_Transform2D: Transform2D +var property_Vector4: Vector4 +var property_Vector4i: Vector4i +var property_Plane: Plane +var property_Quaternion: Quaternion +var property_Aabb: AABB +var property_Basis: Basis +var property_Transform3D: Transform3D +var property_Projection: Projection +var property_Color: Color +var property_StringName: StringName +var property_NodePath: NodePath +var property_Rid: RID +var property_Gd_Node: Node +var property_Gd_Resource: Resource +var property_Callable: Callable +var property_Dictionary: Dictionary +var property_VariantArray: Array +var property_PackedByteArray: PackedByteArray +var property_PackedInt32Array: PackedInt32Array +var property_PackedInt64Array: PackedInt64Array +var property_PackedFloat32Array: PackedFloat32Array +var property_PackedFloat64Array: PackedFloat64Array +var property_PackedStringArray: PackedStringArray +var property_PackedVector2Array: PackedVector2Array +var property_PackedVector3Array: PackedVector3Array +var property_PackedColorArray: PackedColorArray + +# Types nested in arrays + +var property_array_bool: Array[bool] +var property_array_i64: Array[int] +var property_array_i32: Array[int] +var property_array_i16: Array[int] +var property_array_i8: Array[int] +var property_array_u32: Array[int] +var property_array_u16: Array[int] +var property_array_u8: Array[int] +var property_array_f64: Array[float] +var property_array_f32: Array[float] +var property_array_GodotString: Array[String] +var property_array_Vector2: Array[Vector2] +var property_array_Vector2i: Array[Vector2i] +var property_array_Rect2: Array[Rect2] +var property_array_Rect2i: Array[Rect2i] +var property_array_Vector3: Array[Vector3] +var property_array_Vector3i: Array[Vector3i] +var property_array_Transform2D: Array[Transform2D] +var property_array_Vector4: Array[Vector4] +var property_array_Vector4i: Array[Vector4i] +var property_array_Plane: Array[Plane] +var property_array_Quaternion: Array[Quaternion] +var property_array_Aabb: Array[AABB] +var property_array_Basis: Array[Basis] +var property_array_Transform3D: Array[Transform3D] +var property_array_Projection: Array[Projection] +var property_array_Color: Array[Color] +var property_array_StringName: Array[StringName] +var property_array_NodePath: Array[NodePath] +var property_array_Rid: Array[RID] +var property_array_Gd_Node: Array[Node] +var property_array_Gd_Resource: Array[Resource] +var property_array_Callable: Array[Callable] +var property_array_Dictionary: Array[Dictionary] +var property_array_VariantArray: Array[Array] +var property_array_PackedByteArray: Array[PackedByteArray] +var property_array_PackedInt32Array: Array[PackedInt32Array] +var property_array_PackedInt64Array: Array[PackedInt64Array] +var property_array_PackedFloat32Array: Array[PackedFloat32Array] +var property_array_PackedFloat64Array: Array[PackedFloat64Array] +var property_array_PackedStringArray: Array[PackedStringArray] +var property_array_PackedVector2Array: Array[PackedVector2Array] +var property_array_PackedVector3Array: Array[PackedVector3Array] +var property_array_PackedColorArray: Array[PackedColorArray] + +# Exporting base types + +@export var export_bool: bool +@export var export_i64: int +@export var export_i32: int +@export var export_i16: int +@export var export_i8: int +@export var export_u32: int +@export var export_u16: int +@export var export_u8: int +@export var export_f64: float +@export var export_f32: float +@export var export_GodotString: String +@export var export_Vector2: Vector2 +@export var export_Vector2i: Vector2i +@export var export_Rect2: Rect2 +@export var export_Rect2i: Rect2i +@export var export_Vector3: Vector3 +@export var export_Vector3i: Vector3i +@export var export_Transform2D: Transform2D +@export var export_Vector4: Vector4 +@export var export_Vector4i: Vector4i +@export var export_Plane: Plane +@export var export_Quaternion: Quaternion +@export var export_Aabb: AABB +@export var export_Basis: Basis +@export var export_Transform3D: Transform3D +@export var export_Projection: Projection +@export var export_Color: Color +@export var export_StringName: StringName +@export var export_NodePath: NodePath +@export var export_Rid: RID +@export var export_Gd_Node: Node +@export var export_Gd_Resource: Resource +@export var export_Callable: Callable +@export var export_Dictionary: Dictionary +@export var export_VariantArray: Array +@export var export_PackedByteArray: PackedByteArray +@export var export_PackedInt32Array: PackedInt32Array +@export var export_PackedInt64Array: PackedInt64Array +@export var export_PackedFloat32Array: PackedFloat32Array +@export var export_PackedFloat64Array: PackedFloat64Array +@export var export_PackedStringArray: PackedStringArray +@export var export_PackedVector2Array: PackedVector2Array +@export var export_PackedVector3Array: PackedVector3Array +@export var export_PackedColorArray: PackedColorArray + +# Exporting types nested in arrays + +@export var export_array_bool: Array[bool] +@export var export_array_i64: Array[int] +@export var export_array_i32: Array[int] +@export var export_array_i16: Array[int] +@export var export_array_i8: Array[int] +@export var export_array_u32: Array[int] +@export var export_array_u16: Array[int] +@export var export_array_u8: Array[int] +@export var export_array_f64: Array[float] +@export var export_array_f32: Array[float] +@export var export_array_GodotString: Array[String] +@export var export_array_Vector2: Array[Vector2] +@export var export_array_Vector2i: Array[Vector2i] +@export var export_array_Rect2: Array[Rect2] +@export var export_array_Rect2i: Array[Rect2i] +@export var export_array_Vector3: Array[Vector3] +@export var export_array_Vector3i: Array[Vector3i] +@export var export_array_Transform2D: Array[Transform2D] +@export var export_array_Vector4: Array[Vector4] +@export var export_array_Vector4i: Array[Vector4i] +@export var export_array_Plane: Array[Plane] +@export var export_array_Quaternion: Array[Quaternion] +@export var export_array_Aabb: Array[AABB] +@export var export_array_Basis: Array[Basis] +@export var export_array_Transform3D: Array[Transform3D] +@export var export_array_Projection: Array[Projection] +@export var export_array_Color: Array[Color] +@export var export_array_StringName: Array[StringName] +@export var export_array_NodePath: Array[NodePath] +@export var export_array_Rid: Array[RID] +@export var export_array_Gd_Node: Array[Node] +@export var export_array_Gd_Resource: Array[Resource] +@export var export_array_Callable: Array[Callable] +@export var export_array_Dictionary: Array[Dictionary] +@export var export_array_VariantArray: Array[Array] +@export var export_array_PackedByteArray: Array[PackedByteArray] +@export var export_array_PackedInt32Array: Array[PackedInt32Array] +@export var export_array_PackedInt64Array: Array[PackedInt64Array] +@export var export_array_PackedFloat32Array: Array[PackedFloat32Array] +@export var export_array_PackedFloat64Array: Array[PackedFloat64Array] +@export var export_array_PackedStringArray: Array[PackedStringArray] +@export var export_array_PackedVector2Array: Array[PackedVector2Array] +@export var export_array_PackedVector3Array: Array[PackedVector3Array] +@export var export_array_PackedColorArray: Array[PackedColorArray] + +# Exporting with custom hints + +@export_file var export_file: String +@export_file("*.txt") var export_file_wildcard_txt: String +@export_global_file var export_global_file: String +@export_global_file("*.png") var export_global_file_wildcard_png: String +@export_dir var export_dir: String +@export_global_dir var export_global_dir: String +@export_multiline var export_multiline: String +@export_range(0, 20) var export_range_float_0_20: float +@export_range(-10, 20, 0.2) var export_range_float_neg_10_20_02: float +@export_range(0, 100, 1, "or_greater", "or_less") var export_range_int_0_100_1_or_greater_or_less: int +@export_exp_easing var export_exp_easing: float +@export_color_no_alpha var export_color_no_alpha: Color +@export_node_path("Button", "TouchScreenButton") var export_node_path_button_touch_screen_button: NodePath +@export_flags("Fire", "Water", "Earth", "Wind") var export_flags_fire_water_earth_wind: int +@export_flags("Self:4", "Allies:8", "Foes:16") var export_flags_self_4_allies_8_foes_16: int +@export_flags_2d_physics var export_flags_2d_physics: int +@export_flags_2d_render var export_flags_2d_render: int +@export_flags_2d_navigation var export_flags_2d_navigation: int +@export_flags_3d_physics var export_flags_3d_physics: int +@export_flags_3d_render var export_flags_3d_render: int +@export_flags_3d_navigation var export_flags_3d_navigation: int +@export_enum("Warrior", "Magician", "Thief") var export_enum_int_warrior_magician_thief: int +@export_enum("Slow:30", "Average:60", "VeryFast:200") var export_enum_int_slow_30_average_60_very_fast_200: int +@export_enum("Rebecca", "Mary", "Leah") var export_enum_string_rebecca_mary_leah: String diff --git a/itest/godot/TestRunner.gd b/itest/godot/TestRunner.gd index 673196d9a..e26627f57 100644 --- a/itest/godot/TestRunner.gd +++ b/itest/godot/TestRunner.gd @@ -53,12 +53,15 @@ func _ready(): if method_name.begins_with("test_"): gdscript_tests.push_back(await suite.run_test(suite, method_name)) + var property_template = PropertyTemplateGDScript.new() + var success: bool = rust_runner.run_all_tests( gdscript_tests, gdscript_suites.size(), allow_focus, self, - filters + filters, + property_template ) if success: diff --git a/itest/godot/TestSuite.gd b/itest/godot/TestSuite.gd index b0e2ca689..9682d0e2e 100644 --- a/itest/godot/TestSuite.gd +++ b/itest/godot/TestSuite.gd @@ -64,4 +64,4 @@ func assert_fail(message: String = "") -> bool: else: print_error("Test execution should have failed") - return false \ No newline at end of file + return false diff --git a/itest/rust/src/framework/mod.rs b/itest/rust/src/framework/mod.rs index 1dbf1ffc9..c0052044e 100644 --- a/itest/rust/src/framework/mod.rs +++ b/itest/rust/src/framework/mod.rs @@ -73,6 +73,7 @@ fn collect_rust_benchmarks() -> (Vec, usize) { pub struct TestContext { pub scene_tree: Gd, + pub property_template: Gd, } #[derive(Copy, Clone)] diff --git a/itest/rust/src/framework/runner.rs b/itest/rust/src/framework/runner.rs index f78b49cbe..f631e6aee 100644 --- a/itest/rust/src/framework/runner.rs +++ b/itest/rust/src/framework/runner.rs @@ -38,6 +38,7 @@ impl IntegrationTests { allow_focus: bool, scene_tree: Gd, filters: VariantArray, + property_template: Gd, ) -> bool { println!("{}Run{} Godot integration tests...", FMT_CYAN_BOLD, FMT_END); let filters: Vec = filters.iter_shared().map(|v| v.to::()).collect(); @@ -70,8 +71,9 @@ impl IntegrationTests { } let clock = Instant::now(); - self.run_rust_tests(rust_tests, scene_tree); + self.run_rust_tests(rust_tests, scene_tree, property_template.clone()); let rust_time = clock.elapsed(); + property_template.free(); let gdscript_time = if !focus_run { let extra_duration = self.run_gdscript_tests(gdscript_tests); @@ -121,8 +123,16 @@ impl IntegrationTests { } } - fn run_rust_tests(&mut self, tests: Vec, scene_tree: Gd) { - let ctx = TestContext { scene_tree }; + fn run_rust_tests( + &mut self, + tests: Vec, + scene_tree: Gd, + property_template: Gd, + ) { + let ctx = TestContext { + scene_tree, + property_template, + }; let mut last_file = None; for test in tests { diff --git a/itest/rust/src/object_tests/mod.rs b/itest/rust/src/object_tests/mod.rs index 03aa8404f..ac9acebaf 100644 --- a/itest/rust/src/object_tests/mod.rs +++ b/itest/rust/src/object_tests/mod.rs @@ -7,6 +7,7 @@ mod base_test; mod class_rename_test; mod object_test; +mod property_template_test; mod property_test; mod singleton_test; mod virtual_methods_test; diff --git a/itest/rust/src/object_tests/property_template_test.rs b/itest/rust/src/object_tests/property_template_test.rs new file mode 100644 index 000000000..e114b9c23 --- /dev/null +++ b/itest/rust/src/object_tests/property_template_test.rs @@ -0,0 +1,503 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +//! Testing that GDScript and rust produces the same property info for properties exported to Godot. + +// We're using some weird formatting just for simplicity's sake. +#![allow(non_snake_case)] + +use std::collections::HashMap; + +use crate::framework::itest; +use godot::{engine::global::PropertyUsageFlags, prelude::*}; + +use crate::framework::TestContext; + +#[derive(GodotClass)] +#[class(base = Node, init)] +struct PropertyTemplateRust { + // Base types + #[var] + property_bool: bool, + #[var] + property_i64: i64, + #[var] + property_i32: i32, + #[var] + property_i16: i16, + #[var] + property_i8: i8, + #[var] + property_u32: u32, + #[var] + property_u16: u16, + #[var] + property_u8: u8, + #[var] + property_f64: f64, + #[var] + property_f32: f32, + #[var] + property_GodotString: GodotString, + #[var] + property_Vector2: Vector2, + #[var] + property_Vector2i: Vector2i, + #[var] + property_Rect2: Rect2, + #[var] + property_Rect2i: Rect2i, + #[var] + property_Vector3: Vector3, + #[var] + property_Vector3i: Vector3i, + #[var] + property_Transform2D: Transform2D, + #[var] + property_Vector4: Vector4, + #[var] + property_Vector4i: Vector4i, + #[var] + #[init(default = Plane::new(Vector3::new(1.0,0.0,0.0), 0.0))] + property_Plane: Plane, + #[var] + property_Quaternion: Quaternion, + #[var] + property_Aabb: Aabb, + #[var] + property_Basis: Basis, + #[var] + property_Transform3D: Transform3D, + #[var] + property_Projection: Projection, + #[var] + property_Color: Color, + #[var] + property_StringName: StringName, + #[var] + property_NodePath: NodePath, + #[var] + #[init(default = Rid::Invalid)] + property_Rid: Rid, + #[var] + property_Gd_Node: Option>, + #[var] + property_Gd_Resource: Option>, + #[var] + #[init(default = Callable::invalid())] + property_Callable: Callable, + #[var] + property_Dictionary: Dictionary, + #[var] + property_VariantArray: VariantArray, + #[var] + property_PackedByteArray: PackedByteArray, + #[var] + property_PackedInt32Array: PackedInt32Array, + #[var] + property_PackedInt64Array: PackedInt64Array, + #[var] + property_PackedFloat32Array: PackedFloat32Array, + #[var] + property_PackedFloat64Array: PackedFloat64Array, + #[var] + property_PackedStringArray: PackedStringArray, + #[var] + property_PackedVector2Array: PackedVector2Array, + #[var] + property_PackedVector3Array: PackedVector3Array, + #[var] + property_PackedColorArray: PackedColorArray, + // Types nested in arrays + #[var] + property_array_bool: Array, + #[var] + property_array_i64: Array, + #[var] + property_array_i32: Array, + #[var] + property_array_i16: Array, + #[var] + property_array_i8: Array, + #[var] + property_array_u32: Array, + #[var] + property_array_u16: Array, + #[var] + property_array_u8: Array, + #[var] + property_array_f64: Array, + #[var] + property_array_f32: Array, + #[var] + property_array_GodotString: Array, + #[var] + property_array_Vector2: Array, + #[var] + property_array_Vector2i: Array, + #[var] + property_array_Rect2: Array, + #[var] + property_array_Rect2i: Array, + #[var] + property_array_Vector3: Array, + #[var] + property_array_Vector3i: Array, + #[var] + property_array_Transform2D: Array, + #[var] + property_array_Vector4: Array, + #[var] + property_array_Vector4i: Array, + #[var] + property_array_Plane: Array, + #[var] + property_array_Quaternion: Array, + #[var] + property_array_Aabb: Array, + #[var] + property_array_Basis: Array, + #[var] + property_array_Transform3D: Array, + #[var] + property_array_Projection: Array, + #[var] + property_array_Color: Array, + #[var] + property_array_StringName: Array, + #[var] + property_array_NodePath: Array, + #[var] + property_array_Rid: Array, + #[var] + property_array_Gd_Node: Array>, + #[var] + property_array_Gd_Resource: Array>, + #[var] + property_array_Callable: Array, + #[var] + property_array_Dictionary: Array, + #[var] + property_array_VariantArray: Array, + #[var] + property_array_PackedByteArray: Array, + #[var] + property_array_PackedInt32Array: Array, + #[var] + property_array_PackedInt64Array: Array, + #[var] + property_array_PackedFloat32Array: Array, + #[var] + property_array_PackedFloat64Array: Array, + #[var] + property_array_PackedStringArray: Array, + #[var] + property_array_PackedVector2Array: Array, + #[var] + property_array_PackedVector3Array: Array, + #[var] + property_array_PackedColorArray: Array, + + // Exporting base types + #[export] + export_bool: bool, + #[export] + export_i64: i64, + #[export] + export_i32: i32, + #[export] + export_i16: i16, + #[export] + export_i8: i8, + #[export] + export_u32: u32, + #[export] + export_u16: u16, + #[export] + export_u8: u8, + #[export] + export_f64: f64, + #[export] + export_f32: f32, + #[export] + export_GodotString: GodotString, + #[export] + export_Vector2: Vector2, + #[export] + export_Vector2i: Vector2i, + #[export] + export_Rect2: Rect2, + #[export] + export_Rect2i: Rect2i, + #[export] + export_Vector3: Vector3, + #[export] + export_Vector3i: Vector3i, + #[export] + export_Transform2D: Transform2D, + #[export] + export_Vector4: Vector4, + #[export] + export_Vector4i: Vector4i, + #[export] + #[init(default = Plane::new(Vector3::new(1.0,0.0,0.0), 0.0))] + export_Plane: Plane, + #[export] + export_Quaternion: Quaternion, + #[export] + export_Aabb: Aabb, + #[export] + export_Basis: Basis, + #[export] + export_Transform3D: Transform3D, + #[export] + export_Projection: Projection, + #[export] + export_Color: Color, + #[export] + export_StringName: StringName, + #[export] + export_NodePath: NodePath, + // We do not allow exporting RIDs as they are useless when exported. + // #[export] + // export_Rid: Rid, + #[export] + export_Gd_Node: Option>, + #[export] + export_Gd_Resource: Option>, + // We do not allow exporting Callables as they are useless when exported + // #[export] + // export_Callable: Callable, + #[export] + export_Dictionary: Dictionary, + #[export] + export_VariantArray: VariantArray, + #[export] + export_PackedByteArray: PackedByteArray, + #[export] + export_PackedInt32Array: PackedInt32Array, + #[export] + export_PackedInt64Array: PackedInt64Array, + #[export] + export_PackedFloat32Array: PackedFloat32Array, + #[export] + export_PackedFloat64Array: PackedFloat64Array, + #[export] + export_PackedStringArray: PackedStringArray, + #[export] + export_PackedVector2Array: PackedVector2Array, + #[export] + export_PackedVector3Array: PackedVector3Array, + #[export] + export_PackedColorArray: PackedColorArray, + + // Exporting types nested in arrays + #[export] + export_array_bool: Array, + #[export] + export_array_i64: Array, + #[export] + export_array_i32: Array, + #[export] + export_array_i16: Array, + #[export] + export_array_i8: Array, + #[export] + export_array_u32: Array, + #[export] + export_array_u16: Array, + #[export] + export_array_u8: Array, + #[export] + export_array_f64: Array, + #[export] + export_array_f32: Array, + #[export] + export_array_GodotString: Array, + #[export] + export_array_Vector2: Array, + #[export] + export_array_Vector2i: Array, + #[export] + export_array_Rect2: Array, + #[export] + export_array_Rect2i: Array, + #[export] + export_array_Vector3: Array, + #[export] + export_array_Vector3i: Array, + #[export] + export_array_Transform2D: Array, + #[export] + export_array_Vector4: Array, + #[export] + export_array_Vector4i: Array, + #[export] + export_array_Plane: Array, + #[export] + export_array_Quaternion: Array, + #[export] + export_array_Aabb: Array, + #[export] + export_array_Basis: Array, + #[export] + export_array_Transform3D: Array, + #[export] + export_array_Projection: Array, + #[export] + export_array_Color: Array, + #[export] + export_array_StringName: Array, + #[export] + export_array_NodePath: Array, + #[export] + export_array_Rid: Array, + #[export] + export_array_Gd_Node: Array>, + #[export] + export_array_Gd_Resource: Array>, + #[export] + export_array_Callable: Array, + #[export] + export_array_Dictionary: Array, + #[export] + export_array_VariantArray: Array, + #[export] + export_array_PackedByteArray: Array, + #[export] + export_array_PackedInt32Array: Array, + #[export] + export_array_PackedInt64Array: Array, + #[export] + export_array_PackedFloat32Array: Array, + #[export] + export_array_PackedFloat64Array: Array, + #[export] + export_array_PackedStringArray: Array, + #[export] + export_array_PackedVector2Array: Array, + #[export] + export_array_PackedVector3Array: Array, + #[export] + export_array_PackedColorArray: Array, + + // Exporting with custom hints + #[export(file)] + export_file: GodotString, + #[export(file = "*.txt")] + export_file_wildcard_txt: GodotString, + #[export(global_file)] + export_global_file: GodotString, + #[export(global_file = "*.png")] + export_global_file_wildcard_png: GodotString, + #[export(dir)] + export_dir: GodotString, + #[export(global_dir)] + export_global_dir: GodotString, + #[export(multiline)] + export_multiline: GodotString, + #[export(range = (0.0, 20.0))] + export_range_0_20: f64, + // We're missing step currently. + // #[export(range = (-10, 20 /* , 0.2 */))] + // export_range_float_neg_10_20_02: f64, + // #[export(range = (0, 100, 1, "or_greater", "or_less"))] + // export_range_int_0_100_1_or_greater_or_less: int, + #[export(exp_easing)] + export_exp_easing: f64, + #[export(color_no_alpha)] + export_color_no_alpha: Color, + // Not implemented + // #[export(node_path = ("Button", "TouchScreenButton"))] + // export_node_path_button_touch_screen_button: NodePath, + #[export(flags = (Fire, Water, Earth, Wind))] + export_flags_fire_water_earth_wind: i64, + #[export(flags = (Self = 4, Allies = 8, Foes = 16))] + export_flags_self_4_allies_8_foes_16: i64, + #[export(flags_2d_physics)] + export_flags_2d_physics: i64, + #[export(flags_2d_render)] + export_flags_2d_render: i64, + #[export(flags_2d_navigation)] + export_flags_2d_navigation: i64, + #[export(flags_3d_physics)] + export_flags_3d_physics: i64, + #[export(flags_3d_render)] + export_flags_3d_render: i64, + #[export(flags_3d_navigation)] + export_flags_3d_navigation: i64, + #[export(enum = (Warrior, Magician, Thief))] + export_enum_int_warrior_magician_thief: i64, + #[export(enum = (Slow = 30, Average = 60, VeryFast = 200))] + export_enum_int_slow_30_average_60_very_fast_200: i64, + #[export(enum = (Rebecca, Mary, Leah))] + export_enum_string_rebecca_mary_leah: GodotString, +} + +#[godot_api] +impl PropertyTemplateRust {} + +#[itest] +fn property_template_test(ctx: &TestContext) { + let rust_properties = Gd::::new_default(); + let gdscript_properties = ctx.property_template.clone(); + + // Accumulate errors so we can catch all of them in one go. + let mut errors: Vec = Vec::new(); + let mut properties: HashMap = HashMap::new(); + + for property in rust_properties.get_property_list().iter_shared() { + let name = property + .get("name") + .unwrap() + .to::() + .to_string(); + if name.starts_with("property_") { + properties.insert(name, property); + } + } + + assert!(!properties.is_empty()); + + for property in gdscript_properties.get_property_list().iter_shared() { + let name = property + .get("name") + .unwrap() + .to::() + .to_string(); + + let Some(mut rust_prop) = properties.remove(&name) else { + continue; + }; + + let mut rust_usage = rust_prop.get("usage").unwrap().to::(); + + // the GDSscript variables are script variables, and so have `PROPERTY_USAGE_SCRIPT_VARIABLE` set. + if rust_usage == PropertyUsageFlags::PROPERTY_USAGE_STORAGE.ord() as i64 { + // `PROPERTY_USAGE_SCRIPT_VARIABLE` does the same thing as `PROPERTY_USAGE_STORAGE` and so + // GDScript doesn't set both if it doesn't need to. + rust_usage = PropertyUsageFlags::PROPERTY_USAGE_SCRIPT_VARIABLE.ord() as i64 + } else { + rust_usage |= PropertyUsageFlags::PROPERTY_USAGE_SCRIPT_VARIABLE.ord() as i64; + } + + rust_prop.set("usage", rust_usage); + + if rust_prop != property { + errors.push(format!( + "mismatch in property {name}, gdscript: {property:?}, rust: {rust_prop:?}" + )); + } + } + + assert!( + properties.is_empty(), + "not all properties were matched, missing: {properties:?}" + ); + + assert!(errors.is_empty(), "{}", errors.join("\n")); + + rust_properties.free(); +}