1#![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
25pub trait Export: crate::core_types::ToVariant {
27 type Hint;
35
36 fn export_info(hint: Option<Self::Hint>) -> ExportInfo;
38}
39
40#[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 #[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 #[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#[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[inline]
288 pub fn with_default(mut self, default: T) -> Self {
289 self.default = Some(default);
290 self
291 }
292
293 #[inline]
295 pub fn with_hint(mut self, hint: T::Hint) -> Self {
296 self.hint = Some(hint);
297 self
298 }
299
300 #[inline]
302 pub fn with_usage(mut self, usage: PropertyUsage) -> Self {
303 self.usage = usage;
304 self
305 }
306
307 #[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#[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 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}