Skip to content

Commit 0439f55

Browse files
DiddykongaMrGVSV
andauthored
Reflect DynamicStruct field removal methods (#22702)
# Objective / Solution Add methods for field removal on `DynamicStruct`: ```rust DynamicStruct::remove_at( &mut self, index: usize ) -> Option<(Cow<'static, str>, Box<dyn PartialReflect>)> DynamicStruct::remove_by_name( &mut self, name: &str ) -> Option<(Cow<'static, str>, Box<dyn PartialReflect>)> DynamicStruct::remove_if( &mut self, f: FnMut((&str, &dyn PartialReflect))->bool ) -> Option<(Cow<'static, str>, Box<dyn PartialReflect>)> ``` ## Testing 4 New Tests, located in [bevy_reflect/structs.rs](https://github.com/bevyengine/bevy/blob/aa0c0b966c3463a6f39a5b961be91722b13dfb6a/crates/bevy_reflect/src/structs.rs): `dynamic_struct_remove_at` `dynamic_struct_remove_by_name` `dynamic_struct_remove_with` `dynamic_struct_remove_combo` --- ## Showcase <details> <summary>Click to view showcase</summary> ```rust #[derive(Reflect, Default)] struct MyStruct { a: (), b: (), c: (), } #[test] fn dynamic_struct_remove_combo() { let mut my_struct = MyStruct::default().to_dynamic_struct(); assert_eq!(my_struct.field_len(), 3); let field_2 = my_struct .remove_at( my_struct .index_of( my_struct .field("b") .expect("Invalid name for `my_struct.field(name)`"), ) .expect("Invalid field for `my_struct.index_of(field)`"), ) .expect("Invalid index for `my_struct.remove_at(index)`"); assert_eq!(my_struct.field_len(), 2); assert_eq!(field_2.0, "b"); let field_3_name = my_struct .name_of( my_struct .field_at(1) .expect("Invalid index for `my_struct.field_at(index)`"), ) .expect("Invalid field for `my_struct.name_of(field)`") .to_owned(); let field_3 = my_struct .remove_by_name(field_3_name.as_ref()) .expect("Invalid name for `my_struct.remove_by_name(name)`"); assert_eq!(my_struct.field_len(), 1); assert_eq!(field_3.0, "c"); let field_1_name = my_struct .name_at(0) .expect("Invalid name for `my_struct.name_at(name)`") .to_owned(); let field_1 = my_struct .remove_if(|(name, _field)| name == field_1_name) .expect("No valid name/field found for `my_struct.remove_if(|(name, field)|{})`"); assert_eq!(my_struct.field_len(), 0); assert_eq!(field_1.0, "a"); } ``` </details> --------- Co-authored-by: Gino Valente <[email protected]>
1 parent 5f1ca6c commit 0439f55

File tree

2 files changed

+174
-1
lines changed

2 files changed

+174
-1
lines changed

crates/bevy_reflect/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ homepage = "https://bevy.org"
77
repository = "https://github.com/bevyengine/bevy"
88
license = "MIT OR Apache-2.0"
99
keywords = ["bevy"]
10-
rust-version = "1.85.0"
10+
rust-version = "1.87.0"
1111

1212
[features]
1313
default = ["std", "smallvec", "indexmap", "debug", "auto_register_inventory"]

crates/bevy_reflect/src/structs.rs

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,66 @@ impl DynamicStruct {
330330
self.insert_boxed(name, Box::new(value));
331331
}
332332

333+
/// Removes a field at `index`.
334+
pub fn remove_at(
335+
&mut self,
336+
index: usize,
337+
) -> Option<(Cow<'static, str>, Box<dyn PartialReflect>)> {
338+
let mut i: usize = 0;
339+
let mut extract = self.field_names.extract_if(0..self.field_names.len(), |n| {
340+
let mut result = false;
341+
if i == index {
342+
self.field_indices
343+
.remove(n)
344+
.expect("Invalid name for `field_indices.remove(name)`");
345+
result = true;
346+
} else if i > index {
347+
*self
348+
.field_indices
349+
.get_mut(n)
350+
.expect("Invalid name for `field_indices.get_mut(name)`") -= 1;
351+
}
352+
i += 1;
353+
result
354+
});
355+
356+
let name = extract
357+
.nth(0)
358+
.expect("Invalid index for `extract.nth(index)`");
359+
extract.for_each(drop); // Fully evaluate the rest of the iterator, so we don't short-circuit the extract.
360+
361+
Some((name, self.fields.remove(index)))
362+
}
363+
364+
/// Removes the first field that satisfies the given predicate, `f`.
365+
pub fn remove_if<F>(&mut self, mut f: F) -> Option<(Cow<'static, str>, Box<dyn PartialReflect>)>
366+
where
367+
F: FnMut((&str, &dyn PartialReflect)) -> bool,
368+
{
369+
if let Some(index) = self
370+
.field_names
371+
.iter()
372+
.zip(self.fields.iter())
373+
.position(|(name, field)| f((name.as_ref(), field.as_ref())))
374+
{
375+
self.remove_at(index)
376+
} else {
377+
None
378+
}
379+
}
380+
381+
/// Removes a field by `name`.
382+
pub fn remove_by_name(
383+
&mut self,
384+
name: &str,
385+
) -> Option<(Cow<'static, str>, Box<dyn PartialReflect>)> {
386+
if let Some(index) = self.index_of(name) {
387+
self.remove_at(index)
388+
} else {
389+
None
390+
}
391+
}
392+
333393
/// Gets the index of the field with the given name.
334394
pub fn index_of(&self, name: &str) -> Option<usize> {
335395
self.field_indices.get(name).copied()
@@ -686,12 +746,125 @@ pub fn struct_debug(dyn_struct: &dyn Struct, f: &mut Formatter<'_>) -> core::fmt
686746
#[cfg(test)]
687747
mod tests {
688748
use crate::{structs::*, *};
749+
use alloc::borrow::ToOwned;
689750
#[derive(Reflect, Default)]
690751
struct MyStruct {
691752
a: (),
692753
b: (),
693754
c: (),
694755
}
756+
757+
#[test]
758+
fn dynamic_struct_remove_at() {
759+
let mut my_struct = MyStruct::default().to_dynamic_struct();
760+
761+
assert_eq!(my_struct.field_len(), 3);
762+
763+
let field_2 = my_struct
764+
.remove_at(1)
765+
.expect("Invalid index for `my_struct.remove_at(index)`");
766+
767+
assert_eq!(my_struct.field_len(), 2);
768+
assert_eq!(field_2.0, "b");
769+
770+
let field_3 = my_struct
771+
.remove_at(0)
772+
.expect("Invalid index for `my_struct.remove_at(index)`");
773+
774+
assert_eq!(my_struct.field_len(), 1);
775+
assert_eq!(field_3.0, "a");
776+
777+
let field_1 = my_struct
778+
.remove_at(0)
779+
.expect("Invalid index for `my_struct.remove_at(index)`");
780+
781+
assert_eq!(my_struct.field_len(), 0);
782+
assert_eq!(field_1.0, "c");
783+
}
784+
785+
#[test]
786+
fn dynamic_struct_remove_by_name() {
787+
let mut my_struct = MyStruct::default().to_dynamic_struct();
788+
789+
assert_eq!(my_struct.field_len(), 3);
790+
791+
let field_3 = my_struct
792+
.remove_by_name("b")
793+
.expect("Invalid name for `my_struct.remove_by_name(name)`");
794+
795+
assert_eq!(my_struct.field_len(), 2);
796+
assert_eq!(field_3.0, "b");
797+
798+
let field_2 = my_struct
799+
.remove_by_name("c")
800+
.expect("Invalid name for `my_struct.remove_by_name(name)`");
801+
802+
assert_eq!(my_struct.field_len(), 1);
803+
assert_eq!(field_2.0, "c");
804+
805+
let field_1 = my_struct
806+
.remove_by_name("a")
807+
.expect("Invalid name for `my_struct.remove_by_name(name)`");
808+
809+
assert_eq!(my_struct.field_len(), 0);
810+
assert_eq!(field_1.0, "a");
811+
}
812+
813+
#[test]
814+
fn dynamic_struct_remove_if() {
815+
let mut my_struct = MyStruct::default().to_dynamic_struct();
816+
817+
assert_eq!(my_struct.field_len(), 3);
818+
819+
let field_3_name = "c";
820+
let field_3 = my_struct
821+
.remove_if(|(name, _field)| name == field_3_name)
822+
.expect("No valid name/field found for `my_struct.remove_with(|(name, field)|{})");
823+
824+
assert_eq!(my_struct.field_len(), 2);
825+
assert_eq!(field_3.0, "c");
826+
}
827+
828+
#[test]
829+
fn dynamic_struct_remove_combo() {
830+
let mut my_struct = MyStruct::default().to_dynamic_struct();
831+
832+
assert_eq!(my_struct.field_len(), 3);
833+
834+
let field_2 = my_struct
835+
.remove_at(
836+
my_struct
837+
.index_of("b")
838+
.expect("Invalid field for `my_struct.index_of(field)`"),
839+
)
840+
.expect("Invalid index for `my_struct.remove_at(index)`");
841+
842+
assert_eq!(my_struct.field_len(), 2);
843+
assert_eq!(field_2.0, "b");
844+
845+
let field_3_name = my_struct
846+
.name_at(1)
847+
.expect("Invalid field for `my_struct.name_of(field)`")
848+
.to_owned();
849+
let field_3 = my_struct
850+
.remove_by_name(field_3_name.as_ref())
851+
.expect("Invalid name for `my_struct.remove_by_name(name)`");
852+
853+
assert_eq!(my_struct.field_len(), 1);
854+
assert_eq!(field_3.0, "c");
855+
856+
let field_1_name = my_struct
857+
.name_at(0)
858+
.expect("Invalid name for `my_struct.name_at(name)`")
859+
.to_owned();
860+
let field_1 = my_struct
861+
.remove_if(|(name, _field)| name == field_1_name)
862+
.expect("No valid name/field found for `my_struct.remove_with(|(name, field)|{})`");
863+
864+
assert_eq!(my_struct.field_len(), 0);
865+
assert_eq!(field_1.0, "a");
866+
}
867+
695868
#[test]
696869
fn next_index_increment() {
697870
let my_struct = MyStruct::default();

0 commit comments

Comments
 (0)