Skip to content

Commit 6274bd8

Browse files
committed
Allow storing Option<DynGd<T>> in arrays
1 parent d8069a0 commit 6274bd8

File tree

4 files changed

+109
-7
lines changed

4 files changed

+109
-7
lines changed

godot-core/src/meta/args/as_arg.rs

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ where
143143
T: Inherits<Base>,
144144
Base: GodotClass,
145145
{
146+
//noinspection RsConstantConditionIf - false positive in IDE for `T::IS_SAME_CLASS`.
146147
fn into_arg<'arg>(self) -> CowArg<'arg, Gd<Base>>
147148
where
148149
Self: 'arg,
@@ -199,7 +200,7 @@ where
199200
}
200201
}
201202

202-
// Convert `DynGd` -> `Gd` (with upcast).
203+
/// Convert `DynGd` -> `Gd` (with upcast).
203204
impl<T, D, Base> AsArg<Gd<Base>> for &DynGd<T, D>
204205
where
205206
T: Inherits<Base>,
@@ -226,6 +227,29 @@ where
226227
// ----------------------------------------------------------------------------------------------------------------------------------------------
227228
// Optional object (Gd + DynGd) impls
228229

230+
/// Convert `&Gd` -> `Option<Gd>` (with upcast).
231+
impl<T, Base> AsArg<Option<Gd<Base>>> for &Gd<T>
232+
where
233+
T: Inherits<Base>,
234+
Base: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
235+
{
236+
fn into_arg<'arg>(self) -> CowArg<'arg, Option<Gd<Base>>>
237+
where
238+
Self: 'arg,
239+
{
240+
// Upcasting to an owned value Gd<Base> requires cloning. Optimized path in into_ffi_arg().
241+
CowArg::Owned(Some(self.clone().upcast::<Base>()))
242+
}
243+
244+
fn into_ffi_arg<'arg>(self) -> FfiArg<'arg, Option<Gd<Base>>>
245+
where
246+
Self: 'arg,
247+
{
248+
let arg = ObjectArg::from_gd(self);
249+
FfiArg::FfiObject(arg)
250+
}
251+
}
252+
229253
/// Convert `Option<&Gd>` -> `Option<Gd>` (with upcast).
230254
impl<T, Base> AsArg<Option<Gd<Base>>> for Option<&Gd<T>>
231255
where
@@ -252,21 +276,22 @@ where
252276
}
253277
}
254278

255-
/// Convert `&Gd` -> `Option<Gd>` (with upcast).
256-
impl<T, Base> AsArg<Option<Gd<Base>>> for &Gd<T>
279+
/// Convert `&DynGd` -> `Option<DynGd>` (with upcast).
280+
impl<T, D, Base> AsArg<Option<DynGd<Base, D>>> for &DynGd<T, D>
257281
where
258282
T: Inherits<Base>,
283+
D: ?Sized,
259284
Base: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
260285
{
261-
fn into_arg<'arg>(self) -> CowArg<'arg, Option<Gd<Base>>>
286+
fn into_arg<'arg>(self) -> CowArg<'arg, Option<DynGd<Base, D>>>
262287
where
263288
Self: 'arg,
264289
{
265-
// Upcasting to an owned value Gd<Base> requires cloning. Optimized path in into_ffi_arg().
266-
CowArg::Owned(Some(self.clone().upcast::<Base>()))
290+
// Upcasting to an owned value DynGd<Base, D> requires cloning. Optimized path in into_ffi_arg().
291+
CowArg::Owned(Some(self.clone().upcast()))
267292
}
268293

269-
fn into_ffi_arg<'arg>(self) -> FfiArg<'arg, Option<Gd<Base>>>
294+
fn into_ffi_arg<'arg>(self) -> FfiArg<'arg, Option<DynGd<Base, D>>>
270295
where
271296
Self: 'arg,
272297
{
@@ -299,6 +324,34 @@ where
299324
}
300325
}
301326

