1use super::{*, either_both::EitherBothState};
2
3#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
5pub enum Either<T, U> {
6 Left(T),
7 Right(U),
8}
9
10pub struct EitherFetch<T, U> {
11 left: T,
12 right: U,
13 matches: Matches,
14}
15
16enum Matches {
17 Left,
18 Right,
19}
20
21impl<'w, T: Fetch<'w>, U: Fetch<'w>> Fetch<'w> for EitherFetch<T, U> {
22 type Item = Either<T::Item, U::Item>;
23 type State = EitherBothState<T::State, U::State>;
24
25 fn is_dense(&self) -> bool {
26 self.left.is_dense() && self.right.is_dense()
27 }
28
29 unsafe fn init(
30 world: &World,
31 state: &Self::State,
32 last_change_tick: u32,
33 change_tick: u32,
34 ) -> Self {
35 EitherFetch {
36 left: T::init(world, &state.left_state, last_change_tick, change_tick),
37 right: U::init(world, &state.right_state, last_change_tick, change_tick),
38 matches: Matches::Left,
39 }
40 }
41
42 unsafe fn set_archetype(
43 &mut self,
44 state: &Self::State,
45 archetype: &Archetype,
46 tables: &Tables,
47 ) {
48 let left_match = state.left_state.matches_archetype(archetype);
49 let right_match = state.right_state.matches_archetype(archetype);
50 if left_match {
51 self.left.set_archetype(&state.left_state, archetype, tables);
52 self.matches = Matches::Left;
53 } else if right_match {
54 self.matches = Matches::Right;
55 self.right.set_archetype(&state.right_state, archetype, tables);
56 } else if cfg!(not(all(not(debug_assertions), unchecked))) {
57 unreachable!("neither left nor right side matched. what?");
58 }
59 }
60
61 unsafe fn set_table(&mut self, state: &Self::State, table: &Table) {
62 let left_match = state.left_state.matches_table(table);
63 let right_match = state.right_state.matches_table(table);
64 if left_match {
65 self.left.set_table(&state.left_state, table);
66 self.matches = Matches::Left;
67 } else if right_match {
68 self.matches = Matches::Right;
69 self.right.set_table(&state.right_state, table);
70 } else if cfg!(not(all(not(debug_assertions), unchecked))) {
71 unreachable!("neither left nor right side matched. what?");
72 }
73 }
74
75 unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item {
76 match self.matches {
77 Matches::Left => Either::Left(self.left.archetype_fetch(archetype_index)),
78 Matches::Right => Either::Right(self.right.archetype_fetch(archetype_index)),
79 }
80 }
81
82 unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item {
83 match self.matches {
84 Matches::Left => Either::Left(self.left.table_fetch(table_row)),
85 Matches::Right => Either::Right(self.right.table_fetch(table_row)),
86 }
87 }
88}
89
90unsafe impl<'w, T: Fetch<'w>, U: Fetch<'w>> ReadOnlyFetch for EitherFetch<T, U> {}
91
92impl<T: WorldQuery, U: WorldQuery> WorldQuery for Either<T, U> {
93 type Fetch = EitherFetch<T::Fetch, U::Fetch>;
94 type State = EitherBothState<T::State, U::State>;
95}
96
97#[cfg(test)]
98mod tests {
99 use bevy::prelude::*;
100 use super::*;
101
102 #[derive(Clone, Copy)]
103 struct LeftElem;
104
105 #[derive(Clone, Copy)]
106 struct RightElem;
107
108 fn push_entities(world: &mut World) -> (u32, u32, u32) {
109 static SUPERPERM: &[Option<EitherBoth<LeftElem, RightElem>>] = &[
111 None,
112 Some(EitherBoth::Left(LeftElem)),
113 Some(EitherBoth::Right(RightElem)),
114 Some(EitherBoth::Both(LeftElem, RightElem)),
115 None,
116 Some(EitherBoth::Left(LeftElem)),
117 Some(EitherBoth::Right(RightElem)),
118 None,
119 Some(EitherBoth::Both(LeftElem, RightElem)),
120 Some(EitherBoth::Left(LeftElem)),
121 Some(EitherBoth::Right(RightElem)),
122 None,
123 Some(EitherBoth::Left(LeftElem)),
124 Some(EitherBoth::Both(LeftElem, RightElem)),
125 Some(EitherBoth::Right(RightElem)),
126 None,
127 Some(EitherBoth::Left(LeftElem)),
128 None,
129 Some(EitherBoth::Right(RightElem)),
130 Some(EitherBoth::Both(LeftElem, RightElem)),
131 Some(EitherBoth::Left(LeftElem)),
132 None,
133 Some(EitherBoth::Right(RightElem)),
134 Some(EitherBoth::Left(LeftElem)),
135 Some(EitherBoth::Both(LeftElem, RightElem)),
136 None,
137 Some(EitherBoth::Right(RightElem)),
138 Some(EitherBoth::Left(LeftElem)),
139 None,
140 Some(EitherBoth::Both(LeftElem, RightElem)),
141 Some(EitherBoth::Right(RightElem)),
142 Some(EitherBoth::Left(LeftElem)),
143 None,
144 ];
145 let mut left_count = 0;
146 let mut right_count = 0;
147 let mut both_count = 0;
148
149 for &p in SUPERPERM {
150 match p {
151 Some(EitherBoth::Both(l, r)) => {
152 world.spawn().insert(l).insert(r);
153 both_count += 1;
154 },
155 Some(EitherBoth::Left(l)) => {
156 world.spawn().insert(l);
157 left_count += 1;
158 },
159 Some(EitherBoth::Right(r)) => {
160 world.spawn().insert(r);
161 right_count += 1;
162 },
163 None => {
164 world.spawn();
165 },
166 }
167 }
168
169 (left_count, right_count, both_count)
170 }
171
172 #[derive(Debug, PartialEq, Eq)]
173 struct LeftCount(u32);
174
175 #[derive(Debug, PartialEq, Eq)]
176 struct RightCount(u32);
177
178 #[test]
179 fn test_eitherboth() {
180 let mut world = World::default();
181 world.insert_resource(LeftCount(0));
182 world.insert_resource(RightCount(0));
183 let (real_left_count, real_right_count, real_both_count) = push_entities(&mut world);
184 let mut update_stage = SystemStage::single((|
185 q: Query<Either<&LeftElem, &RightElem>>,
186 mut l: ResMut<LeftCount>,
187 mut r: ResMut<RightCount>,
188 | {
189 for eb in q.iter() {
190 match eb {
191 Either::Left(_) => {
192 l.0 += 1;
193 },
194 Either::Right(_) => {
195 r.0 += 1;
196 },
197 }
198 }
199 }
200 ).system());
201 update_stage.run(&mut world);
202 assert_eq!(world.get_resource::<LeftCount>().unwrap().0, real_left_count + real_both_count);
203 assert_eq!(world.get_resource::<RightCount>().unwrap().0, real_right_count);
204 }
205}