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 godot_ffi as sys;
9
10use crate::builtin;
11use crate::builtin::{Variant, VariantType};
12use crate::global::PropertyUsageFlags;
13use crate::meta::error::ConvertError;
14use crate::meta::{
15 sealed, ClassId, FromGodot, GodotConvert, PropertyHintInfo, PropertyInfo, ToGodot,
16};
17use crate::registry::method::MethodParamOrReturnInfo;
18use crate::registry::property::builtin_type_string;
19
20// Re-export sys traits in this module, so all are in one place.
21#[rustfmt::skip] // Do not reorder.
22pub use sys::{ExtVariantType, GodotFfi, GodotNullableFfi};
23
24pub use crate::builtin::meta_reexport::PackedArrayElement;
25
26/// Conversion of [`GodotFfi`] types to/from [`Variant`].
27#[doc(hidden)]
28pub trait GodotFfiVariant: Sized + GodotFfi {
29 fn ffi_to_variant(&self) -> Variant;
30 fn ffi_from_variant(variant: &Variant) -> Result<Self, ConvertError>;
31}
32
33/// Type that is directly representable in the engine.
34///
35/// This trait cannot be implemented for custom user types; for those, [`GodotConvert`] exists instead.
36/// A type implements `GodotType` when Godot has a direct, native representation for it. For instance:
37/// - [`i64`] implements `GodotType`, since it can be directly represented by Godot's `int` type.
38/// - But [`VariantType`][crate::builtin::VariantType] does not implement `GodotType`. While it is an enum Godot uses,
39/// we have no native way to indicate to Godot that a value should be one of the variants of `VariantType`.
40//
41// Unlike `GodotFfi`, types implementing this trait don't need to fully represent its corresponding Godot
42// type. For instance [`i32`] does not implement `GodotFfi` because it cannot represent all values of
43// Godot's `int` type, however it does implement `GodotType` because we can set the meta-data of values with
44// this type to indicate that they are 32 bits large.
45pub trait GodotType: GodotConvert<Via = Self> + sealed::Sealed + Sized + 'static
46// 'static is not technically required, but it simplifies a few things (limits e.g. `ObjectArg`).
47{
48 // Value type for this type's FFI representation.
49 #[doc(hidden)]
50 type Ffi: GodotFfiVariant + 'static;
51
52 // Value or reference type when passing this type *to* Godot FFI.
53 #[doc(hidden)]
54 type ToFfi<'f>: GodotFfiVariant
55 where
56 Self: 'f;
57
58 /// Returns the FFI representation of this type, used for argument passing.
59 ///
60 /// Often returns a reference to the value, which can then be used to interact with Godot without cloning/inc-ref-ing the value.
61 /// For scalars and `Copy` types, this usually returns a copy of the value.
62 #[doc(hidden)]
63 fn to_ffi(&self) -> Self::ToFfi<'_>;
64
65 /// Consumes value and converts into FFI representation, used for return types.
66 ///
67 /// Unlike [`to_ffi()`][Self:to_ffi], this method consumes the value and is used for return types rather than argument passing.
68 /// Using `to_ffi()` for return types can be incorrect, since the associated types `Ffi` and `ToFfi<'f>` may differ and the latter
69 /// may not implement return type conversions such as [`GodotFfi::move_return_ptr()`].
70 #[doc(hidden)]
71 fn into_ffi(self) -> Self::Ffi;
72
73 /// Converts from FFI representation to Rust type.
74 #[doc(hidden)]
75 fn try_from_ffi(ffi: Self::Ffi) -> Result<Self, ConvertError>;
76
77 #[doc(hidden)]
78 fn from_ffi(ffi: Self::Ffi) -> Self {
79 Self::try_from_ffi(ffi).expect("Failed conversion from FFI representation to Rust type")
80 }
81
82 #[doc(hidden)]
83 fn param_metadata() -> sys::GDExtensionClassMethodArgumentMetadata {
84 Self::Ffi::default_param_metadata()
85 }
86
87 #[doc(hidden)]
88 fn class_id() -> ClassId {
89 // If we use `ClassId::of::<()>`, then this type shows up as `(no base)` in documentation.
90 ClassId::none()
91 }
92
93 #[doc(hidden)]
94 fn property_info(property_name: &str) -> PropertyInfo {
95 PropertyInfo {
96 variant_type: Self::Ffi::VARIANT_TYPE.variant_as_nil(),
97 class_id: Self::class_id(),
98 property_name: builtin::StringName::from(property_name),
99 hint_info: Self::property_hint_info(),
100 usage: PropertyUsageFlags::DEFAULT,
101 }
102 }
103
104 #[doc(hidden)]
105 fn property_hint_info() -> PropertyHintInfo {
106 // The default implementation is mostly good for builtin types.
107 //PropertyHintInfo::with_type_name::<Self>()
108
109 PropertyHintInfo::none()
110 }
111
112 #[doc(hidden)]
113 fn argument_info(property_name: &str) -> MethodParamOrReturnInfo {
114 MethodParamOrReturnInfo::new(Self::property_info(property_name), Self::param_metadata())
115 }
116
117 #[doc(hidden)]
118 fn return_info() -> Option<MethodParamOrReturnInfo> {
119 Some(MethodParamOrReturnInfo::new(
120 Self::property_info(""),
121 Self::param_metadata(),
122 ))
123 }
124
125 /// Returns a string representation of the Godot type name, as it is used in several property hint contexts.
126 ///
127 /// Examples:
128 /// - `MyClass` for objects
129 /// - `StringName`, `AABB` or `int` for built-ins
130 /// - `Array` for arrays
131 #[doc(hidden)]
132 fn godot_type_name() -> String;
133
134 /// Special-casing for `FromVariant` conversions higher up: true if the variant can be interpreted as `Option<Self>::None`.
135 ///
136 /// Returning false only means that this is not a special case, not that it cannot be `None`. Regular checks are expected to run afterward.
137 ///
138 /// This exists only for var-calls and serves a similar purpose as `GodotNullableFfi::is_null()` (although that handles general cases).
139 #[doc(hidden)]
140 fn qualifies_as_special_none(_from_variant: &Variant) -> bool {
141 false
142 }
143
144 /// Convert to `ObjectArg` for efficient object argument passing.
145 ///
146 /// Implemented in `GodotType` because Rust has no specialization, and there's no good way to have trait bounds in `ByObject`, but not in
147 /// other arg-passing strategies `ByValue`/`ByRef`.
148 ///
149 /// # Panics
150 /// If `Self` is not an object type (`Gd<T>`, `Option<Gd<T>>`). Note that `DynGd<T>` isn't directly implemented here, but uses `Gd<T>`'s
151 /// impl on the FFI layer.
152 #[doc(hidden)]
153 fn as_object_arg(&self) -> crate::meta::ObjectArg<'_> {
154 panic!(
155 "as_object_arg() called for non-object type: {}",
156 std::any::type_name::<Self>()
157 )
158 }
159}
160
161// ----------------------------------------------------------------------------------------------------------------------------------------------
162
163/// Marker trait to identify types that can be stored in [`Array<T>`][crate::builtin::Array].
164///
165/// The types, for which this trait is implemented, overlap mostly with [`GodotType`].
166///
167/// Notable differences are:
168/// - Only `VariantArray`, not `Array<T>` is allowed (typed arrays cannot be nested).
169/// - `Option` is only supported for `Option<Gd<T>>`, but not e.g. `Option<i32>`.
170///
171/// # Integer and float types
172/// `u8`, `i8`, `u16`, `i16`, `u32`, `i32` and `f32` are supported by this trait, however they don't have their own array type in Godot.
173/// The engine only knows about `i64` ("int") and `f64` ("float") types. This means that when using any integer or float type, Godot
174/// will treat it as the equivalent of GDScript's `Array[int]` or `Array[float]`, respectively.
175///
176/// 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`.
177/// 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
178/// end up with panics on element access (since the `Variant` storing 160 will fail to convert to `i8`). In Debug mode, we add additional
179/// best-effort checks to detect such errors, however they are expensive and not bullet-proof. If you need very rigid type safety, stick to
180/// `i64` and `f64`. The other types however can be extremely convenient and work well, as long as you are aware of the limitations.
181///
182/// `u64` is entirely unsupported since it cannot be safely stored inside a `Variant`.
183///
184/// Also, keep in mind that Godot uses `Variant` for each element. If performance matters and you have small element types such as `u8`,
185/// consider using packed arrays (e.g. `PackedByteArray`) instead.
186//
187#[diagnostic::on_unimplemented(
188 message = "`Array<T>` can only store element types supported in Godot arrays (no nesting).",
189 label = "has invalid element type"
190)]
191pub trait ArrayElement: ToGodot + FromGodot + sealed::Sealed + 'static {
192 // Note: several indirections in `ArrayElement` and the global `element_*` functions go through `GodotConvert::Via`,
193 // to not require Self: `GodotType`. What matters is how array elements map to Godot on the FFI level (`GodotType` trait).
194
195 /// Returns the representation of this type as a type string, e.g. `"4:"` for string, or `"24:34/MyClass"` for objects.
196 ///
197 /// (`4` and `24` are variant type ords; `34` is `PropertyHint::NODE_TYPE` ord).
198 ///
199 /// Used for elements in arrays (the latter despite `ArrayElement` not having a direct relation).
200 ///
201 /// See [`PropertyHint::TYPE_STRING`] and
202 /// [upstream docs](https://docs.godotengine.org/en/stable/classes/class_%40globalscope.html#enum-globalscope-propertyhint).
203 #[doc(hidden)]
204 fn element_type_string() -> String {
205 // Most array elements and all packed array elements are builtin types, so this is a good default.
206 builtin_type_string::<Self::Via>()
207 }
208
209 #[doc(hidden)]
210 fn debug_validate_elements(_array: &builtin::Array<Self>) -> Result<(), ConvertError> {
211 // No-op for most element types.
212 Ok(())
213 }
214}
215
216// ----------------------------------------------------------------------------------------------------------------------------------------------
217// Non-polymorphic helper functions, to avoid constant `<T::Via as GodotType>::` in the code.
218
219#[doc(hidden)]
220pub(crate) const fn element_variant_type<T: ArrayElement>() -> VariantType {
221 <T::Via as GodotType>::Ffi::VARIANT_TYPE.variant_as_nil()
222}
223
224/// Classifies `T` into one of Godot's builtin types. **Important:** variants are mapped to `NIL`.
225#[doc(hidden)]
226pub(crate) const fn ffi_variant_type<T: GodotConvert>() -> ExtVariantType {
227 <T::Via as GodotType>::Ffi::VARIANT_TYPE
228}
229
230#[doc(hidden)]
231pub(crate) fn element_godot_type_name<T: ArrayElement>() -> String {
232 <T::Via as GodotType>::godot_type_name()
233}
234
235// #[doc(hidden)]
236// pub(crate) fn element_godot_type_name<T: ArrayElement>() -> String {
237// <T::Via as GodotType>::godot_type_name()
238// }
239
240// ----------------------------------------------------------------------------------------------------------------------------------------------
241
242/// Implemented for types that can be used as immutable default parameters in `#[func]` methods.
243///
244/// This trait ensures that default parameter values cannot be mutated by callers, preventing the Python "mutable default argument" problem
245/// where a single default value is shared across multiple calls.
246///
247/// Post-processes the default value in some cases, e.g. makes `Array<T>` read-only via `into_read_only()`.
248///
249/// At the moment, this trait is conservatively implemented for types where immutability can be statically guaranteed.
250/// Depending on usage, the API might be expanded in the future to allow defaults whose immutability is only determined
251/// at runtime (e.g. untyped arrays/dictionaries where all element types are immutable).
252///
253/// # Safety
254/// Allows to use the implementors in a limited `Sync` context. Implementing this trait asserts that `Self` is either:
255/// - `Copy`, i.e. each instance is truly independent.
256/// - Thread-safe in the sense that `clone()` is thread-safe. Individual clones must not offer a way to mutate the value or cause race conditions.
257#[diagnostic::on_unimplemented(
258 message = "#[opt(default = ...)] only supports a set of truly immutable types",
259 label = "this type is not immutable and thus not eligible for a default value"
260)]
261pub unsafe trait GodotImmutable: GodotConvert + Sized {
262 fn into_runtime_immutable(self) -> Self {
263 self
264 }
265}
266
267mod godot_immutable_impls {
268 use super::GodotImmutable;
269 use crate::builtin::*;
270 use crate::meta::ArrayElement;
271
272 unsafe impl GodotImmutable for bool {}
273 unsafe impl GodotImmutable for i8 {}
274 unsafe impl GodotImmutable for u8 {}
275 unsafe impl GodotImmutable for i16 {}
276 unsafe impl GodotImmutable for u16 {}
277 unsafe impl GodotImmutable for i32 {}
278 unsafe impl GodotImmutable for u32 {}
279 unsafe impl GodotImmutable for i64 {}
280 unsafe impl GodotImmutable for f32 {}
281 unsafe impl GodotImmutable for f64 {}
282
283 // No NodePath, Callable, Signal, Rid, Variant.
284 unsafe impl GodotImmutable for Aabb {}
285 unsafe impl GodotImmutable for Basis {}
286 unsafe impl GodotImmutable for Color {}
287 unsafe impl GodotImmutable for GString {}
288 unsafe impl GodotImmutable for Plane {}
289 unsafe impl GodotImmutable for Projection {}
290 unsafe impl GodotImmutable for Quaternion {}
291 unsafe impl GodotImmutable for Rect2 {}
292 unsafe impl GodotImmutable for Rect2i {}
293 unsafe impl GodotImmutable for StringName {}
294 unsafe impl GodotImmutable for Transform2D {}
295 unsafe impl GodotImmutable for Transform3D {}
296 unsafe impl GodotImmutable for Vector2 {}
297 unsafe impl GodotImmutable for Vector2i {}
298 unsafe impl GodotImmutable for Vector3 {}
299 unsafe impl GodotImmutable for Vector3i {}
300 unsafe impl GodotImmutable for Vector4 {}
301 unsafe impl GodotImmutable for Vector4i {}
302
303 unsafe impl GodotImmutable for PackedByteArray {}
304 unsafe impl GodotImmutable for PackedColorArray {}
305 unsafe impl GodotImmutable for PackedFloat32Array {}
306 unsafe impl GodotImmutable for PackedFloat64Array {}
307 unsafe impl GodotImmutable for PackedInt32Array {}
308 unsafe impl GodotImmutable for PackedInt64Array {}
309 unsafe impl GodotImmutable for PackedStringArray {}
310 unsafe impl GodotImmutable for PackedVector2Array {}
311 unsafe impl GodotImmutable for PackedVector3Array {}
312 #[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
313 unsafe impl GodotImmutable for PackedVector4Array {}
314
315 unsafe impl<T> GodotImmutable for Array<T>
316 where
317 T: GodotImmutable + ArrayElement,
318 {
319 fn into_runtime_immutable(self) -> Self {
320 self.into_read_only()
321 }
322 }
323}