Skip to content

Commit 34faefa

Browse files
committed
Add ExportInfo struct
Add TypeStringHint trait Add support for exporting nodes, resources, and arrays with type info
1 parent 04dc3e3 commit 34faefa

File tree

10 files changed

+295
-104
lines changed

10 files changed

+295
-104
lines changed

godot-core/src/builtin/array.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ use godot_ffi as sys;
88

99
use crate::builtin::meta::VariantMetadata;
1010
use crate::builtin::*;
11-
use crate::obj::{Export, Share};
11+
use crate::export::{Export, ExportInfo, TypeStringHint};
12+
use crate::obj::Share;
1213
use std::fmt;
1314
use std::marker::PhantomData;
1415
use sys::{ffi_methods, interface_fn, GodotFfi};
@@ -635,10 +636,34 @@ impl<T: VariantMetadata> Share for Array<T> {
635636
}
636637
}
637638

638-
impl<T: VariantMetadata> Export for Array<T> {
639+
impl<T: VariantMetadata + TypeStringHint> TypeStringHint for Array<T> {
640+
fn type_string() -> String {
641+
format!("{}:{}", sys::VariantType::Array as i32, T::type_string())
642+
}
643+
}
644+
645+
impl<T: VariantMetadata + TypeStringHint> Export for Array<T> {
639646
fn export(&self) -> Self {
640647
self.share()
641648
}
649+
650+
fn default_export_info() -> ExportInfo {
651+
ExportInfo {
652+
variant_type: Self::variant_type(),
653+
hint: crate::engine::global::PropertyHint::PROPERTY_HINT_TYPE_STRING,
654+
hint_string: T::type_string().into(),
655+
}
656+
}
657+
}
658+
659+
impl Export for Array<Variant> {
660+
fn export(&self) -> Self {
661+
self.share()
662+
}
663+
664+
fn default_export_info() -> ExportInfo {
665+
ExportInfo::with_hint_none(Self::variant_type())
666+
}
642667
}
643668

644669
impl<T: VariantMetadata> Default for Array<T> {

godot-core/src/builtin/dictionary.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66

77
use godot_ffi as sys;
88

9+
use crate::builtin::meta::VariantMetadata;
910
use crate::builtin::{inner, FromVariant, ToVariant, Variant};
10-
use crate::obj::{Export, Share};
11+
use crate::export::{Export, ExportInfo};
12+
use crate::obj::Share;
1113
use std::fmt;
1214
use std::marker::PhantomData;
1315
use std::ptr::addr_of_mut;
@@ -310,6 +312,10 @@ impl Export for Dictionary {
310312
fn export(&self) -> Self {
311313
self.share()
312314
}
315+
316+
fn default_export_info() -> ExportInfo {
317+
ExportInfo::with_hint_none(Self::variant_type())
318+
}
313319
}
314320

315321
// ----------------------------------------------------------------------------------------------------------------------------------------------

godot-core/src/builtin/meta/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ impl<T: VariantMetadata> VariantMetadata for Option<T> {
4747
fn variant_type() -> VariantType {
4848
T::variant_type()
4949
}
50+
51+
fn class_name() -> ClassName {
52+
T::class_name()
53+
}
5054
}
5155

5256
// ----------------------------------------------------------------------------------------------------------------------------------------------

godot-core/src/builtin/mod.rs

Lines changed: 0 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -340,74 +340,6 @@ pub enum RectSide {
340340

341341
// ----------------------------------------------------------------------------------------------------------------------------------------------
342342

343-
/// Implementations of the `Export` trait for types where it can be done trivially.
344-
mod export {
345-
use crate::builtin::*;
346-
use crate::obj::Export;
347-
348-
macro_rules! impl_export_by_clone {
349-
($ty:path) => {
350-
impl Export for $ty {
351-
fn export(&self) -> Self {
352-
// If `Self` does not implement `Clone`, this gives a clearer error message
353-
// than simply `self.clone()`.
354-
Clone::clone(self)
355-
}
356-
}
357-
};
358-
}
359-
360-
impl_export_by_clone!(bool);
361-
impl_export_by_clone!(isize);
362-
impl_export_by_clone!(usize);
363-
impl_export_by_clone!(i8);
364-
impl_export_by_clone!(i16);
365-
impl_export_by_clone!(i32);
366-
impl_export_by_clone!(i64);
367-
impl_export_by_clone!(u8);
368-
impl_export_by_clone!(u16);
369-
impl_export_by_clone!(u32);
370-
impl_export_by_clone!(u64);
371-
impl_export_by_clone!(f32);
372-
impl_export_by_clone!(f64);
373-
374-
impl_export_by_clone!(Aabb);
375-
impl_export_by_clone!(Basis);
376-
impl_export_by_clone!(Color);
377-
impl_export_by_clone!(GodotString);
378-
impl_export_by_clone!(NodePath);
379-
impl_export_by_clone!(PackedByteArray);
380-
impl_export_by_clone!(PackedColorArray);
381-
impl_export_by_clone!(PackedFloat32Array);
382-
impl_export_by_clone!(PackedFloat64Array);
383-
impl_export_by_clone!(PackedInt32Array);
384-
impl_export_by_clone!(PackedInt64Array);
385-
impl_export_by_clone!(PackedStringArray);
386-
impl_export_by_clone!(PackedVector2Array);
387-
impl_export_by_clone!(PackedVector3Array);
388-
impl_export_by_clone!(Plane);
389-
impl_export_by_clone!(Projection);
390-
impl_export_by_clone!(Quaternion);
391-
impl_export_by_clone!(Rect2);
392-
impl_export_by_clone!(Rect2i);
393-
impl_export_by_clone!(Rid);
394-
impl_export_by_clone!(StringName);
395-
impl_export_by_clone!(Transform2D);
396-
impl_export_by_clone!(Transform3D);
397-
impl_export_by_clone!(Vector2);
398-
impl_export_by_clone!(Vector2i);
399-
impl_export_by_clone!(Vector3);
400-
impl_export_by_clone!(Vector3i);
401-
impl_export_by_clone!(Vector4);
402-
403-
// Callables can be exported, however you can't do anything with them in the editor.
404-
// But we do need to be able to export them since we can't make something a property without exporting.
405-
// And it should be possible to access Callables by property from for instance GDScript.
406-
impl_export_by_clone!(Callable);
407-
// TODO investigate whether Signal should impl Export at all, and if so, how
408-
// impl_export_by_clone!(Signal);
409-
}
410-
411343
#[cfg(all(test, feature = "serde"))]
412344
pub(crate) mod test_utils {
413345
use serde::{Deserialize, Serialize};

godot-core/src/export.rs

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
5+
*/
6+
7+
use crate::builtin::meta::{ClassName, PropertyInfo};
8+
use crate::builtin::{GodotString, StringName};
9+
use crate::engine::global::{PropertyHint, PropertyUsageFlags};
10+
use crate::obj::GodotClass;
11+
12+
use godot_ffi as sys;
13+
use sys::VariantType;
14+
15+
/// Trait implemented for types that can be used as `#[export]` fields. This creates a copy of the
16+
/// value, for some type-specific definition of "copy". For example, `Array` and `Gd` are returned
17+
/// via `Share::share()` instead of copying the actual data.
18+
pub trait Export {
19+
/// Creates a copy to be returned from a getter.
20+
fn export(&self) -> Self;
21+
22+
/// The export info to use for an exported field of this type, if no other export info is specified.
23+
fn default_export_info() -> ExportInfo;
24+
}
25+
26+
/// Info needed for godot to understand how to export a type to the editor.
27+
#[derive(Clone, Eq, PartialEq, Debug)]
28+
pub struct ExportInfo {
29+
pub variant_type: VariantType,
30+
pub hint: PropertyHint,
31+
pub hint_string: GodotString,
32+
}
33+
34+
impl ExportInfo {
35+
/// Create a new `ExportInfo` with a property hint of
36+
/// [`PROPERTY_HINT_NONE`](PropertyHint::PROPERTY_HINT_NONE).
37+
pub fn with_hint_none(variant_type: VariantType) -> Self {
38+
Self {
39+
variant_type,
40+
hint: PropertyHint::PROPERTY_HINT_NONE,
41+
hint_string: GodotString::new(),
42+
}
43+
}
44+
45+
/// Create a `PropertyInfo` from this export info, using the given property_name and usage, as well as the class name of `C`.
46+
pub fn to_property_info<C: GodotClass>(
47+
self,
48+
property_name: StringName,
49+
usage: PropertyUsageFlags,
50+
) -> PropertyInfo {
51+
let Self {
52+
variant_type,
53+
hint,
54+
hint_string,
55+
} = self;
56+
57+
PropertyInfo {
58+
variant_type,
59+
class_name: ClassName::of::<C>(),
60+
property_name,
61+
hint,
62+
hint_string,
63+
usage,
64+
}
65+
}
66+
}
67+
68+
impl<T: Export> Export for Option<T> {
69+
fn export(&self) -> Self {
70+
self.as_ref().map(Export::export)
71+
}
72+
73+
fn default_export_info() -> ExportInfo {
74+
T::default_export_info()
75+
}
76+
}
77+
78+
/// Trait for types that can be represented as a type string for use with
79+
/// [`PropertyHint::PROPERTY_HINT_TYPE_STRING`].
80+
pub trait TypeStringHint {
81+
/// Returns the representation of this type as a type string.
82+
///
83+
/// See [`PropertyHint.PROPERTY_HINT_TYPE_STRING`](
84+
/// https://docs.godotengine.org/en/stable/classes/class_%40globalscope.html#enum-globalscope-propertyhint
85+
/// ).
86+
fn type_string() -> String;
87+
}
88+
89+
mod export_impls {
90+
use super::*;
91+
use crate::builtin::meta::VariantMetadata;
92+
use crate::builtin::*;
93+
94+
macro_rules! impl_export_by_clone {
95+
($Ty:ty => $variant_type:ident) => {
96+
impl Export for $Ty {
97+
fn export(&self) -> Self {
98+
// If `Self` does not implement `Clone`, this gives a clearer error message
99+
// than simply `self.clone()`.
100+
Clone::clone(self)
101+
}
102+
103+
fn default_export_info() -> ExportInfo {
104+
ExportInfo::with_hint_none(Self::variant_type())
105+
}
106+
}
107+
108+
impl TypeStringHint for $Ty {
109+
fn type_string() -> String {
110+
format!("{}:", sys::VariantType::$variant_type as i32)
111+
}
112+
}
113+
};
114+
}
115+
116+
impl_export_by_clone!(Aabb => Aabb);
117+
impl_export_by_clone!(bool => Bool);
118+
impl_export_by_clone!(Basis => Basis);
119+
impl_export_by_clone!(Vector2 => Vector2);
120+
impl_export_by_clone!(Vector3 => Vector3);
121+
impl_export_by_clone!(Vector4 => Vector4);
122+
impl_export_by_clone!(Vector2i => Vector2i);
123+
impl_export_by_clone!(Vector3i => Vector3i);
124+
impl_export_by_clone!(Quaternion => Quaternion);
125+
impl_export_by_clone!(Color => Color);
126+
impl_export_by_clone!(GodotString => String);
127+
impl_export_by_clone!(StringName => StringName);
128+
impl_export_by_clone!(NodePath => NodePath);
129+
impl_export_by_clone!(PackedByteArray => PackedByteArray);
130+
impl_export_by_clone!(PackedInt32Array => PackedInt32Array);
131+
impl_export_by_clone!(PackedInt64Array => PackedInt64Array);
132+
impl_export_by_clone!(PackedFloat32Array => PackedFloat32Array);
133+
impl_export_by_clone!(PackedFloat64Array => PackedFloat64Array);
134+
impl_export_by_clone!(PackedStringArray => PackedStringArray);
135+
impl_export_by_clone!(PackedVector2Array => PackedVector2Array);
136+
impl_export_by_clone!(PackedVector3Array => PackedVector3Array);
137+
impl_export_by_clone!(PackedColorArray => PackedColorArray);
138+
impl_export_by_clone!(Plane => Plane);
139+
impl_export_by_clone!(Projection => Projection);
140+
impl_export_by_clone!(Rid => Rid);
141+
impl_export_by_clone!(Rect2 => Rect2);
142+
impl_export_by_clone!(Rect2i => Rect2i);
143+
impl_export_by_clone!(Transform2D => Transform2D);
144+
impl_export_by_clone!(Transform3D => Transform3D);
145+
impl_export_by_clone!(f64 => Float);
146+
impl_export_by_clone!(i64 => Int);
147+
148+
// Godot uses f64 internally for floats, and if Godot tries to pass an invalid f32 into a rust property
149+
// then the property will just round the value or become inf.
150+
impl_export_by_clone!(f32 => Float);
151+
152+
// Godot uses i64 internally for integers, and if Godot tries to pass an invalid integer into a property
153+
// accepting one of the below values then rust will panic. In the editor this will appear as the property
154+
// failing to be set to a value and an error printed in the console. During runtime this will crash the
155+
// program and print the panic from rust stating that the property cannot store the value.
156+
impl_export_by_clone!(i32 => Int);
157+
impl_export_by_clone!(i16 => Int);
158+
impl_export_by_clone!(i8 => Int);
159+
impl_export_by_clone!(u32 => Int);
160+
impl_export_by_clone!(u16 => Int);
161+
impl_export_by_clone!(u8 => Int);
162+
163+
// Callables can be exported, however you can't do anything with them in the editor.
164+
// But we do need to be able to export them since we can't make something a property without exporting.
165+
// And it should be possible to access Callables by property from for instance GDScript.
166+
// TODO:
167+
// Remove export impl when we can create properties without exporting them.
168+
impl_export_by_clone!(Callable => Callable);
169+
170+
// impl_export_by_clone!(Signal => Signal);
171+
}

godot-core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mod storage;
99

1010
pub mod builder;
1111
pub mod builtin;
12+
pub mod export;
1213
pub mod init;
1314
pub mod log;
1415
pub mod macros;

godot-core/src/obj/gd.rs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ use crate::builtin::meta::{ClassName, VariantMetadata};
1818
use crate::builtin::{
1919
Callable, FromVariant, GodotString, StringName, ToVariant, Variant, VariantConversionError,
2020
};
21-
use crate::engine::Object;
21+
use crate::engine::{Node, Object, Resource};
22+
use crate::export::{Export, ExportInfo, TypeStringHint};
2223
use crate::obj::dom::Domain as _;
2324
use crate::obj::mem::Memory as _;
24-
use crate::obj::{cap, dom, mem, Export, GodotClass, Inherits, Share};
25+
use crate::obj::{cap, dom, mem, EngineEnum, GodotClass, Inherits, Share};
2526
use crate::obj::{GdMut, GdRef, InstanceId};
2627
use crate::storage::InstanceStorage;
2728
use crate::{callbacks, engine, out};
@@ -652,10 +653,49 @@ impl<T: GodotClass> Share for Gd<T> {
652653
}
653654
}
654655

656+
impl<T: GodotClass> TypeStringHint for Gd<T> {
657+
fn type_string() -> String {
658+
use engine::global::PropertyHint;
659+
660+
match Self::default_export_info().hint {
661+
hint @ (PropertyHint::PROPERTY_HINT_RESOURCE_TYPE
662+
| PropertyHint::PROPERTY_HINT_NODE_TYPE) => {
663+
format!(
664+
"{}/{}:{}",
665+
VariantType::Object as i32,
666+
hint.ord(),
667+
T::CLASS_NAME
668+
)
669+
}
670+
_ => format!("{}:", VariantType::Object as i32),
671+
}
672+
}
673+
}
674+
655675
impl<T: GodotClass> Export for Gd<T> {
656676
fn export(&self) -> Self {
657677
self.share()
658678
}
679+
680+
fn default_export_info() -> ExportInfo {
681+
let hint = if T::inherits::<Resource>() {
682+
engine::global::PropertyHint::PROPERTY_HINT_RESOURCE_TYPE
683+
} else if T::inherits::<Node>() {
684+
engine::global::PropertyHint::PROPERTY_HINT_NODE_TYPE
685+
} else {
686+
engine::global::PropertyHint::PROPERTY_HINT_NONE
687+
};
688+
689+
// Godot does this by default too, it doesn't seem to make a difference when not a resource/node
690+
// but is needed when it is a resource/node.
691+
let hint_string = T::CLASS_NAME.into();
692+
693+
ExportInfo {
694+
variant_type: Self::variant_type(),
695+
hint,
696+
hint_string,
697+
}
698+
}
659699
}
660700

661701
// ----------------------------------------------------------------------------------------------------------------------------------------------

0 commit comments

Comments
 (0)