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;