1use crate::query::QueryError;
7use crate::time::ChangeTick;
8use crate::world::WorldOwner;
9
10pub struct QueryCursor {
12 owner: WorldOwner,
13 fingerprint: u64,
14 last_observed: ChangeTick,
15}
16
17impl QueryCursor {
18 pub fn from_spec_start<T: 'static>(
20 world: &mut crate::world::World,
21 spec: &crate::query::QuerySpec,
22 ) -> Result<Self, QueryError> {
23 let fingerprint = world.query_fingerprint::<T>(spec)?;
24 Ok(Self::from_start(world, fingerprint))
25 }
26
27 pub fn from_spec_now<T: 'static>(
29 world: &mut crate::world::World,
30 spec: &crate::query::QuerySpec,
31 ) -> Result<Self, QueryError> {
32 let fingerprint = world.query_fingerprint::<T>(spec)?;
33 Self::from_now(world, fingerprint)
34 }
35
36 pub fn from_spec2_start<A: 'static, B: 'static>(
38 world: &mut crate::world::World,
39 spec: &crate::query::QuerySpec,
40 ) -> Result<Self, QueryError> {
41 let (plan, _, _) = world.resolve_query2_plan::<A, B>(spec)?;
42 Ok(Self::from_start(world, plan.fingerprint))
43 }
44
45 pub fn from_spec2_now<A: 'static, B: 'static>(
47 world: &mut crate::world::World,
48 spec: &crate::query::QuerySpec,
49 ) -> Result<Self, QueryError> {
50 let (plan, _, _) = world.resolve_query2_plan::<A, B>(spec)?;
51 Self::from_now(world, plan.fingerprint)
52 }
53
54 #[cfg(test)]
55 pub(crate) fn for_entities_from_start(
56 world: &mut crate::world::World,
57 spec: &crate::query::QuerySpec,
58 ) -> Result<Self, QueryError> {
59 let fingerprint = world.entity_query_fingerprint(spec)?;
60 Ok(Self::from_start(world, fingerprint))
61 }
62
63 pub fn fork(&self) -> Self {
65 Self {
66 owner: self.owner.clone(),
67 fingerprint: self.fingerprint,
68 last_observed: self.last_observed,
69 }
70 }
71
72 pub(crate) fn from_start(world: &crate::world::World, fingerprint: u64) -> Self {
73 Self {
74 owner: world.owner_token(),
75 fingerprint,
76 last_observed: ChangeTick::ZERO,
77 }
78 }
79
80 pub(crate) fn from_now(
81 world: &crate::world::World,
82 fingerprint: u64,
83 ) -> Result<Self, QueryError> {
84 Ok(Self {
85 owner: world.owner_token(),
86 fingerprint,
87 last_observed: world.change_tick(),
88 })
89 }
90
91 pub(crate) fn validate(
92 &self,
93 world: &crate::world::World,
94 fingerprint: u64,
95 ) -> Result<(), QueryError> {
96 if !self.owner.same(&world.owner_token()) {
97 return Err(QueryError::WrongOwner);
98 }
99 if self.fingerprint != fingerprint {
100 return Err(QueryError::WrongQuery {
101 detail: alloc::string::String::from("cursor fingerprint does not match query spec"),
102 });
103 }
104 Ok(())
105 }
106
107 pub fn since(&self) -> ChangeTick {
109 self.last_observed
110 }
111
112 pub(crate) fn commit(&mut self, captured_now: ChangeTick) {
113 self.last_observed = captured_now;
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120 use crate::component::ComponentOptions;
121 use crate::query::{QueryParams, QuerySpec};
122 use crate::world::WorldBuilder;
123
124 #[derive(Clone, Copy)]
125 struct Position(#[allow(dead_code)] i32);
126
127 #[derive(Clone, Copy)]
128 struct Velocity(#[allow(dead_code)] i32);
129
130 #[test]
131 fn query_cursor_commits_on_exhaustion() {
132 let mut builder = WorldBuilder::new();
133 builder
134 .register_component::<Position>(ComponentOptions::sparse())
135 .expect("register");
136 let mut world = builder.build().expect("build");
137 let entity = world.spawn().expect("spawn");
138 world.insert(entity, Position(1)).expect("insert");
139
140 let spec = QuerySpec::new().added::<Position>();
141 let mut cursor =
142 QueryCursor::from_spec_start::<Position>(&mut world, &spec).expect("cursor");
143 let params = QueryParams::new().cursor(&mut cursor);
144 let mut query = world.query::<Position>(&spec, params).expect("query");
145 assert!(query.next().is_some());
146 assert!(query.next().is_none());
147 drop(query);
148 assert!(cursor.since().raw() > 0);
149 }
150
151 #[test]
152 fn query_cursor_skips_commit_on_partial_iteration() {
153 let mut builder = WorldBuilder::new();
154 builder
155 .register_component::<Position>(ComponentOptions::sparse())
156 .expect("register");
157 let mut world = builder.build().expect("build");
158 let a = world.spawn().expect("spawn");
159 let b = world.spawn().expect("spawn");
160 world.insert(a, Position(1)).expect("insert");
161 world.insert(b, Position(2)).expect("insert");
162
163 let spec = QuerySpec::new().added::<Position>();
164 let mut cursor =
165 QueryCursor::from_spec_start::<Position>(&mut world, &spec).expect("cursor");
166 let before = cursor.since();
167 let params = QueryParams::new().cursor(&mut cursor);
168 let mut query = world.query::<Position>(&spec, params).expect("query");
169 let _ = query.next();
170 drop(query);
171 assert_eq!(cursor.since(), before);
172 }
173
174 #[test]
175 fn from_spec_now_and_from_now_capture_change_tick() {
176 let mut builder = WorldBuilder::new();
177 builder
178 .register_component::<Position>(ComponentOptions::sparse())
179 .expect("register");
180 let mut world = builder.build().expect("build");
181 let entity = world.spawn().expect("spawn");
182 world.insert(entity, Position(1)).expect("insert");
183 let spec = QuerySpec::new();
184 let fingerprint = world.query_fingerprint::<Position>(&spec).expect("fp");
185 let cursor = QueryCursor::from_spec_now::<Position>(&mut world, &spec).expect("now");
186 assert_eq!(cursor.since(), world.change_tick());
187 let direct = QueryCursor::from_now(&world, fingerprint).expect("direct");
188 assert_eq!(direct.since(), world.change_tick());
189 }
190
191 #[test]
192 fn query2_now_and_fork_preserve_fingerprint_and_tick() {
193 let mut builder = WorldBuilder::new();
194 builder
195 .register_component::<Position>(ComponentOptions::sparse())
196 .expect("position");
197 builder
198 .register_component::<Velocity>(ComponentOptions::sparse())
199 .expect("velocity");
200 let mut world = builder.build().expect("world");
201 let entity = world.spawn().expect("spawn");
202 world.insert(entity, Position(1)).expect("position");
203 world.insert(entity, Velocity(2)).expect("velocity");
204 let spec = QuerySpec::new().changed::<Position>();
205
206 let cursor = QueryCursor::from_spec2_now::<Position, Velocity>(&mut world, &spec)
207 .expect("query2 now");
208 let fork = cursor.fork();
209 assert_eq!(cursor.since(), world.change_tick());
210 assert_eq!(fork.since(), cursor.since());
211
212 let (plan, _, _) = world
213 .resolve_query2_plan::<Position, Velocity>(&spec)
214 .expect("plan");
215 assert!(fork.validate(&world, plan.fingerprint).is_ok());
216 }
217
218 #[test]
219 fn validate_rejects_foreign_owner() {
220 let mut builder_a = WorldBuilder::new();
221 builder_a
222 .register_component::<Position>(ComponentOptions::sparse())
223 .expect("register");
224 let mut world_a = builder_a.build().expect("a");
225 let spec = QuerySpec::new();
226 let mut cursor =
227 QueryCursor::from_spec_start::<Position>(&mut world_a, &spec).expect("cursor");
228 let mut builder_b = WorldBuilder::new();
229 builder_b
230 .register_component::<Position>(ComponentOptions::sparse())
231 .expect("register");
232 let mut world_b = builder_b.build().expect("b");
233 let fingerprint = world_b.query_fingerprint::<Position>(&spec).expect("fp");
234 assert!(matches!(
235 cursor.validate(&world_b, fingerprint),
236 Err(QueryError::WrongOwner)
237 ));
238 let _ = &mut cursor;
239 }
240}