|
1 | 1 | //! Property registration.
|
| 2 | +use std::marker::PhantomData; |
2 | 3 |
|
3 | 4 | use accessor::{Getter, RawGetter, RawSetter, Setter};
|
4 | 5 | use invalid_accessor::{InvalidGetter, InvalidSetter};
|
@@ -322,6 +323,115 @@ impl PropertyUsage {
|
322 | 323 | }
|
323 | 324 | }
|
324 | 325 |
|
| 326 | +/// Placeholder type for exported properties with no backing field. |
| 327 | +/// |
| 328 | +/// This is the go-to type whenever you want to expose a getter/setter to GDScript, which |
| 329 | +/// does not directly map to a field in your struct. Instead of adding a useless field |
| 330 | +/// of the corresponding type (which needs initialization, extra space, etc.), you can use |
| 331 | +/// an instance of this type as a placeholder. |
| 332 | +/// |
| 333 | +/// `Property` is a zero-sized type (ZST) which has exactly one value: `Property::default()`. |
| 334 | +/// It implements most of the basic traits, which allows its enclosing struct to remain |
| 335 | +/// composable and derive those traits itself. |
| 336 | +/// |
| 337 | +/// ## When to use `Property<T>` instead of `T` |
| 338 | +/// |
| 339 | +/// The following table shows which combinations of `#[property]` attributes and field types are allowed. |
| 340 | +/// In this context, `get` and `set` behave symmetrically, so only one of the combinations is listed. |
| 341 | +/// Furthermore, `get_ref` can be used in place of `get`, when it appears with a path. |
| 342 | +/// |
| 343 | +/// Field type ➡ <br> Attributes ⬇ | bare `T` | `Property<T>` |
| 344 | +/// ------------------------------------------|-------------------------------|----------------------------- |
| 345 | +/// `#[property]` | ✔️ default get + set | ❌️ |
| 346 | +/// `#[property(get, set)]` _(same as above)_ | ✔️ default get + set | ❌️ |
| 347 | +/// `#[property(get)]` | ✔️ default get (no set) | ❌️ |
| 348 | +/// `#[property(get="path")]` | ⚠️ custom get (no set) | ✔️ custom get (no set) |
| 349 | +/// `#[property(get="path", set)]` | ✔️ custom get, default set | ❌️ |
| 350 | +/// `#[property(get="path", set="path")]` | ⚠️ custom get + set | ✔️ custom get + set |
| 351 | +/// |
| 352 | +/// "⚠️" means that this attribute combination is allowed for bare `T`, but you should consider |
| 353 | +/// using `Property<T>`. |
| 354 | +/// |
| 355 | +/// Since there is no default `get` or `set` in these cases, godot-rust will never access the field |
| 356 | +/// directly. In other words, you are not really exporting _that field_, but linking its name and type |
| 357 | +/// (but not its value) to the specified get/set methods. |
| 358 | +/// |
| 359 | +/// To decide when to use which: |
| 360 | +/// * If you access your field as-is on the Rust side, use bare `T`.<br> |
| 361 | +/// With a `Property<T>` field on the other hand, you would need to _additionally_ add a `T` backing field. |
| 362 | +/// * If you don't need a backing field, use `Property<T>`.<br> |
| 363 | +/// This is the case whenever you compute a result dynamically, or map values between Rust and GDScript |
| 364 | +/// representations. |
| 365 | +/// |
| 366 | +/// ## Examples |
| 367 | +/// |
| 368 | +/// Read/write accessible: |
| 369 | +/// ```no_run |
| 370 | +/// # use gdnative::prelude::*; |
| 371 | +/// #[derive(NativeClass)] |
| 372 | +/// # #[no_constructor] |
| 373 | +/// struct MyObject { |
| 374 | +/// #[property] |
| 375 | +/// color: Color, |
| 376 | +/// } |
| 377 | +/// ``` |
| 378 | +/// |
| 379 | +/// Read-only: |
| 380 | +/// ```no_run |
| 381 | +/// # use gdnative::prelude::*; |
| 382 | +/// #[derive(NativeClass)] |
| 383 | +/// # #[no_constructor] |
| 384 | +/// struct MyObject { |
| 385 | +/// #[property(get)] |
| 386 | +/// hitpoints: f32, |
| 387 | +/// } |
| 388 | +/// ``` |
| 389 | +/// |
| 390 | +/// Read-write, with validating setter: |
| 391 | +/// ```no_run |
| 392 | +/// # use gdnative::prelude::*; |
| 393 | +/// # fn validate(s: &String) -> bool { true } |
| 394 | +/// #[derive(NativeClass)] |
| 395 | +/// # #[no_constructor] |
| 396 | +/// struct MyObject { |
| 397 | +/// #[property(get, set = "Self::set_name")] |
| 398 | +/// player_name: String, |
| 399 | +/// } |
| 400 | +/// |
| 401 | +/// #[methods] |
| 402 | +/// impl MyObject { |
| 403 | +/// fn set_name(&mut self, _owner: TRef<Reference>, name: String) { |
| 404 | +/// if validate(&name) { |
| 405 | +/// self.player_name = name; |
| 406 | +/// } |
| 407 | +/// } |
| 408 | +/// } |
| 409 | +/// ``` |
| 410 | +/// |
| 411 | +/// Write-only, no backing field, custom setter: |
| 412 | +/// ```no_run |
| 413 | +/// # use gdnative::prelude::*; |
| 414 | +/// #[derive(NativeClass)] |
| 415 | +/// # #[no_constructor] |
| 416 | +/// struct MyObject { |
| 417 | +/// #[property(set = "Self::set_password")] |
| 418 | +/// password: Property<String>, |
| 419 | +/// } |
| 420 | +/// |
| 421 | +/// #[methods] |
| 422 | +/// impl MyObject { |
| 423 | +/// fn set_password(&mut self, _owner: TRef<Reference>, password: String) { |
| 424 | +/// // securely hash and store password |
| 425 | +/// } |
| 426 | +/// } |
| 427 | +/// ``` |
| 428 | +
|
| 429 | +// Note: traits are mostly implemented to enable deriving the same traits on the enclosing struct. |
| 430 | +#[derive(Copy, Clone, Debug, Default, Ord, PartialOrd, Eq, PartialEq, Hash)] |
| 431 | +pub struct Property<T> { |
| 432 | + _marker: PhantomData<T>, |
| 433 | +} |
| 434 | + |
325 | 435 | mod impl_export {
|
326 | 436 | use super::*;
|
327 | 437 |
|
|
0 commit comments