bevy_ecs/entity/
visit_entities.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
pub use bevy_ecs_macros::{VisitEntities, VisitEntitiesMut};

use crate::entity::Entity;

/// Apply an operation to all entities in a container.
///
/// This is implemented by default for types that implement [`IntoIterator`].
///
/// It may be useful to implement directly for types that can't produce an
/// iterator for lifetime reasons, such as those involving internal mutexes.
pub trait VisitEntities {
    /// Apply an operation to all contained entities.
    fn visit_entities<F: FnMut(Entity)>(&self, f: F);
}

impl<T> VisitEntities for T
where
    for<'a> &'a T: IntoIterator<Item = &'a Entity>,
{
    fn visit_entities<F: FnMut(Entity)>(&self, f: F) {
        self.into_iter().copied().for_each(f);
    }
}

impl VisitEntities for Entity {
    fn visit_entities<F: FnMut(Entity)>(&self, mut f: F) {
        f(*self);
    }
}

/// Apply an operation to mutable references to all entities in a container.
///
/// This is implemented by default for types that implement [`IntoIterator`].
///
/// It may be useful to implement directly for types that can't produce an
/// iterator for lifetime reasons, such as those involving internal mutexes.
pub trait VisitEntitiesMut: VisitEntities {
    /// Apply an operation to mutable references to all contained entities.
    fn visit_entities_mut<F: FnMut(&mut Entity)>(&mut self, f: F);
}

impl<T: VisitEntities> VisitEntitiesMut for T
where
    for<'a> &'a mut T: IntoIterator<Item = &'a mut Entity>,
{
    fn visit_entities_mut<F: FnMut(&mut Entity)>(&mut self, f: F) {
        self.into_iter().for_each(f);
    }
}

impl VisitEntitiesMut for Entity {
    fn visit_entities_mut<F: FnMut(&mut Entity)>(&mut self, mut f: F) {
        f(self);
    }
}

#[cfg(test)]
mod tests {
    use crate::{
        self as bevy_ecs,
        entity::{EntityHashMap, MapEntities, SceneEntityMapper},
        world::World,
    };
    use bevy_utils::HashSet;

    use super::*;

    #[derive(VisitEntities, Debug, PartialEq)]
    struct Foo {
        ordered: Vec<Entity>,
        unordered: HashSet<Entity>,
        single: Entity,
        #[allow(dead_code)]
        #[visit_entities(ignore)]
        not_an_entity: String,
    }

    // Need a manual impl since VisitEntitiesMut isn't implemented for `HashSet`.
    // We don't expect users to actually do this - it's only for test purposes
    // to prove out the automatic `MapEntities` impl we get with `VisitEntitiesMut`.
    impl VisitEntitiesMut for Foo {
        fn visit_entities_mut<F: FnMut(&mut Entity)>(&mut self, mut f: F) {
            self.ordered.visit_entities_mut(&mut f);
            self.unordered = self
                .unordered
                .drain()
                .map(|mut entity| {
                    f(&mut entity);
                    entity
                })
                .collect();
            f(&mut self.single);
        }
    }

    #[test]
    fn visit_entities() {
        let mut world = World::new();
        let entities = world.entities();
        let mut foo = Foo {
            ordered: vec![entities.reserve_entity(), entities.reserve_entity()],
            unordered: [
                entities.reserve_entity(),
                entities.reserve_entity(),
                entities.reserve_entity(),
            ]
            .into_iter()
            .collect(),
            single: entities.reserve_entity(),
            not_an_entity: "Bar".into(),
        };

        let mut entity_map = EntityHashMap::<Entity>::default();
        let mut remapped = Foo {
            ordered: vec![],
            unordered: HashSet::new(),
            single: Entity::PLACEHOLDER,
            not_an_entity: foo.not_an_entity.clone(),
        };

        // Note: this assumes that the VisitEntities derive is field-ordered,
        //       which isn't explicitly stated/guaranteed.
        //       If that changes, this test will fail, but that might be OK if
        //       we're intentionally breaking that assumption.
        let mut i = 0;
        foo.visit_entities(|entity| {
            let new_entity = entities.reserve_entity();
            if i < foo.ordered.len() {
                assert_eq!(entity, foo.ordered[i]);
                remapped.ordered.push(new_entity);
            } else if i < foo.ordered.len() + foo.unordered.len() {
                assert!(foo.unordered.contains(&entity));
                remapped.unordered.insert(new_entity);
            } else {
                assert_eq!(entity, foo.single);
                remapped.single = new_entity;
            }

            entity_map.insert(entity, new_entity);

            i += 1;
        });

        SceneEntityMapper::world_scope(&mut entity_map, &mut world, |_, mapper| {
            foo.map_entities(mapper);
        });

        assert_eq!(foo, remapped);
    }
}