@@ -47,6 +47,10 @@ impl Labels {
4747 self . labels . insert ( label. into ( ) ) ;
4848 }
4949
50+ pub fn remove < T : Into < Cow < ' static , str > > > ( & mut self , label : T ) {
51+ self . labels . remove ( & label. into ( ) ) ;
52+ }
53+
5054 pub fn iter ( & self ) -> impl Iterator < Item = & str > {
5155 self . labels . iter ( ) . map ( |label| label. deref ( ) )
5256 }
@@ -60,37 +64,182 @@ pub struct EntityLabels {
6064}
6165
6266impl EntityLabels {
63- pub fn get ( & self , label : & str ) -> Option < & [ Entity ] > {
67+ pub fn get ( & self , label : & str ) -> & [ Entity ] {
6468 self . label_entities
6569 . get ( label)
6670 . map ( |entities| entities. as_slice ( ) )
71+ . unwrap_or ( & [ ] )
6772 }
6873}
6974
7075pub ( crate ) fn entity_labels_system (
7176 mut entity_labels : ResMut < EntityLabels > ,
72- // TODO: use change tracking when add/remove events are added
73- // mut query: Query<(Entity, Changed<Labels>)>,
77+ // the system runs in an early stage and so can't use a Changed<Labels> filter
7478 query : Query < ( Entity , & Labels ) > ,
7579) {
7680 let entity_labels = entity_labels. deref_mut ( ) ;
81+
82+ for entity in query. removed :: < Labels > ( ) {
83+ if let Some ( labels) = entity_labels. entity_labels . get ( entity) {
84+ for label in labels. iter ( ) {
85+ if let Some ( entities) = entity_labels. label_entities . get_mut ( label) {
86+ entities. retain ( |e| e != entity) ;
87+ }
88+ }
89+ }
90+ }
91+
7792 for ( entity, labels) in query. iter ( ) {
7893 let current_labels = entity_labels
7994 . entity_labels
8095 . entry ( entity)
8196 . or_insert_with ( HashSet :: default) ;
97+
8298 for removed_label in current_labels. difference ( & labels. labels ) {
8399 if let Some ( entities) = entity_labels. label_entities . get_mut ( removed_label) {
84100 entities. retain ( |e| * e != entity) ;
85101 }
86102 }
87103
88104 for added_label in labels. labels . difference ( & current_labels) {
89- if let Some ( entities) = entity_labels. label_entities . get_mut ( added_label) {
90- entities. push ( entity) ;
91- }
105+ entity_labels
106+ . label_entities
107+ . entry ( added_label. clone ( ) )
108+ . or_insert_with ( Vec :: new)
109+ . push ( entity) ;
92110 }
93111
94112 * current_labels = labels. labels . clone ( ) ;
95113 }
96114}
115+
116+ #[ cfg( test) ]
117+ mod tests {
118+ use super :: * ;
119+
120+ fn setup ( ) -> ( World , Resources , bevy_ecs:: Schedule ) {
121+ let world = World :: new ( ) ;
122+ let mut resources = Resources :: default ( ) ;
123+ resources. insert ( EntityLabels :: default ( ) ) ;
124+ let mut schedule = bevy_ecs:: Schedule :: default ( ) ;
125+ schedule. add_stage ( "test" , SystemStage :: serial ( ) ) ;
126+ schedule. add_system_to_stage ( "test" , entity_labels_system. system ( ) ) ;
127+ ( world, resources, schedule)
128+ }
129+
130+ fn holy_cow ( ) -> Labels {
131+ Labels :: from ( [ "holy" , "cow" ] . iter ( ) . cloned ( ) )
132+ }
133+
134+ fn holy_shamoni ( ) -> Labels {
135+ Labels :: from ( [ "holy" , "shamoni" ] . iter ( ) . cloned ( ) )
136+ }
137+
138+ #[ test]
139+ fn adds_spawned_entity ( ) {
140+ let ( mut world, mut resources, mut schedule) = setup ( ) ;
141+
142+ let e1 = world. spawn ( ( holy_cow ( ) , ) ) ;
143+ schedule. initialize_and_run ( & mut world, & mut resources) ;
144+
145+ let entity_labels = resources. get :: < EntityLabels > ( ) . unwrap ( ) ;
146+ assert_eq ! ( entity_labels. get( "holy" ) , & [ e1] , "holy" ) ;
147+ assert_eq ! ( entity_labels. get( "cow" ) , & [ e1] , "cow" ) ;
148+ assert_eq ! ( entity_labels. get( "shalau" ) , & [ ] , "shalau" ) ;
149+ }
150+
151+ #[ test]
152+ fn add_labels ( ) {
153+ let ( mut world, mut resources, mut schedule) = setup ( ) ;
154+ let e1 = world. spawn ( ( holy_cow ( ) , ) ) ;
155+ schedule. initialize_and_run ( & mut world, & mut resources) ;
156+
157+ world. get_mut :: < Labels > ( e1) . unwrap ( ) . insert ( "shalau" ) ;
158+ schedule. initialize_and_run ( & mut world, & mut resources) ;
159+
160+ let entity_labels = resources. get :: < EntityLabels > ( ) . unwrap ( ) ;
161+ assert_eq ! ( entity_labels. get( "holy" ) , & [ e1] , "holy" ) ;
162+ assert_eq ! ( entity_labels. get( "cow" ) , & [ e1] , "cow" ) ;
163+ assert_eq ! ( entity_labels. get( "shalau" ) , & [ e1] , "shalau" ) ;
164+ }
165+
166+ #[ test]
167+ fn remove_labels ( ) {
168+ let ( mut world, mut resources, mut schedule) = setup ( ) ;
169+ let e1 = world. spawn ( ( holy_cow ( ) , ) ) ;
170+ schedule. initialize_and_run ( & mut world, & mut resources) ;
171+
172+ world. get_mut :: < Labels > ( e1) . unwrap ( ) . remove ( "holy" ) ;
173+ schedule. initialize_and_run ( & mut world, & mut resources) ;
174+
175+ let entity_labels = resources. get :: < EntityLabels > ( ) . unwrap ( ) ;
176+ assert_eq ! ( entity_labels. get( "holy" ) , & [ ] , "holy" ) ;
177+ assert_eq ! ( entity_labels. get( "cow" ) , & [ e1] , "cow" ) ;
178+ assert_eq ! ( entity_labels. get( "shalau" ) , & [ ] , "shalau" ) ;
179+ }
180+
181+ #[ test]
182+ fn removes_despawned_entity ( ) {
183+ let ( mut world, mut resources, mut schedule) = setup ( ) ;
184+ let e1 = world. spawn ( ( holy_cow ( ) , ) ) ;
185+ schedule. initialize_and_run ( & mut world, & mut resources) ;
186+
187+ world. despawn ( e1) . unwrap ( ) ;
188+ schedule. initialize_and_run ( & mut world, & mut resources) ;
189+
190+ let entity_labels = resources. get :: < EntityLabels > ( ) . unwrap ( ) ;
191+ assert_eq ! ( entity_labels. get( "holy" ) , & [ ] , "holy" ) ;
192+ assert_eq ! ( entity_labels. get( "cow" ) , & [ ] , "cow" ) ;
193+ assert_eq ! ( entity_labels. get( "shalau" ) , & [ ] , "shalau" ) ;
194+ }
195+
196+ #[ test]
197+ fn removes_labels_when_component_removed ( ) {
198+ let ( mut world, mut resources, mut schedule) = setup ( ) ;
199+ let e1 = world. spawn ( ( holy_cow ( ) , ) ) ;
200+ schedule. initialize_and_run ( & mut world, & mut resources) ;
201+
202+ world. remove_one :: < Labels > ( e1) . unwrap ( ) ;
203+ schedule. initialize_and_run ( & mut world, & mut resources) ;
204+
205+ let entity_labels = resources. get :: < EntityLabels > ( ) . unwrap ( ) ;
206+ assert_eq ! ( entity_labels. get( "holy" ) , & [ ] , "holy" ) ;
207+ assert_eq ! ( entity_labels. get( "cow" ) , & [ ] , "cow" ) ;
208+ assert_eq ! ( entity_labels. get( "shalau" ) , & [ ] , "shalau" ) ;
209+ }
210+
211+ #[ test]
212+ fn adds_another_spawned_entity ( ) {
213+ let ( mut world, mut resources, mut schedule) = setup ( ) ;
214+ let e1 = world. spawn ( ( holy_cow ( ) , ) ) ;
215+ schedule. initialize_and_run ( & mut world, & mut resources) ;
216+
217+ let e2 = world. spawn ( ( holy_shamoni ( ) , ) ) ;
218+ schedule. initialize_and_run ( & mut world, & mut resources) ;
219+
220+ let entity_labels = resources. get :: < EntityLabels > ( ) . unwrap ( ) ;
221+ assert_eq ! ( entity_labels. get( "holy" ) , & [ e1, e2] , "holy" ) ;
222+ assert_eq ! ( entity_labels. get( "cow" ) , & [ e1] , "cow" ) ;
223+ assert_eq ! ( entity_labels. get( "shamoni" ) , & [ e2] , "shamoni" ) ;
224+ assert_eq ! ( entity_labels. get( "shalau" ) , & [ ] , "shalau" ) ;
225+ }
226+
227+ #[ test]
228+ fn removes_despawned_entity_but_leaves_other ( ) {
229+ let ( mut world, mut resources, mut schedule) = setup ( ) ;
230+ let e1 = world. spawn ( ( holy_cow ( ) , ) ) ;
231+ schedule. initialize_and_run ( & mut world, & mut resources) ;
232+
233+ let e2 = world. spawn ( ( holy_shamoni ( ) , ) ) ;
234+ schedule. initialize_and_run ( & mut world, & mut resources) ;
235+
236+ world. despawn ( e1) . unwrap ( ) ;
237+ schedule. initialize_and_run ( & mut world, & mut resources) ;
238+
239+ let entity_labels = resources. get :: < EntityLabels > ( ) . unwrap ( ) ;
240+ assert_eq ! ( entity_labels. get( "holy" ) , & [ e2] , "holy" ) ;
241+ assert_eq ! ( entity_labels. get( "cow" ) , & [ ] , "cow" ) ;
242+ assert_eq ! ( entity_labels. get( "shamoni" ) , & [ e2] , "shamoni" ) ;
243+ assert_eq ! ( entity_labels. get( "shalau" ) , & [ ] , "shalau" ) ;
244+ }
245+ }
0 commit comments