godot_core/meta/traits.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
8use crate::builtin::{Variant, VariantType};
9use crate::global::PropertyUsageFlags;
10use crate::meta::error::ConvertError;
11use crate::meta::{
12 sealed, ClassName, FromGodot, GodotConvert, ParamType, PropertyHintInfo, PropertyInfo, ToGodot,
13};
14use crate::registry::method::MethodParamOrReturnInfo;
15use godot_ffi as sys;
16
17// Re-export sys traits in this module, so all are in one place.
18use crate::builtin;
19use crate::registry::property::builtin_type_string;
20pub use sys::{ExtVariantType, GodotFfi, GodotNullableFfi};
21
22/// Conversion of [`GodotFfi`] types to/from [`Variant`].
23#[doc(hidden)]
24pub trait GodotFfiVariant: Sized + GodotFfi {
25 fn ffi_to_variant(&self) -> Variant;
26 fn ffi_from_variant(variant: &Variant) -> Result<Self, ConvertError>;
27}
28
29/// Type that is directly representable in the engine.
30///
31/// This trait cannot be implemented for custom user types; for those, [`GodotConvert`] exists instead.
32/// A type implements `GodotType` when Godot has a direct, native representation for it. For instance:
33/// - [`i64`] implements `GodotType`, since it can be directly represented by Godot's `int` type.
34/// - But [`VariantType`][crate::builtin::VariantType] does not implement `GodotType`. While it is an enum Godot uses,
35/// we have no native way to indicate to Godot that a value should be one of the variants of `VariantType`.
36//
37// Unlike `GodotFfi`, types implementing this trait don't need to fully represent its corresponding Godot
38// type. For instance [`i32`] does not implement `GodotFfi` because it cannot represent all values of
39// Godot's `int` type, however it does implement `GodotType` because we can set the meta-data of values with
40// this type to indicate that they are 32 bits large.
41pub trait GodotType: GodotConvert<Via = Self> + sealed::Sealed + Sized + 'static
42// 'static is not technically required, but it simplifies a few things (limits e.g. `ObjectArg`).
43{
44 // Value type for this type's FFI representation.
45 #[doc(hidden)]
46 type Ffi: GodotFfiVariant + 'static;
47
48 // Value or reference type when passing this type *to* Godot FFI.
49 #[doc(hidden)]
50 type ToFfi<'f>: GodotFfiVariant
51 where
52 Self: 'f;
53
54 /// Returns the FFI representation of this type, used for argument passing.
55 ///
56 /// Often returns a reference to the value, which can then be used to interact with Godot without cloning/inc-ref-ing the value.
57 /// For scalars and `Copy` types, this usually returns a copy of the value.
58 #[doc(hidden)]
59 fn to_ffi(&self) -> Self::ToFfi<'_>;
60
61 /// Consumes value and converts into FFI representation, used for return types.
62 ///
63 /// Unlike [`to_ffi()`][Self:to_ffi], this method consumes the value and is used for return types rather than argument passing.
64 /// Using `to_ffi()` for return types can be incorrect, since the associated types `Ffi` and `ToFfi<'f>` may differ and the latter
65 /// may not implement return type conversions such as [`GodotFfi::move_return_ptr()`].
66 #[doc(hidden)]
67 fn into_ffi(self) -> Self::Ffi;
68
69 /// Converts from FFI representation to Rust type.
70 #[doc(hidden)]
71 fn try_from_ffi(ffi: Self::Ffi) -> Result<Self, ConvertError>;
72
73 #[doc(hidden)]
74 fn from_ffi(ffi: Self::Ffi) -> Self {
75 Self::try_from_ffi(ffi).expect("Failed conversion from FFI representation to Rust type")
76 }
77
78 #[doc(hidden)]
79 fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata {
80 Self::Ffi::default_param_metadata()
81 }
82
83 #[doc(hidden)]
84 fn class_name() -> ClassName {
85 // If we use `ClassName::of::<()>()` then this type shows up as `(no base)` in documentation.
86 ClassName::none()
87 }
88
89 #[doc(hidden)]
90 fn property_info(property_name: &str) -> PropertyInfo {
91 PropertyInfo {
92 variant_type: Self::Ffi::VARIANT_TYPE.variant_as_nil(),
93 class_name: Self::class_name(),
94 property_name: builtin::StringName::from(property_name),
95 hint_info: Self::property_hint_info(),
96 usage: PropertyUsageFlags::DEFAULT,
97 }
98 }
99
100 #[doc(hidden)]
101 fn property_hint_info() -> PropertyHintInfo {
102 // The default implementation is mostly good for builtin types.
103 //PropertyHintInfo::with_type_name::<Self>()
104
105 PropertyHintInfo::none()
106 }
107
108 #[doc(hidden)]
109 fn argument_info(property_name: &str) -> MethodParamOrReturnInfo {
110 MethodParamOrReturnInfo::new(Self::property_info(property_name), Self::param_metadata())
111 }
112
113 #[doc(hidden)]
114 fn return_info() -> Option<MethodParamOrReturnInfo> {
115 Some(MethodParamOrReturnInfo::new(
116 Self::property_info(""),
117 Self::param_metadata(),
118 ))
119 }
120
121 /// Returns a string representation of the Godot type name, as it is used in several property hint contexts.
122 ///
123 /// Examples:
124 /// - `MyClass` for objects
125 /// - `StringName`, `AABB` or `int` for built-ins
126 /// - `Array` for arrays
127 #[doc(hidden)]
128 fn godot_type_name() -> String;
129
130 /// Special-casing for `FromVariant` conversions higher up: true if the variant can be interpreted as `Option<Self>::None`.
131 ///
132 /// Returning false only means that this is not a special case, not that it cannot be `None`. Regular checks are expected to run afterward.
133 ///
134 /// This exists only for var-calls and serves a similar purpose as `GodotNullableFfi::is_null()` (although that handles general cases).
135 #[doc(hidden)]
136 fn qualifies_as_special_none(_from_variant: &Variant) -> bool {
137 false
138 }
139}
140
141// ----------------------------------------------------------------------------------------------------------------------------------------------
142
143/// Marker trait to identify types that can be stored in [`Array<T>`][crate::builtin::Array].
144///
145/// The types, for which this trait is implemented, overlap mostly with [`GodotType`].
146///
147/// Notable differences are:
148/// - Only `VariantArray`, not `Array<T>` is allowed (typed arrays cannot be nested).
149/// - `Option` is only supported for `Option<Gd<T>>`, but not e.g. `Option<i32>`.
150///
151/// # Integer and float types
152/// `u8`, `i8`, `u16`, `i16`, `u32`, `i32` and `f32` are supported by this trait, however they don't have their own array type in Godot.
153/// The engine only knows about `i64` ("int") and `f64` ("float") types. This means that when using any integer or float type, Godot
154/// will treat it as the equivalent of GDScript's `Array[int]` or `Array[float]`, respectively.
155///
156/// As a result, when converting from a Godot typed array to a Rust `Array<T>`, the values stored may not actually fit into a `T`.
157/// For example, you have a GDScript `Array[int]` which stores value 160, and you convert it to a Rust `Array<i8>`. This means that you may
158/// end up with panics on element access (since the `Variant` storing 160 will fail to convert to `i8`). In Debug mode, we add additional
159/// best-effort checks to detect such errors, however they are expensive and not bullet-proof. If you need very rigid type safety, stick to
160/// `i64` and `f64`. The other types however can be extremely convenient and work well, as long as you are aware of the limitations.
161///
162/// `u64` is entirely unsupported since it cannot be safely stored inside a `Variant`.
163///
164/// Also, keep in mind that Godot uses `Variant` for each element. If performance matters and you have small element types such as `u8`,
165/// consider using packed arrays (e.g. `PackedByteArray`) instead.
166//
167// TODO: The `ParamType` super trait is no longer needed and can be removed in 0.4. We are only keeping it for backwards compatibility.
168#[diagnostic::on_unimplemented(
169 message = "`Array<T>` can only store element types supported in Godot arrays (no nesting).",
170 label = "has invalid element type"
171)]
172pub trait ArrayElement: ToGodot + FromGodot + sealed::Sealed + ParamType + 'static {
173 // Note: several indirections in `ArrayElement` and the global `element_*` functions go through `GodotConvert::Via`,
174 // to not require Self: `GodotType`. What matters is how array elements map to Godot on the FFI level (`GodotType` trait).
175
176 /// Returns the representation of this type as a type string, e.g. `"4:"` for string, or `"24:34/MyClass"` for objects.
177 ///
178 /// (`4` and `24` are variant type ords; `34` is `PropertyHint::NODE_TYPE` ord).
179 ///
180 /// Used for elements in arrays (the latter despite `ArrayElement` not having a direct relation).
181 ///
182 /// See [`PropertyHint::TYPE_STRING`] and
183 /// [upstream docs](https://docs.godotengine.org/en/stable/classes/class_%40globalscope.html#enum-globalscope-propertyhint).
184 #[doc(hidden)]
185 fn element_type_string() -> String {
186 // Most array elements and all packed array elements are builtin types, so this is a good default.
187 builtin_type_string::<Self::Via>()
188 }
189
190 #[doc(hidden)]
191 fn debug_validate_elements(_array: &builtin::Array<Self>) -> Result<(), ConvertError> {
192 // No-op for most element types.
193 Ok(())
194 }
195}
196
197// ----------------------------------------------------------------------------------------------------------------------------------------------
198// Non-polymorphic helper functions, to avoid constant `<T::Via as GodotType>::` in the code.
199
200#[doc(hidden)]
201pub(crate) const fn element_variant_type<T: ArrayElement>() -> VariantType {
202 <T::Via as GodotType>::Ffi::VARIANT_TYPE.variant_as_nil()
203}
204
205/// Classifies `T` into one of Godot's builtin types. **Important:** variants are mapped to `NIL`.
206#[doc(hidden)]
207pub(crate) const fn ffi_variant_type<T: GodotConvert>() -> ExtVariantType {
208 <T::Via as GodotType>::Ffi::VARIANT_TYPE
209}
210
211#[doc(hidden)]
212pub(crate) fn element_godot_type_name<T: ArrayElement>() -> String {
213 <T::Via as GodotType>::godot_type_name()
214}
215
216// #[doc(hidden)]
217// pub(crate) fn element_godot_type_name<T: ArrayElement>() -> String {
218// <T::Via as GodotType>::godot_type_name()
219// }
220
221/// Marker trait to identify types that can be stored in `Packed*Array` types.
222#[diagnostic::on_unimplemented(
223 message = "`Packed*Array` can only store element types supported in Godot packed arrays.",
224 label = "has invalid element type"
225)]
226pub trait PackedArrayElement: GodotType + sealed::Sealed {
227 /// See [`ArrayElement::element_type_string()`].
228 #[doc(hidden)]
229 fn element_type_string() -> String {
230 builtin_type_string::<Self>()
231 }
232}
233
234// Implement all packed array element types.
235impl PackedArrayElement for u8 {}
236impl PackedArrayElement for i32 {}
237impl PackedArrayElement for i64 {}
238impl PackedArrayElement for f32 {}
239impl PackedArrayElement for f64 {}
240impl PackedArrayElement for builtin::Vector2 {}
241impl PackedArrayElement for builtin::Vector3 {}
242#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
243impl PackedArrayElement for builtin::Vector4 {}
244impl PackedArrayElement for builtin::Color {}
245impl PackedArrayElement for builtin::GString {}