|
| 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 | + |
| 8 | +use crate::obj::guards::DynGdRef; |
| 9 | +use crate::obj::{bounds, AsDyn, Bounds, DynGdMut, Gd, GodotClass, Inherits}; |
| 10 | +use std::ops; |
| 11 | + |
| 12 | +/// Smart pointer integrating Rust traits via `dyn` dispatch. |
| 13 | +/// |
| 14 | +/// `DynGd<T, D>` extends a Godot object [`Gd<T>`] with functionality for Rust's trait dynamic dispatch. \ |
| 15 | +/// In this context, the type parameters have the following meaning: |
| 16 | +/// - `T` is the Godot class. |
| 17 | +/// - `D` is a trait object `dyn Trait`, where `T: Trait`. |
| 18 | +/// |
| 19 | +/// To register the `T` -> `D` relation with godot-rust, `T` must implement [`AsDyn<D>`]. This can be automated with the |
| 20 | +/// [`#[godot_dyn]`](../register/attr.godot_dyn.html) attribute macro. |
| 21 | +/// |
| 22 | +/// # Construction and API |
| 23 | +/// You can convert between `Gd` and `DynGd` using [`Gd::into_dyn()`] and [`DynGd::into_gd()`]. The former sometimes needs an explicit |
| 24 | +/// `::<dyn Trait>` type argument, but can often be inferred. |
| 25 | +/// |
| 26 | +/// The `DynGd` API is very close to `Gd`. In fact, both `Deref` and `DerefMut` are implemented for `DynGd` -> `Gd`, so you can access all the |
| 27 | +/// underlying `Gd` methods as well as Godot class APIs directly. |
| 28 | +/// |
| 29 | +/// The main new parts are two methods [`dyn_bind()`][Self::dyn_bind] and [`dyn_bind_mut()`][Self::dyn_bind_mut]. These are very similar to `Gd`'s |
| 30 | +/// [`bind()`][Gd::bind] and [`bind_mut()`][Gd::bind_mut], but return a reference guard to the trait object `D` instead of the Godot class `T`. |
| 31 | +/// |
| 32 | +/// # Example |
| 33 | +/// |
| 34 | +/// ```no_run |
| 35 | +/// use godot::obj::{Gd, DynGd,NewGd}; |
| 36 | +/// use godot::register::{godot_dyn, GodotClass}; |
| 37 | +/// use godot::classes::RefCounted; |
| 38 | +/// |
| 39 | +/// #[derive(GodotClass)] |
| 40 | +/// #[class(init)] |
| 41 | +/// struct Monster { |
| 42 | +/// #[init(val = 100)] |
| 43 | +/// hitpoints: u16, |
| 44 | +/// } |
| 45 | +/// |
| 46 | +/// trait Health { |
| 47 | +/// fn is_alive(&self) -> bool; |
| 48 | +/// fn deal_damage(&mut self, damage: u16); |
| 49 | +/// } |
| 50 | +/// |
| 51 | +/// // The #[godot_dyn] attribute macro registers the dynamic relation in godot-rust. |
| 52 | +/// // Traits are implemented as usual. |
| 53 | +/// #[godot_dyn] |
| 54 | +/// impl Health for Monster { |
| 55 | +/// fn is_alive(&self) -> bool { |
| 56 | +/// self.hitpoints > 0 |
| 57 | +/// } |
| 58 | +/// |
| 59 | +/// fn deal_damage(&mut self, damage: u16) { |
| 60 | +/// self.hitpoints = self.hitpoints.saturating_sub(damage); |
| 61 | +/// } |
| 62 | +/// } |
| 63 | +/// |
| 64 | +/// // Create a Gd<Monster> and convert it -> DynGd<Monster, dyn Health>. |
| 65 | +/// let monster = Monster::new_gd(); |
| 66 | +/// let dyn_monster = monster.into_dyn::<dyn Health>(); |
| 67 | +/// |
| 68 | +/// // Now upcast it to its base class -> type is DynGd<RefCounted, dyn Health>. |
| 69 | +/// let mut dyn_monster = dyn_monster.upcast::<RefCounted>(); |
| 70 | +/// |
| 71 | +/// // Due to RefCounted abstraction, you can no longer access concrete Monster properties. |
| 72 | +/// // However, the trait Health is still accessible through dyn_bind(). |
| 73 | +/// assert!(dyn_monster.dyn_bind().is_alive()); |
| 74 | +/// |
| 75 | +/// // To mutate the object, call dyn_bind_mut(). Rust borrow rules apply. |
| 76 | +/// let mut guard = dyn_monster.dyn_bind_mut(); |
| 77 | +/// guard.deal_damage(120); |
| 78 | +/// assert!(!guard.is_alive()); |
| 79 | +/// ``` |
| 80 | +pub struct DynGd<T, D> |
| 81 | +where |
| 82 | + // T does _not_ require AsDyn<D> here. Otherwise, it's impossible to upcast (without implementing the relation for all base classes). |
| 83 | + T: GodotClass, |
| 84 | + D: ?Sized, |
| 85 | +{ |
| 86 | + // Potential optimizations: use single Gd; use Rc/Arc instead of Box+clone; store a downcast fn from Gd<T>; ... |
| 87 | + obj: Gd<T>, |
| 88 | + erased_obj: Box<dyn ErasedGd<D>>, |
| 89 | +} |
| 90 | + |
| 91 | +impl<T, D> DynGd<T, D> |
| 92 | +where |
| 93 | + T: AsDyn<D> + Bounds<Declarer = bounds::DeclUser>, |
| 94 | + D: ?Sized, |
| 95 | +{ |
| 96 | + pub(crate) fn from_gd(gd_instance: Gd<T>) -> Self { |
| 97 | + let erased_obj = Box::new(gd_instance.clone()); |
| 98 | + |
| 99 | + Self { |
| 100 | + obj: gd_instance, |
| 101 | + erased_obj, |
| 102 | + } |
| 103 | + } |
| 104 | +} |
| 105 | + |
| 106 | +impl<T, D> DynGd<T, D> |
| 107 | +where |
| 108 | + // Again, T deliberately does not require AsDyn<D> here. See above. |
| 109 | + T: GodotClass, |
| 110 | + D: ?Sized, |
| 111 | +{ |
| 112 | + /// Acquires a shared reference guard to the trait object `D`. |
| 113 | + /// |
| 114 | + /// The resulting guard implements `Deref<Target = D>`, allowing shared access to the trait's methods. |
| 115 | + /// |
| 116 | + /// See [`Gd::bind()`][Gd::bind] for borrow checking semantics and panics. |
| 117 | + pub fn dyn_bind(&self) -> DynGdRef<D> { |
| 118 | + self.erased_obj.dyn_bind() |
| 119 | + } |
| 120 | + |
| 121 | + /// Acquires an exclusive reference guard to the trait object `D`. |
| 122 | + /// |
| 123 | + /// The resulting guard implements `DerefMut<Target = D>`, allowing exclusive mutable access to the trait's methods. |
| 124 | + /// |
| 125 | + /// See [`Gd::bind_mut()`][Gd::bind_mut] for borrow checking semantics and panics. |
| 126 | + pub fn dyn_bind_mut(&mut self) -> DynGdMut<D> { |
| 127 | + self.erased_obj.dyn_bind_mut() |
| 128 | + } |
| 129 | + |
| 130 | + // Certain methods "overridden" from deref'ed Gd here, so they're more idiomatic to use. |
| 131 | + // Those taking self by value, like free(), must be overridden. |
| 132 | + |
| 133 | + /// Upcast to a Godot base, while retaining the `D` trait object. |
| 134 | + /// |
| 135 | + /// This is useful when you want to gather multiple objects under a common Godot base (e.g. `Node`), but still enable common functionality. |
| 136 | + /// The common functionality is still accessible through `D` even when upcasting. |
| 137 | + /// |
| 138 | + /// See also [`Gd::upcast()`]. |
| 139 | + pub fn upcast<Base>(self) -> DynGd<Base, D> |
| 140 | + where |
| 141 | + Base: GodotClass, |
| 142 | + T: Inherits<Base>, |
| 143 | + { |
| 144 | + DynGd { |
| 145 | + obj: self.obj.upcast::<Base>(), |
| 146 | + erased_obj: self.erased_obj, |
| 147 | + } |
| 148 | + } |
| 149 | + |
| 150 | + /// Downgrades to a `Gd<T>` pointer, abandoning the `D` abstraction. |
| 151 | + #[must_use] |
| 152 | + pub fn into_gd(self) -> Gd<T> { |
| 153 | + self.obj |
| 154 | + } |
| 155 | +} |
| 156 | + |
| 157 | +impl<T, D> DynGd<T, D> |
| 158 | +where |
| 159 | + T: GodotClass + Bounds<Memory = bounds::MemManual>, |
| 160 | + D: ?Sized, |
| 161 | +{ |
| 162 | + pub fn free(self) { |
| 163 | + self.obj.free() |
| 164 | + } |
| 165 | +} |
| 166 | + |
| 167 | +// Don't derive since that messes with bounds, and `.clone()` may silently fall back to deref'ed `Gd::clone()`. |
| 168 | +impl<T, D> Clone for DynGd<T, D> |
| 169 | +where |
| 170 | + T: GodotClass, |
| 171 | + D: ?Sized, |
| 172 | +{ |
| 173 | + fn clone(&self) -> Self { |
| 174 | + Self { |
| 175 | + obj: self.obj.clone(), |
| 176 | + erased_obj: self.erased_obj.clone_box(), |
| 177 | + } |
| 178 | + } |
| 179 | +} |
| 180 | + |
| 181 | +impl<T, D> ops::Deref for DynGd<T, D> |
| 182 | +where |
| 183 | + T: GodotClass, |
| 184 | + D: ?Sized, |
| 185 | +{ |
| 186 | + type Target = Gd<T>; |
| 187 | + |
| 188 | + fn deref(&self) -> &Self::Target { |
| 189 | + &self.obj |
| 190 | + } |
| 191 | +} |
| 192 | + |
| 193 | +impl<T, D> ops::DerefMut for DynGd<T, D> |
| 194 | +where |
| 195 | + T: GodotClass, |
| 196 | + D: ?Sized, |
| 197 | +{ |
| 198 | + fn deref_mut(&mut self) -> &mut Self::Target { |
| 199 | + &mut self.obj |
| 200 | + } |
| 201 | +} |
| 202 | + |
| 203 | +// ---------------------------------------------------------------------------------------------------------------------------------------------- |
| 204 | +// Type erasure |
| 205 | + |
| 206 | +trait ErasedGd<D: ?Sized> { |
| 207 | + fn dyn_bind(&self) -> DynGdRef<D>; |
| 208 | + fn dyn_bind_mut(&mut self) -> DynGdMut<D>; |
| 209 | + |
| 210 | + fn clone_box(&self) -> Box<dyn ErasedGd<D>>; |
| 211 | +} |
| 212 | + |
| 213 | +impl<T, D> ErasedGd<D> for Gd<T> |
| 214 | +where |
| 215 | + T: AsDyn<D> + Bounds<Declarer = bounds::DeclUser>, |
| 216 | + D: ?Sized, |
| 217 | +{ |
| 218 | + fn dyn_bind(&self) -> DynGdRef<D> { |
| 219 | + DynGdRef::from_guard::<T>(Gd::bind(self)) |
| 220 | + } |
| 221 | + |
| 222 | + fn dyn_bind_mut(&mut self) -> DynGdMut<D> { |
| 223 | + DynGdMut::from_guard::<T>(Gd::bind_mut(self)) |
| 224 | + } |
| 225 | + |
| 226 | + fn clone_box(&self) -> Box<dyn ErasedGd<D>> { |
| 227 | + Box::new(Gd::clone(self)) |
| 228 | + } |
| 229 | +} |
| 230 | + |
| 231 | +// ---------------------------------------------------------------------------------------------------------------------------------------------- |
| 232 | +// Integration with Godot traits |
0 commit comments