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