godot_core/registry/property/
phantom_var.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 std::cmp::Ordering;
9use std::fmt;
10use std::hash::{Hash, Hasher};
11use std::marker::PhantomData;
12
13use crate::meta::{ClassId, GodotConvert, GodotType, PropertyHintInfo};
14use crate::registry::property::{Export, Var};
15
16/// A zero-sized type for creating a property without a backing field, accessible only through custom getter/setter functions.
17///
18/// This must be used in a struct deriving [`GodotClass`](../register/derive.GodotClass.html) and requires that the field has
19/// an explicit [`#[var]` attribute](../register/derive.GodotClass.html#register-properties--var) with a custom getter,
20/// and optionally a custom setter. Both getter and setter operate on the specified type `T`.
21///
22/// (Note that write-only properties, with a setter but not a getter, are not currently supported.
23/// Godot doesn't fully support them either, silently returning null instead of an error if the property is being read.)
24///
25/// # Example
26///
27/// Suppose you have a field `text` whose value you want to keep as a Rust `String` rather than a Godot `GString`,
28/// accepting the performance penalty for conversions whenever the property is accessed from Godot:
29///
30/// ```
31/// # use godot::prelude::*;
32/// #[derive(GodotClass)]
33/// #[class(init)]
34/// struct Banner {
35///     #[var(get = get_text, set = set_text)]
36///     text: PhantomVar<GString>,
37///
38///     text_string: String,
39/// }
40///
41/// #[godot_api]
42/// impl Banner {
43///     #[func]
44///     fn get_text(&self) -> GString {
45///         GString::from(&self.text_string)
46///     }
47///
48///     #[func]
49///     fn set_text(&mut self, text: GString) {
50///         self.text_string = String::from(&text);
51///     }
52/// }
53/// ```
54///
55/// This field can now be accessed from GDScript as `banner.text`.
56// Bounds for T are somewhat un-idiomatically directly on the type, rather than impls.
57// This improves error messages in IDEs when using the type as a field.
58pub struct PhantomVar<T: GodotType + Var>(PhantomData<T>);
59
60impl<T: GodotType + Var> GodotConvert for PhantomVar<T> {
61    type Via = T;
62}
63
64// `PhantomVar` supports only part of `Var`, but it has to implement it, otherwise we cannot implement `Export` either.
65// The `GodotClass` derive macro should ensure that the `Var` implementation is not used.
66impl<T: GodotType + Var> Var for PhantomVar<T> {
67    fn get_property(&self) -> Self::Via {
68        unreachable!("code generated by GodotClass should call the custom getter")
69    }
70
71    fn set_property(&mut self, _value: Self::Via) {
72        unreachable!("code generated by GodotClass should call the custom setter");
73    }
74
75    fn var_hint() -> PropertyHintInfo {
76        <T as Var>::var_hint()
77    }
78}
79
80// Reuse values from `T`, if any.
81impl<T: GodotType + Var + Export> Export for PhantomVar<T> {
82    fn export_hint() -> PropertyHintInfo {
83        <T as Export>::export_hint()
84    }
85
86    fn as_node_class() -> Option<ClassId> {
87        <T as Export>::as_node_class()
88    }
89}
90
91impl<T: GodotType + Var> Default for PhantomVar<T> {
92    fn default() -> Self {
93        Self(Default::default())
94    }
95}
96
97// Like `PhantomData` from the Rust standard library, `PhantomVar` implements many common traits like `Eq` and `Hash`
98// to allow these traits to be derived on containing structs as well.
99
100impl<T: GodotType + Var> Clone for PhantomVar<T> {
101    fn clone(&self) -> Self {
102        *self
103    }
104}
105
106impl<T: GodotType + Var> Copy for PhantomVar<T> {}
107
108impl<T: GodotType + Var> fmt::Debug for PhantomVar<T> {
109    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
110        f.debug_tuple("PhantomVar").finish()
111    }
112}
113
114impl<T: GodotType + Var> PartialEq for PhantomVar<T> {
115    fn eq(&self, _other: &Self) -> bool {
116        true
117    }
118}
119
120impl<T: GodotType + Var> Eq for PhantomVar<T> {}
121
122impl<T: GodotType + Var> PartialOrd for PhantomVar<T> {
123    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
124        Some(self.cmp(other))
125    }
126}
127
128impl<T: GodotType + Var> Ord for PhantomVar<T> {
129    fn cmp(&self, _other: &Self) -> Ordering {
130        Ordering::Equal
131    }
132}
133
134impl<T: GodotType + Var> Hash for PhantomVar<T> {
135    fn hash<H: Hasher>(&self, _state: &mut H) {}
136}
137
138// SAFETY: This type contains no data.
139unsafe impl<T: GodotType + Var> Send for PhantomVar<T> {}
140
141// SAFETY: This type contains no data.
142unsafe impl<T: GodotType + Var> Sync for PhantomVar<T> {}
143
144/// This type exists only as a place to add `compile_fail` doctests for `PhantomVar`, which do not need to be in the public documentation.
145///
146/// Omitting the `#[var]` attribute is an error:
147///
148/// ```compile_fail
149/// # use godot::prelude::*;
150/// #[derive(GodotClass)]
151/// #[class(init)]
152/// struct Oops {
153///     missing_var: PhantomVar<i64>,
154/// }
155/// ```
156///
157/// Declaring `#[var]` without a getter and/or setter is an error:
158///
159/// ```compile_fail
160/// # use godot::prelude::*;
161/// #[derive(GodotClass)]
162/// #[class(init)]
163/// struct Oops {
164///     #[var]
165///     missing_get_set: PhantomVar<i64>,
166/// }
167/// ```
168///
169/// Declaring `#[var]` without a getter is an error:
170///
171/// ```compile_fail
172/// # use godot::prelude::*;
173/// #[derive(GodotClass)]
174/// #[class(init)]
175/// struct Oops {
176///     #[var(set = setter)]
177///     missing_get: PhantomVar<i64>,
178/// }
179///
180/// #[godot_api]
181/// impl Oops {
182///     #[func]
183///     fn setter(&mut self, value: i64) {
184///     }
185/// }
186/// ```
187///
188/// Declaring `#[var]` with a default getter is an error:
189///
190/// ```compile_fail
191/// # use godot::prelude::*;
192/// #[derive(GodotClass)]
193/// #[class(init)]
194/// struct Oops {
195///     #[var(get, set = setter)]
196///     default_get: PhantomVar<i64>,
197/// }
198///
199/// #[godot_api]
200/// impl Oops {
201///     #[func]
202///     fn setter(&mut self, value: i64) {
203///     }
204/// }
205/// ```
206///
207/// Declaring `#[var]` with a default setter is an error:
208///
209/// ```compile_fail
210/// # use godot::prelude::*;
211/// #[derive(GodotClass)]
212/// #[class(init)]
213/// struct Oops {
214///     #[var(get = getter, set)]
215///     missing_set: PhantomVar<i64>,
216/// }
217///
218/// #[godot_api]
219/// impl Oops {
220///     #[func]
221///     fn getter(&self) -> i64 {
222///         0
223///     }
224/// }
225/// ```
226#[allow(dead_code)]
227struct PhantomVarDoctests;