gdnative_core/export/
property.rs

1//! Property registration.
2
3// For the `PropertyUsage` bitflags declaration. The attribute doesn't work above the macro
4// invocation.
5#![allow(clippy::unnecessary_cast)]
6
7use std::marker::PhantomData;
8
9use accessor::{Getter, RawGetter, RawSetter, Setter};
10use invalid_accessor::{InvalidGetter, InvalidSetter};
11
12use crate::core_types::*;
13use crate::export::{ClassBuilder, NativeClass};
14use crate::object::ownership::Shared;
15use crate::object::{GodotObject, Instance, Ref};
16use crate::private::get_api;
17
18use super::RpcMode;
19
20mod accessor;
21mod invalid_accessor;
22
23pub mod hint;
24
25/// Trait for exportable types.
26pub trait Export: crate::core_types::ToVariant {
27    /// A type-specific hint type that is valid for the type being exported.
28    ///
29    /// If this type shows up as `NoHint`, a private, uninhabitable type indicating
30    /// that there are no hints available for the time being, users *must* use `None`
31    /// for properties of this type. This ensures that it will not be a breaking change
32    /// to add a hint for the type later, since it supports no operations and cannot
33    /// be named directly in user code.
34    type Hint;
35
36    /// Returns `ExportInfo` given an optional typed hint.
37    fn export_info(hint: Option<Self::Hint>) -> ExportInfo;
38}
39
40/// Metadata about the exported property.
41#[derive(Debug)]
42pub struct ExportInfo {
43    pub(super) variant_type: VariantType,
44    pub(super) hint_kind: sys::godot_property_hint,
45    pub(super) hint_string: GodotString,
46}
47
48impl ExportInfo {
49    /// Create an `ExportInfo` with the given Variant type, but without a hint.
50    #[inline]
51    pub fn new(variant_type: VariantType) -> Self {
52        ExportInfo {
53            variant_type,
54            hint_kind: sys::godot_property_hint_GODOT_PROPERTY_HINT_NONE,
55            hint_string: GodotString::new(),
56        }
57    }
58
59    /// Create an `ExportInfo` with a hint for a specific Godot resource type.
60    #[inline]
61    pub fn resource_type<T>() -> Self
62    where
63        T: GodotObject,
64    {
65        ExportInfo {
66            variant_type: VariantType::Object,
67            hint_kind: sys::godot_property_hint_GODOT_PROPERTY_HINT_RESOURCE_TYPE,
68            hint_string: T::class_name().into(),
69        }
70    }
71}
72
73/// Builder type used to register a property on a `NativeClass`.
74#[derive(Debug)]
75#[must_use = "PropertyBuilder left unbuilt -- did you forget to call done()?"]
76pub struct PropertyBuilder<'a, C, T: Export, S = InvalidSetter<'a>, G = InvalidGetter<'a>> {
77    name: &'a str,
78    setter: S,
79    getter: G,
80    default: Option<T>,
81    hint: Option<T::Hint>,
82    usage: PropertyUsage,
83    rpc_mode: RpcMode,
84    class_builder: &'a ClassBuilder<C>,
85}
86
87impl<'a, C, T> PropertyBuilder<'a, C, T, InvalidSetter<'a>, InvalidGetter<'a>>
88where
89    C: NativeClass,
90    T: Export,
91{
92    /// Creates a new `PropertyBuilder` with the given property name.
93    #[inline]
94    pub(super) fn new(class_builder: &'a ClassBuilder<C>, name: &'a str) -> Self {
95        PropertyBuilder {
96            name,
97            setter: InvalidSetter::new(name),
98            getter: InvalidGetter::new(name),
99            default: None,
100            hint: None,
101            usage: PropertyUsage::DEFAULT,
102            rpc_mode: RpcMode::Disabled,
103            class_builder,
104        }
105    }
106}
107
108impl<'a, C, T, S, G> PropertyBuilder<'a, C, T, S, G>
109where
110    C: NativeClass,
111    T: Export,
112    S: RawSetter<C, T>,
113    G: RawGetter<C, T>,
114{
115    /// Register the property built with this builder.
116    #[inline]
117    pub fn done(self) {
118        let ExportInfo {
119            variant_type,
120            hint_kind,
121            hint_string,
122        } = T::export_info(self.hint);
123        let default = self.default.to_variant();
124
125        let mut attr = sys::godot_property_attributes {
126            rset_type: self.rpc_mode.sys(),
127            type_: variant_type as sys::godot_int,
128            hint: hint_kind,
129            hint_string: hint_string.to_sys(),
130            usage: self.usage.to_sys(),
131            default_value: default.to_sys(),
132        };
133
134        let path = ::std::ffi::CString::new(self.name).unwrap();
135
136        let set = unsafe { self.setter.into_godot_function() };
137        let get = unsafe { self.getter.into_godot_function() };
138
139        unsafe {
140            (get_api().godot_nativescript_register_property)(
141                self.class_builder.init_handle,
142                self.class_builder.class_name.as_ptr(),
143                path.as_ptr() as *const _,
144                &mut attr,
145                set,
146                get,
147            );
148        }
149    }
150
151    /// Provides a setter function with the signature `fn(&mut C, owner: C::Base, value: T)`
152    /// where `C` is the `NativeClass` type being registered and `T` is the type of the property.
153    #[inline]
154    pub fn with_setter<NS>(
155        self,
156        setter: NS,
157    ) -> PropertyBuilder<'a, C, T, Setter<accessor::Mut, NS>, G>
158    where
159        Setter<accessor::Mut, NS>: RawSetter<C, T>,
160    {
161        PropertyBuilder {
162            name: self.name,
163            setter: Setter::new(setter),
164            getter: self.getter,
165            default: self.default,
166            hint: self.hint,
167            usage: self.usage,
168            rpc_mode: self.rpc_mode,
169            class_builder: self.class_builder,
170        }
171    }
172
173    /// Provides a setter function with the signature `fn(&C, owner: C::Base, value: T)`
174    /// where `C` is the `NativeClass` type being registered and `T` is the type of the property.
175    ///
176    /// "shr" stands for "shared reference", as opposed to the more common `&mut self`.
177    #[inline]
178    pub fn with_shr_setter<NS>(
179        self,
180        setter: NS,
181    ) -> PropertyBuilder<'a, C, T, Setter<accessor::Shr, NS>, G>
182    where
183        Setter<accessor::Shr, NS>: RawSetter<C, T>,
184    {
185        PropertyBuilder {
186            name: self.name,
187            setter: Setter::new(setter),
188            getter: self.getter,
189            default: self.default,
190            hint: self.hint,
191            usage: self.usage,
192            rpc_mode: self.rpc_mode,
193            class_builder: self.class_builder,
194        }
195    }
196
197    /// Provides a getter function with the signature `fn(&C, owner: C::Base) -> T`,
198    /// where `C` is the `NativeClass` type being registered and `T` is the type of the property.
199    #[inline]
200    pub fn with_getter<NG>(
201        self,
202        getter: NG,
203    ) -> PropertyBuilder<'a, C, T, S, Getter<accessor::Shr, accessor::Owned, NG>>
204    where
205        Getter<accessor::Shr, accessor::Owned, NG>: RawGetter<C, T>,
206    {
207        PropertyBuilder {
208            name: self.name,
209            setter: self.setter,
210            getter: Getter::new(getter),
211            default: self.default,
212            hint: self.hint,
213            usage: self.usage,
214            rpc_mode: self.rpc_mode,
215            class_builder: self.class_builder,
216        }
217    }
218
219    /// Provides a getter function with the signature `fn(&C, owner: C::Base) -> &T`,
220    /// where `C` is the `NativeClass` type being registered and `T` is the type of the property.
221    #[inline]
222    pub fn with_ref_getter<NG>(
223        self,
224        getter: NG,
225    ) -> PropertyBuilder<'a, C, T, S, Getter<accessor::Shr, accessor::Ref, NG>>
226    where
227        Getter<accessor::Shr, accessor::Ref, NG>: RawGetter<C, T>,
228    {
229        PropertyBuilder {
230            name: self.name,
231            setter: self.setter,
232            getter: Getter::new(getter),
233            default: self.default,
234            hint: self.hint,
235            usage: self.usage,
236            rpc_mode: self.rpc_mode,
237            class_builder: self.class_builder,
238        }
239    }
240
241    /// Provides a getter function with the signature `fn(&mut C, owner: C::Base) -> T`,
242    /// where `C` is the `NativeClass` type being registered and `T` is the type of the property.
243    #[inline]
244    pub fn with_mut_getter<NG>(
245        self,
246        getter: NG,
247    ) -> PropertyBuilder<'a, C, T, S, Getter<accessor::Mut, accessor::Owned, NG>>
248    where
249        Getter<accessor::Mut, accessor::Owned, NG>: RawGetter<C, T>,
250    {
251        PropertyBuilder {
252            name: self.name,
253            setter: self.setter,
254            getter: Getter::new(getter),
255            default: self.default,
256            hint: self.hint,
257            usage: self.usage,
258            rpc_mode: self.rpc_mode,
259            class_builder: self.class_builder,
260        }
261    }
262
263    /// Provides a getter function with the signature `fn(&mut C, owner: C::Base) -> &T`,
264    /// where `C` is the `NativeClass` type being registered and `T` is the type of the property.
265    #[inline]
266    pub fn with_mut_ref_getter<NG>(
267        self,
268        getter: NG,
269    ) -> PropertyBuilder<'a, C, T, S, Getter<accessor::Mut, accessor::Ref, NG>>
270    where
271        Getter<accessor::Mut, accessor::Ref, NG>: RawGetter<C, T>,
272    {
273        PropertyBuilder {
274            name: self.name,
275            setter: self.setter,
276            getter: Getter::new(getter),
277            default: self.default,
278            hint: self.hint,
279            usage: self.usage,
280            rpc_mode: self.rpc_mode,
281            class_builder: self.class_builder,
282        }
283    }
284
285    /// Sets a default value for the property as a hint to the editor. The setter may or may not
286    /// be actually called with this value.
287    #[inline]
288    pub fn with_default(mut self, default: T) -> Self {
289        self.default = Some(default);
290        self
291    }
292
293    /// Sets an editor hint.
294    #[inline]
295    pub fn with_hint(mut self, hint: T::Hint) -> Self {
296        self.hint = Some(hint);
297        self
298    }
299
300    /// Sets a property usage.
301    #[inline]
302    pub fn with_usage(mut self, usage: PropertyUsage) -> Self {
303        self.usage = usage;
304        self
305    }
306
307    /// Sets a RPC mode.
308    #[inline]
309    pub fn with_rpc_mode(mut self, rpc_mode: RpcMode) -> Self {
310        self.rpc_mode = rpc_mode;
311        self
312    }
313}
314
315bitflags::bitflags! {
316    pub struct PropertyUsage: u32 {
317        const STORAGE = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_STORAGE as u32;
318        const EDITOR = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_EDITOR as u32;
319        const NETWORK = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_NETWORK as u32;
320        const EDITOR_HELPER = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_EDITOR_HELPER as u32;
321        const CHECKABLE = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_CHECKABLE as u32;
322        const CHECKED = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_CHECKED as u32;
323        const INTERNATIONALIZED = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_INTERNATIONALIZED as u32;
324        const GROUP = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_GROUP as u32;
325        const CATEGORY = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_CATEGORY as u32;
326        const STORE_IF_NONZERO = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_STORE_IF_NONZERO as u32;
327        const STORE_IF_NONONE = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_STORE_IF_NONONE as u32;
328        const NO_INSTANCE_STATE = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_NO_INSTANCE_STATE as u32;
329        const RESTART_IF_CHANGED = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_RESTART_IF_CHANGED as u32;
330        const SCRIPT_VARIABLE  = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_SCRIPT_VARIABLE as u32;
331        const STORE_IF_NULL = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_STORE_IF_NULL as u32;
332        const ANIMATE_AS_TRIGGER = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_ANIMATE_AS_TRIGGER as u32;
333        const UPDATE_ALL_IF_MODIFIED = sys::godot_property_usage_flags_GODOT_PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED as u32;
334
335        const DEFAULT = Self::STORAGE.bits | Self::EDITOR.bits | Self::NETWORK.bits as u32;
336        const DEFAULT_INTL = Self::DEFAULT.bits | Self::INTERNATIONALIZED.bits as u32;
337        const NOEDITOR = Self::STORAGE.bits | Self::NETWORK.bits as u32;
338    }
339}
340
341impl PropertyUsage {
342    #[inline]
343    pub fn to_sys(self) -> sys::godot_property_usage_flags {
344        self.bits() as sys::godot_property_usage_flags
345    }
346}
347
348/// Placeholder type for exported properties with no backing field.
349///
350/// This is the go-to type whenever you want to expose a getter/setter to GDScript, which
351/// does not directly map to a field in your struct. Instead of adding a useless field
352/// of the corresponding type (which needs initialization, extra space, etc.), you can use
353/// an instance of this type as a placeholder.
354///
355/// `Property` is a zero-sized type (ZST) which has exactly one value: `Property::default()`.
356/// It implements most of the basic traits, which allows its enclosing struct to remain
357/// composable and derive those traits itself.
358///
359/// ## When to use `Property<T>` instead of `T`
360///
361/// The following table shows which combinations of `#[property]` attributes and field types are allowed.
362/// In this context, `get` and `set` behave symmetrically, so only one of the combinations is listed.
363/// Furthermore, `get_ref` can be used in place of `get`, when it appears with a path.
364///
365/// Field type ➡ <br> Attributes ⬇           | bare `T`                      | `Property<T>`
366/// ------------------------------------------|-------------------------------|-----------------------------
367/// `#[property]`                             | ✔️ default get + set       | ❌️
368/// `#[property(get, set)]` _(same as above)_ | ✔️ default get + set       | ❌️
369/// `#[property(get)]`                        | ✔️ default get (no set)    | ❌️
370/// `#[property(get="path")]`                 | ⚠️ custom get (no set)     | ✔️ custom get (no set)
371/// `#[property(get="path", set)]`            | ✔️ custom get, default set | ❌️
372/// `#[property(get="path", set="path")]`     | ⚠️ custom get + set        | ✔️ custom get + set
373///
374/// "⚠️" means that this attribute combination is allowed for bare `T`, but you should consider
375/// using `Property<T>`.
376///
377/// Since there is no default `get` or `set` in these cases, godot-rust will never access the field
378/// directly. In other words, you are not really exporting _that field_, but linking its name and type
379/// (but not its value) to the specified get/set methods.
380///
381/// To decide when to use which:
382/// * If you access your field as-is on the Rust side, use bare `T`.<br>
383///   With a `Property<T>` field on the other hand, you would need to _additionally_ add a `T` backing field.
384/// * If you don't need a backing field, use `Property<T>`.<br>
385///   This is the case whenever you compute a result dynamically, or map values between Rust and GDScript
386///   representations.
387///
388/// ## Examples
389///
390/// Read/write accessible:
391/// ```no_run
392/// # use gdnative::prelude::*;
393/// #[derive(NativeClass)]
394/// # #[no_constructor]
395/// struct MyObject {
396///     #[property]
397///     color: Color,
398/// }
399/// ```
400///
401/// Read-only:
402/// ```no_run
403/// # use gdnative::prelude::*;
404/// #[derive(NativeClass)]
405/// # #[no_constructor]
406/// struct MyObject {
407///     #[property(get)]
408///     hitpoints: f32,
409/// }
410/// ```
411///
412/// Read-write, with validating setter:
413/// ```no_run
414/// # use gdnative::prelude::*;
415/// # fn validate(s: &String) -> bool { true }
416/// #[derive(NativeClass)]
417/// # #[no_constructor]
418/// struct MyObject {
419///     #[property(get, set = "Self::set_name")]
420///     player_name: String,
421/// }
422///
423/// #[methods]
424/// impl MyObject {
425///     fn set_name(&mut self, _owner: TRef<Reference>, name: String) {
426///         if validate(&name) {
427///             self.player_name = name;
428///         }
429///     }
430/// }
431/// ```
432///
433/// Write-only, no backing field, custom setter:
434/// ```no_run
435/// # use gdnative::prelude::*;
436/// #[derive(NativeClass)]
437/// # #[no_constructor]
438/// struct MyObject {
439///     #[property(set = "Self::set_password")]
440///     password: Property<String>,
441/// }
442///
443/// #[methods]
444/// impl MyObject {
445///     fn set_password(&mut self, _owner: TRef<Reference>, password: String) {
446///         // securely hash and store password
447///     }
448/// }
449/// ```
450
451// Note: traits are mostly implemented to enable deriving the same traits on the enclosing struct.
452#[derive(Copy, Clone, Debug, Default, Ord, PartialOrd, Eq, PartialEq, Hash)]
453pub struct Property<T> {
454    _marker: PhantomData<T>,
455}
456
457mod impl_export {
458    use std::collections::{HashMap, HashSet};
459
460    use super::*;
461
462    /// Hint type indicating that there are no hints available for the time being.
463    ///
464    /// This is an inhabitable type that cannot be constructed. As a result, users
465    /// *must* use `None` for the property hint when exporting types using this as
466    /// the hint.
467    pub enum NoHint {}
468
469    macro_rules! impl_export_for_int {
470        ($ty:ident) => {
471            impl Export for $ty {
472                type Hint = hint::IntHint<$ty>;
473                #[inline]
474                fn export_info(hint: Option<Self::Hint>) -> ExportInfo {
475                    hint.map_or_else(
476                        || ExportInfo::new(VariantType::I64),
477                        Self::Hint::export_info,
478                    )
479                }
480            }
481        };
482    }
483
484    impl_export_for_int!(i8);
485    impl_export_for_int!(i16);
486    impl_export_for_int!(i32);
487    impl_export_for_int!(i64);
488    impl_export_for_int!(u8);
489    impl_export_for_int!(u16);
490    impl_export_for_int!(u32);
491    impl_export_for_int!(u64);
492
493    macro_rules! impl_export_for_float {
494        ($ty:ident) => {
495            impl Export for $ty {
496                type Hint = hint::FloatHint<$ty>;
497                #[inline]
498                fn export_info(hint: Option<Self::Hint>) -> ExportInfo {
499                    hint.map_or_else(
500                        || ExportInfo::new(VariantType::F64),
501                        Self::Hint::export_info,
502                    )
503                }
504            }
505        };
506    }
507
508    impl_export_for_float!(f32);
509    impl_export_for_float!(f64);
510
511    macro_rules! impl_export_for_string {
512        ($ty:ty) => {
513            impl Export for $ty {
514                type Hint = hint::StringHint;
515                #[inline]
516                fn export_info(hint: Option<Self::Hint>) -> ExportInfo {
517                    hint.map_or_else(
518                        || ExportInfo::new(VariantType::GodotString),
519                        Self::Hint::export_info,
520                    )
521                }
522            }
523        };
524    }
525
526    impl_export_for_string!(GodotString);
527    impl_export_for_string!(String);
528
529    macro_rules! impl_export_for_core_type_without_hint {
530        ($ty:ty: $variant_ty:ident) => {
531            impl Export for $ty {
532                type Hint = NoHint;
533                #[inline]
534                fn export_info(_hint: Option<Self::Hint>) -> ExportInfo {
535                    ExportInfo::new(VariantType::$variant_ty)
536                }
537            }
538        };
539        ($ty:ident) => {
540            impl_export_for_core_type_without_hint!($ty: $ty);
541        };
542    }
543
544    impl_export_for_core_type_without_hint!(bool: Bool);
545    impl_export_for_core_type_without_hint!(Vector2);
546    impl_export_for_core_type_without_hint!(Rect2);
547    impl_export_for_core_type_without_hint!(Vector3);
548    impl_export_for_core_type_without_hint!(Transform2D);
549    impl_export_for_core_type_without_hint!(Plane);
550    impl_export_for_core_type_without_hint!(Quat);
551    impl_export_for_core_type_without_hint!(Aabb);
552    impl_export_for_core_type_without_hint!(Basis);
553    impl_export_for_core_type_without_hint!(Transform);
554    impl_export_for_core_type_without_hint!(NodePath);
555    impl_export_for_core_type_without_hint!(Rid);
556    impl_export_for_core_type_without_hint!(Dictionary);
557    impl_export_for_core_type_without_hint!(PoolArray<u8>: ByteArray);
558    impl_export_for_core_type_without_hint!(PoolArray<i32>: Int32Array);
559    impl_export_for_core_type_without_hint!(PoolArray<f32>: Float32Array);
560    impl_export_for_core_type_without_hint!(PoolArray<GodotString>: StringArray);
561    impl_export_for_core_type_without_hint!(PoolArray<Vector2>: Vector2Array);
562    impl_export_for_core_type_without_hint!(PoolArray<Vector3>: Vector3Array);
563    impl_export_for_core_type_without_hint!(PoolArray<Color>: ColorArray);
564
565    impl Export for Color {
566        type Hint = hint::ColorHint;
567        #[inline]
568        fn export_info(hint: Option<Self::Hint>) -> ExportInfo {
569            hint.map_or_else(
570                || ExportInfo::new(VariantType::Color),
571                Self::Hint::export_info,
572            )
573        }
574    }
575
576    impl<T> Export for Ref<T, Shared>
577    where
578        T: GodotObject,
579    {
580        type Hint = NoHint;
581        #[inline]
582        fn export_info(_hint: Option<Self::Hint>) -> ExportInfo {
583            ExportInfo::resource_type::<T>()
584        }
585    }
586
587    impl<T> Export for Instance<T, Shared>
588    where
589        T: NativeClass,
590        Instance<T, Shared>: ToVariant,
591    {
592        type Hint = NoHint;
593        #[inline]
594        fn export_info(_hint: Option<Self::Hint>) -> ExportInfo {
595            ExportInfo::resource_type::<T::Base>()
596        }
597    }
598
599    impl<T> Export for Option<T>
600    where
601        T: Export,
602    {
603        type Hint = T::Hint;
604        #[inline]
605        fn export_info(hint: Option<Self::Hint>) -> ExportInfo {
606            T::export_info(hint)
607        }
608    }
609
610    impl Export for VariantArray<Shared> {
611        type Hint = hint::ArrayHint;
612
613        #[inline]
614        fn export_info(hint: Option<Self::Hint>) -> ExportInfo {
615            hint.unwrap_or_default().export_info()
616        }
617    }
618
619    impl<K, V> Export for HashMap<K, V>
620    where
621        K: std::hash::Hash + ToVariantEq + ToVariant,
622        V: ToVariant,
623    {
624        type Hint = NoHint;
625
626        #[inline]
627        fn export_info(_hint: Option<Self::Hint>) -> ExportInfo {
628            ExportInfo::new(VariantType::Dictionary)
629        }
630    }
631
632    impl<T> Export for HashSet<T>
633    where
634        T: ToVariant,
635    {
636        type Hint = NoHint;
637
638        #[inline]
639        fn export_info(_hint: Option<Self::Hint>) -> ExportInfo {
640            ExportInfo::new(VariantType::VariantArray)
641        }
642    }
643
644    impl<T> Export for Vec<T>
645    where
646        T: ToVariant,
647    {
648        type Hint = NoHint;
649
650        #[inline]
651        fn export_info(_hint: Option<Self::Hint>) -> ExportInfo {
652            ExportInfo::new(VariantType::VariantArray)
653        }
654    }
655}