327+
/// Convert `Option<&DynGd>` -> `Option<DynGd>` (with upcast).
328+
impl<T, D, Base> AsArg<Option<DynGd<Base, D>>> for Option<&DynGd<T, D>>
329+
where
330+
T: Inherits<Base>,
331+
D: ?Sized,
332+
Base: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
333+
{
334+
fn into_arg<'arg>(self) -> CowArg<'arg, Option<DynGd<Base, D>>>
335+
where
336+
Self: 'arg,
337+
{
338+
// Upcasting to an owned value Gd<Base> requires cloning. Optimized path in into_ffi_arg().
339+
match self {
340+
Some(gd_ref) => AsArg::into_arg(gd_ref),
341+
None => CowArg::Owned(None),
342+
}
343+
}
344+
345+
fn into_ffi_arg<'arg>(self) -> FfiArg<'arg, Option<DynGd<Base, D>>>
346+
where
347+
Self: 'arg,
348+
{
349+
let option_gd: Option<&Gd<T>> = self.map(|v| &**v); // as_deref() not working.
350+
let arg = ObjectArg::from_option_gd(option_gd);
351+
FfiArg::FfiObject(arg)
352+
}
353+
}
354+
302355
// ----------------------------------------------------------------------------------------------------------------------------------------------
303356
// Public helper functions (T|&T -> AsArg)
304357

godot-core/src/meta/sealed.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ impl<T: ArrayElement> Sealed for Array<T> {}
6262
impl<T: GodotClass> Sealed for Gd<T> {}
6363
impl<T: GodotClass> Sealed for RawGd<T> {}
6464
impl<T: GodotClass, D: ?Sized> Sealed for DynGd<T, D> {}
65+
impl<T: GodotClass, D: ?Sized + 'static> Sealed for Option<DynGd<T, D>> {}
6566
impl<T> Sealed for Option<T>
6667
where
6768
T: GodotType,

godot-core/src/obj/dyn_gd.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,16 @@ where
606606
}
607607
}
608608

609+
impl<T, D> meta::ArrayElement for Option<DynGd<T, D>>
610+
where
611+
T: GodotClass,
612+
D: ?Sized + 'static,
613+
{
614+
fn element_type_string() -> String {
615+
DynGd::<T, D>::element_type_string()
616+
}
617+
}
618+
609619
impl<T, D> Var for DynGd<T, D>
610620
where
611621
T: GodotClass,

itest/rust/src/object_tests/dyn_gd_test.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,44 @@ fn dyn_gd_store_in_godot_array() {
430430
*/
431431
}
432432

433+
#[itest]
434+
fn dyn_gd_as_arg() {
435+
let refc_health = Gd::from_object(RefcHealth { hp: 42 }).into_dyn();
436+
let node_health = foreign::NodeHealth::new_alloc().into_dyn();
437+
let typed_none = None::<&DynGd<RefcHealth, dyn Health>>;
438+
439+
// Array<DynGd>.
440+
let array: Array<DynGd<Object, dyn Health>> = array![&refc_health, &node_health];
441+
assert_eq!(array.len(), 2);
442+
443+
let first = array.at(0);
444+
assert_eq!(first.dyn_bind().get_hitpoints(), 42);
445+
446+
let second = array.at(1);
447+
assert_eq!(second.dyn_bind().get_hitpoints(), 100);
448+
449+
// Array<Option<DynGd>>.
450+
let opt_array: Array<Option<DynGd<Object, dyn Health>>> = array![
451+
Some(&refc_health),
452+
Some(&node_health),
453+
typed_none,
454+
// Gd::null_arg(),
455+
];
456+
assert_eq!(opt_array.len(), 3);
457+
458+
let first = opt_array.at(0).expect("element 0 is Some");
459+
assert_eq!(first.dyn_bind().get_hitpoints(), 42);
460+
461+
let second = opt_array.at(1).expect("element 1 is Some");
462+
assert_eq!(second.dyn_bind().get_hitpoints(), 100);
463+
464+
let third = opt_array.at(2);
465+
assert!(third.is_none(), "element 2 is None");
466+
467+
// Clean up manually managed objects.
468+
opt_array.at(1).unwrap().free();
469+
}
470+
433471
#[itest]
434472
fn dyn_gd_error_unregistered_trait() {
435473
trait UnrelatedTrait {}

0 commit comments

Comments
 (0)