godot_core/meta/godot_convert/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
8mod impls;
9
10use crate::builtin::Variant;
11use crate::meta::error::ConvertError;
12use crate::meta::shape::GodotShape;
13use crate::meta::traits::GodotFfiVariant;
14use crate::meta::{ArgPassing, GodotType, ToArg};
15
16/// Indicates that a type can be passed to/from Godot, either directly or through an intermediate "via" type.
17///
18/// The associated type `Via` specifies _how_ this type is passed across the FFI boundary to/from Godot.
19/// Generally [`ToGodot`] needs to be implemented to pass a type to Godot, and [`FromGodot`] to receive this type from Godot.
20///
21/// [`GodotType`] is a stronger bound than [`GodotConvert`], since it expresses that a type is _directly_ representable
22/// in Godot (without intermediate "via"). Every `GodotType` also implements `GodotConvert` with `Via = Self`.
23///
24/// Please read the [`godot::meta` module docs](index.html) for further information about conversions.
25///
26/// # u64
27/// The type `u64` is **not** supported by `ToGodot` and `FromGodot` traits. You can thus not pass it in `#[func]` parameters/return types.
28///
29/// The reason is that Godot's `Variant` type, and therefore also GDScript, only support _signed_ 64-bit integers (`i64`).
30/// Implicitly wrapping `u64` to `i64` would be surprising behavior, as the value could suddenly change for large numbers.
31/// As such, godot-rust leaves this decision to users: it's possible to define a newtype around `u64` with custom `ToGodot`/`FromGodot` impls.
32#[doc(alias = "via", alias = "transparent")]
33#[diagnostic::on_unimplemented(
34 message = "`GodotConvert` is needed for `#[func]` parameters/returns, as well as `#[var]` and `#[export]` properties",
35 note = "check following errors for more information"
36)]
37pub trait GodotConvert {
38 /// The type through which `Self` is represented in Godot.
39 type Via: GodotType;
40
41 /// Which "shape" this type has for property registration (e.g. builtin, enum, ...).
42 ///
43 /// godot-rust derives property hints, class names, usage flags, and element metadata from this.
44 fn godot_shape() -> GodotShape;
45}
46
47/// Defines the canonical conversion to Godot for a type.
48///
49/// It is assumed that all the methods return equal values given equal inputs. Additionally, it is assumed
50/// that if [`FromGodot`] is implemented, converting to Godot and back again will return a value equal to the
51/// starting value.
52///
53/// Violating these assumptions is safe but will give unexpected results.
54///
55/// Please read the [`godot::meta` module docs](index.html) for further information about conversions.
56///
57/// This trait can be derived using the [`#[derive(GodotConvert)]`](../register/derive.GodotConvert.html) macro.
58///
59/// # `Result<T, E>`
60/// It is possible to return `Result<T, E>` from `#[func]`, when `T: ToGodot` and [`E: ErrorToGodot`][crate::meta::error::ErrorToGodot].
61/// However, `Result<T, E>` currently does not implement `ToGodot` itself, as it is not generally infallible.
62///
63/// # Panics
64/// Currently, the methods `to_godot()`, `to_godot_owned()` and `to_variant()` are infallible and never panic, i.e. you can convert every value
65/// to a Godot representation. If new types are supported in the future that may not satisfy this (example: `Result<T, E>`), it's possible
66/// that panics are introduced _only for those new types_.
67#[diagnostic::on_unimplemented(
68 message = "passing type `{Self}` to Godot requires `ToGodot` trait, which is usually provided by the library",
69 note = "ToGodot is implemented for built-in types (i32, Vector2, GString, …). For objects, use Gd<T> instead of T.",
70 note = "if you really need a custom representation (for non-class types), implement ToGodot manually or use #[derive(GodotConvert)].",
71 note = "see also: https://godot-rust.github.io/docs/gdext/master/godot/meta"
72)]
73pub trait ToGodot: Sized + GodotConvert {
74 /// Whether arguments of this type are passed by value or by reference.
75 ///
76 /// Can be either [`ByValue`][crate::meta::ByValue] or [`ByRef`][crate::meta::ByRef]. In most cases, you need `ByValue`.
77 ///
78 /// Select `ByValue` if:
79 /// - `Self` is `Copy` (e.g. `i32`, `f64`, `Vector2`, `Color`, etc).
80 /// - You need a conversion (e.g. `Self = MyString`, `Via = GString`).
81 /// - You like the simple life and can't be bothered with lifetimes.
82 ///
83 /// Select `ByRef` if:
84 /// - Performance of argument passing is very important and you have measured it.
85 /// - You store a cached value which can be borrowed (e.g. `&GString`).
86 ///
87 /// Will auto-implement [`AsArg<T>`][crate::meta::AsArg] for either `T` (by-value) or for `&T` (by-reference).
88 /// This has an influence on contexts such as [`Array::push()`][crate::builtin::Array::push], the [`array![...]`][crate::builtin::array]
89 /// macro or generated signal `emit()` signatures.
90 type Pass: ArgPassing;
91
92 /// Converts this type to Godot representation, optimizing for zero-copy when possible.
93 ///
94 /// # Return type
95 /// - For `Pass = ByValue`, returns owned `Self::Via`.
96 /// - For `Pass = ByRef`, returns borrowed `&Self::Via`.
97 fn to_godot(&self) -> ToArg<'_, Self::Via, Self::Pass>;
98
99 /// Converts this type to owned Godot representation.
100 ///
101 /// Always returns `Self::Via`, cloning if necessary for ByRef types.
102 fn to_godot_owned(&self) -> Self::Via {
103 Self::Pass::ref_to_owned_via(self)
104 }
105
106 /// Converts this type to a [Variant].
107 // Exception safety: introducing a panic would have invariant implications, e.g. in Array::resize().
108 fn to_variant(&self) -> Variant {
109 Self::Pass::ref_to_variant(self)
110 }
111}
112
113/// Defines the canonical conversion from Godot for a type.
114///
115/// It is assumed that all the methods return equal values given equal inputs. Additionally, it is assumed
116/// that if [`ToGodot`] is implemented, converting to Godot and back again will return a value equal to the
117/// starting value.
118///
119/// Violating these assumptions is safe but will give unexpected results.
120///
121/// Please read the [`godot::meta` module docs](index.html) for further information about conversions.
122///
123/// This trait can be derived using the [`#[derive(GodotConvert)]`](../register/derive.GodotConvert.html) macro.
124#[diagnostic::on_unimplemented(
125 message = "receiving type `{Self}` from Godot requires `FromGodot` trait, which is usually provided by the library",
126 note = "FromGodot is implemented for built-in types (i32, Vector2, GString, …). For objects, use Gd<T> instead of T.",
127 note = "if you really need a custom representation (for non-class types), implement FromGodot manually or use #[derive(GodotConvert)]",
128 note = "see also: https://godot-rust.github.io/docs/gdext/master/godot/meta"
129)]
130pub trait FromGodot: Sized + GodotConvert {
131 /// Converts the Godot representation to this type, returning `Err` on failure.
132 fn try_from_godot(via: Self::Via) -> Result<Self, ConvertError>;
133
134 /// ⚠️ Converts the Godot representation to this type.
135 ///
136 /// # Panics
137 /// If the conversion fails.
138 fn from_godot(via: Self::Via) -> Self {
139 Self::try_from_godot(via)
140 .unwrap_or_else(|err| panic!("FromGodot::from_godot() failed: {err}"))
141 }
142
143 /// Performs the conversion from a [`Variant`], returning `Err` on failure.
144 fn try_from_variant(variant: &Variant) -> Result<Self, ConvertError> {
145 let ffi = <Self::Via as GodotType>::Ffi::ffi_from_variant(variant)?;
146
147 let via = Self::Via::try_from_ffi(ffi)?;
148 Self::try_from_godot(via)
149 }
150
151 /// ⚠️ Performs the conversion from a [`Variant`].
152 ///
153 /// # Panics
154 /// If the conversion fails.
155 fn from_variant(variant: &Variant) -> Self {
156 Self::try_from_variant(variant).unwrap_or_else(|err| {
157 panic!("FromGodot::from_variant() failed -- {err}");
158 })
159 }
160}
161
162// ----------------------------------------------------------------------------------------------------------------------------------------------
163// Engine conversion traits (for APIs and virtual methods, not user-facing #[func])
164
165/// Engine-internal variant of [`ToGodot`], used for engine APIs and virtual methods.
166///
167/// This trait exists to support types like `u64` that work in engine contexts (backed by C++ `uint64_t`), but cannot be used in user-facing
168/// `#[func]` methods (as they don't fit in GDScript/Variant, which can only store `i64`).
169///
170/// On the FFI level, `u64` are passed as `i64` (same bit pattern). The C++ side reinterprets the bits again as `uint64_t` in engine APIs
171/// and bitfields. User-defined GDScript code generally does not get into contact with `i64` (except for bitfields).
172///
173/// For internal use only; see [`ToGodot`] for user-facing conversions.
174#[doc(hidden)]
175pub trait EngineToGodot: Sized + GodotConvert {
176 /// Whether arguments of this type are passed by value or by reference.
177 type Pass: ArgPassing;
178
179 /// Converts this type to Godot representation, optimizing for zero-copy when possible.
180 fn engine_to_godot(&self) -> ToArg<'_, Self::Via, Self::Pass>;
181
182 /// Converts this type to owned Godot representation.
183 fn engine_to_godot_owned(&self) -> Self::Via {
184 Self::Pass::ref_to_owned_via(self)
185 }
186
187 fn engine_to_variant(&self) -> Variant;
188
189 /// Consuming conversion to `Variant` for `#[func]` varcall return values. Relevant for `Result<T, E>`.
190 ///
191 /// Defaults to infallible [`Self::engine_to_variant()`] for types without `ToGodot` (e.g. `u64`). For `ToGodot` types,
192 /// the blanket impl delegates to [`ToGodot::__godot_try_into_variant()`].
193 //
194 // Could alternatively be avoided by splitting Signature in-call methods into `in_varcall`/`in_ptrcall` (EngineToGodot, for virtual
195 // methods + property accessors) and `in_func_varcall`/`in_func_ptrcall` (ToGodot, for #[func]). That avoids this trait method but
196 // duplicates more code in signature.rs and requires is_func plumbing in the macro. Trying this resulted in ~120 additional LoC.
197 fn engine_try_into_variant(
198 self,
199 _call_ctx: &crate::meta::CallContext,
200 ) -> Result<Variant, crate::meta::error::CallError> {
201 Ok(self.engine_to_variant())
202 }
203
204 /// Consuming conversion to the Godot `Via` type for `#[func]` ptrcall return values.
205 ///
206 /// Defaults to infallible [`Self::engine_to_godot_owned()`]. For `ToGodot` types,
207 /// the blanket impl delegates to [`ToGodot::__godot_try_into_godot_owned()`].
208 fn engine_try_into_godot_owned(
209 self,
210 _call_ctx: &crate::meta::CallContext,
211 ) -> Result<Self::Via, crate::meta::error::CallError> {
212 Ok(self.engine_to_godot_owned())
213 }
214}
215
216// Blanket implementations: all user-facing types work in engine contexts.
217impl<T: ToGodot> EngineToGodot for T {
218 type Pass = T::Pass;
219
220 fn engine_to_godot(&self) -> ToArg<'_, Self::Via, Self::Pass> {
221 <T as ToGodot>::to_godot(self)
222 }
223
224 fn engine_to_godot_owned(&self) -> Self::Via {
225 <T as ToGodot>::to_godot_owned(self)
226 }
227
228 fn engine_to_variant(&self) -> Variant {
229 <T as ToGodot>::to_variant(self)
230 }
231
232 fn engine_try_into_variant(
233 self,
234 _call_ctx: &crate::meta::CallContext,
235 ) -> Result<Variant, crate::meta::error::CallError> {
236 Ok(self.to_variant())
237 }
238
239 fn engine_try_into_godot_owned(
240 self,
241 _call_ctx: &crate::meta::CallContext,
242 ) -> Result<Self::Via, crate::meta::error::CallError> {
243 Ok(self.to_godot_owned())
244 }
245}
246
247/// Engine-internal variant of [`FromGodot`], used for engine APIs and virtual methods.
248///
249/// See [`EngineToGodot`] for rationale.
250///
251/// For internal use only; see [`FromGodot`] for user-facing conversions.
252#[doc(hidden)]
253pub trait EngineFromGodot: Sized + GodotConvert {
254 /// Converts the Godot representation to this type, returning `Err` on failure.
255 fn engine_try_from_godot(via: Self::Via) -> Result<Self, ConvertError>;
256
257 fn engine_try_from_variant(variant: &Variant) -> Result<Self, ConvertError>;
258}
259
260impl<T: FromGodot> EngineFromGodot for T {
261 fn engine_try_from_godot(via: Self::Via) -> Result<Self, ConvertError> {
262 <T as FromGodot>::try_from_godot(via)
263 }
264
265 fn engine_try_from_variant(variant: &Variant) -> Result<Self, ConvertError> {
266 <T as FromGodot>::try_from_variant(variant)
267 }
268}
269
270// ----------------------------------------------------------------------------------------------------------------------------------------------
271// Impls
272
273#[macro_export]
274macro_rules! impl_godot_as_self {
275 ($T:ty: $Passing:ident) => {
276 impl $crate::meta::GodotConvert for $T {
277 type Via = $T;
278
279 fn godot_shape() -> $crate::meta::shape::GodotShape {
280 $crate::meta::shape::GodotShape::of_builtin::<$T>()
281 }
282 }
283
284 $crate::impl_godot_as_self!(@to_godot $T: $Passing);
285
286 impl $crate::meta::FromGodot for $T {
287 #[inline]
288 fn try_from_godot(via: Self::Via) -> Result<Self, $crate::meta::error::ConvertError> {
289 Ok(via)
290 }
291 }
292 };
293
294 (@to_godot $T:ty: ByValue) => {
295 impl $crate::meta::ToGodot for $T {
296 type Pass = $crate::meta::ByValue;
297
298 #[inline]
299 fn to_godot(&self) -> Self::Via {
300 self.clone()
301 }
302 }
303 };
304
305 (@to_godot $T:ty: ByRef) => {
306 impl $crate::meta::ToGodot for $T {
307 type Pass = $crate::meta::ByRef;
308
309 #[inline]
310 fn to_godot(&self) -> &Self::Via {
311 self
312 }
313 }
314 };
315
316 (@to_godot $T:ty: ByVariant) => {
317 impl $crate::meta::ToGodot for $T {
318 type Pass = $crate::meta::ByVariant;
319
320 #[inline]
321 fn to_godot(&self) -> &Self::Via {
322 self
323 }
324 }
325 };
326}