anput/
database.rs

1use crate::{
2    bundle::Bundle,
3    commands::{DespawnManyCommand, SpawnManyCommand},
4    entity::Entity,
5    query::{TypedLookupAccess, TypedLookupFetch},
6};
7
8pub trait WorldCreateIteratorExt: Iterator
9where
10    Self::Item: Bundle + Send + Sync + 'static,
11{
12    fn to_spawn_command(self) -> SpawnManyCommand<Self::Item>;
13}
14
15impl<I> WorldCreateIteratorExt for I
16where
17    I: Iterator,
18    I::Item: Bundle + Send + Sync + 'static,
19{
20    fn to_spawn_command(self) -> SpawnManyCommand<Self::Item> {
21        SpawnManyCommand::new(self)
22    }
23}
24
25pub trait WorldDestroyIteratorExt: Iterator {
26    fn to_despawn_command(self) -> DespawnManyCommand;
27}
28
29impl<I> WorldDestroyIteratorExt for I
30where
31    I: Iterator<Item = Entity>,
32{
33    fn to_despawn_command(self) -> DespawnManyCommand {
34        DespawnManyCommand::new(self)
35    }
36}
37
38pub struct WorldJoinIterator<'a, const LOCKING: bool, LeftIter, RightFetch, F, EntityIIter>
39where
40    LeftIter: Iterator,
41    RightFetch: TypedLookupFetch<'a, LOCKING>,
42    F: Fn(LeftIter::Item) -> EntityIIter,
43    EntityIIter: Iterator<Item = Entity>,
44{
45    left_iter: LeftIter,
46    right_lookup: TypedLookupAccess<'a, LOCKING, RightFetch>,
47    entity_producer: F,
48    current: Option<(LeftIter::Item, EntityIIter)>,
49}
50
51impl<'a, const LOCKING: bool, LeftIter, RightFetch, F, EntityIter>
52    WorldJoinIterator<'a, LOCKING, LeftIter, RightFetch, F, EntityIter>
53where
54    LeftIter: Iterator,
55    RightFetch: TypedLookupFetch<'a, LOCKING>,
56    F: Fn(LeftIter::Item) -> EntityIter,
57    EntityIter: Iterator<Item = Entity>,
58{
59    pub fn new(
60        left_iter: LeftIter,
61        right_lookup: TypedLookupAccess<'a, LOCKING, RightFetch>,
62        entity_producer: F,
63    ) -> Self {
64        Self {
65            left_iter,
66            right_lookup,
67            entity_producer,
68            current: None,
69        }
70    }
71}
72
73impl<'a, const LOCKING: bool, LeftIter, RightFetch, F, EI> Iterator
74    for WorldJoinIterator<'a, LOCKING, LeftIter, RightFetch, F, EI>
75where
76    LeftIter: Iterator,
77    RightFetch: TypedLookupFetch<'a, LOCKING>,
78    F: Fn(LeftIter::Item) -> EI,
79    EI: Iterator<Item = Entity>,
80    LeftIter::Item: Copy,
81{
82    type Item = (LeftIter::Item, RightFetch::Value);
83
84    fn next(&mut self) -> Option<Self::Item> {
85        loop {
86            if let Some((left, entities)) = self.current.as_mut() {
87                if let Some(entity) = entities.next() {
88                    let right = self.right_lookup.access(entity)?;
89                    return Some((*left, right));
90                } else {
91                    self.current = None;
92                }
93            }
94            let left = self.left_iter.next()?;
95            let entities = (self.entity_producer)(left);
96            self.current = Some((left, entities));
97        }
98    }
99}
100
101pub trait WorldJoinIteratorExt: Iterator {
102    fn join<'a, const LOCKING: bool, RightFetch, F, EntityIter>(
103        self,
104        right_lookup: TypedLookupAccess<'a, LOCKING, RightFetch>,
105        entity_producer: F,
106    ) -> WorldJoinIterator<'a, LOCKING, Self, RightFetch, F, EntityIter>
107    where
108        RightFetch: TypedLookupFetch<'a, LOCKING>,
109        F: Fn(Self::Item) -> EntityIter,
110        EntityIter: Iterator<Item = Entity>,
111        Self: Sized;
112}
113
114impl<I> WorldJoinIteratorExt for I
115where
116    I: Iterator,
117{
118    fn join<'a, const LOCKING: bool, RightFetch, F, EntityIter>(
119        self,
120        right_lookup: TypedLookupAccess<'a, LOCKING, RightFetch>,
121        entity_producer: F,
122    ) -> WorldJoinIterator<'a, LOCKING, Self, RightFetch, F, EntityIter>
123    where
124        RightFetch: TypedLookupFetch<'a, LOCKING>,
125        F: Fn(Self::Item) -> EntityIter,
126        EntityIter: Iterator<Item = Entity>,
127        Self: Sized,
128    {
129        WorldJoinIterator::new(self, right_lookup, entity_producer)
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136    use crate::{
137        commands::Command,
138        entity::Entity,
139        world::{Relation, World},
140    };
141
142    #[test]
143    fn test_crud() {
144        let mut world = World::default();
145
146        // Create:
147        [
148            ("a", 1usize, 1.0f32),
149            ("b", 2usize, 2.0f32),
150            ("c", 3usize, 3.0f32),
151        ]
152        .into_iter()
153        .to_spawn_command()
154        .execute(&mut world);
155
156        // Read:
157        let rows = world
158            .query::<true, (&&str, &usize, &f32)>()
159            .collect::<Vec<_>>();
160
161        assert_eq!(
162            rows,
163            vec![
164                (&"a", &1usize, &1.0f32),
165                (&"b", &2usize, &2.0f32),
166                (&"c", &3usize, &3.0f32),
167            ]
168        );
169
170        // Update:
171        for value in world.query::<true, &mut usize>() {
172            if *value < 2 {
173                *value = 0;
174            }
175        }
176
177        // Delete:
178        world
179            .query::<true, (Entity, &usize)>()
180            .filter(|(_, &a)| a > 0)
181            .map(|(entity, _)| entity)
182            .to_despawn_command()
183            .execute(&mut world);
184
185        let rows = world
186            .query::<true, (&&str, &usize, &f32)>()
187            .collect::<Vec<_>>();
188        assert_eq!(rows, vec![(&"a", &0usize, &1.0f32),]);
189    }
190
191    #[test]
192    fn test_join() {
193        let mut world = World::default();
194
195        let a = world.spawn(("a", 1usize)).unwrap();
196        let b = world.spawn(("b", 2usize)).unwrap();
197        world
198            .spawn(("c", 3usize, Relation::<()>::new((), a).with((), b)))
199            .unwrap();
200        world
201            .spawn(("d", 4usize, Relation::<()>::default()))
202            .unwrap();
203
204        // Join:
205        let rows = world
206            .query::<true, (&&str, &Relation<()>)>()
207            .join(world.lookup_access::<true, &usize>(), |(_, relation)| {
208                relation.entities()
209            })
210            .map(|((name, _), value)| (name, value))
211            .collect::<Vec<_>>();
212
213        assert_eq!(rows, vec![(&"c", &1usize), (&"c", &2usize)]);
214    }
215}