1#[cfg(feature = "dev")]
2use crate::{grouping::RangeEnd, parameters::SubspaceId, PathBuilder};
3#[cfg(feature = "dev")]
4use arbitrary::Arbitrary;
5
6use crate::{
7 entry::{Entry, Timestamp},
8 path::Path,
9};
10
11use super::range::Range;
12
13#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
15#[cfg_attr(feature = "dev", derive(Arbitrary))]
16pub enum AreaSubspace<S> {
17 Any,
19 Id(S),
21}
22
23impl<S> AreaSubspace<S> {
24 pub fn is_any(&self) -> bool {
26 matches!(self, AreaSubspace::Any)
27 }
28}
29
30impl<S> AreaSubspace<S>
31where
32 S: PartialEq,
33{
34 pub fn includes(&self, sub: &S) -> bool {
36 match self {
37 AreaSubspace::Any => true,
38 AreaSubspace::Id(id) => sub == id,
39 }
40 }
41}
42
43impl<S> AreaSubspace<S>
44where
45 S: PartialEq + Clone,
46{
47 pub fn includes_area_subspace(&self, other: &Self) -> bool {
49 match (self, other) {
50 (AreaSubspace::Any, AreaSubspace::Any) => true,
51 (AreaSubspace::Any, AreaSubspace::Id(_)) => true,
52 (AreaSubspace::Id(_), AreaSubspace::Any) => false,
53 (AreaSubspace::Id(id), AreaSubspace::Id(id_other)) => id == id_other,
54 }
55 }
56
57 fn intersection(&self, other: &Self) -> Option<Self> {
59 match (self, other) {
60 (Self::Any, Self::Any) => Some(Self::Any),
61 (Self::Id(a), Self::Any) => Some(Self::Id(a.clone())),
62 (Self::Any, Self::Id(b)) => Some(Self::Id(b.clone())),
63 (Self::Id(a), Self::Id(b)) if a == b => Some(Self::Id(a.clone())),
64 (Self::Id(_a), Self::Id(_b)) => None,
65 }
66 }
67}
68
69impl<S: PartialEq> PartialEq<S> for AreaSubspace<S> {
70 fn eq(&self, other: &S) -> bool {
71 match self {
72 AreaSubspace::Any => false,
73 AreaSubspace::Id(s) => s == other,
74 }
75 }
76}
77
78#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
82#[cfg_attr(feature = "dev", derive(Arbitrary))]
83pub struct Area<const MCL: usize, const MCC: usize, const MPL: usize, S> {
84 subspace: AreaSubspace<S>,
86 path: Path<MCL, MCC, MPL>,
88 times: Range<Timestamp>,
90}
91
92impl<const MCL: usize, const MCC: usize, const MPL: usize, S> Area<MCL, MCC, MPL, S> {
93 pub fn new(
95 subspace: AreaSubspace<S>,
96 path: Path<MCL, MCC, MPL>,
97 times: Range<Timestamp>,
98 ) -> Self {
99 Area {
100 subspace,
101 path,
102 times,
103 }
104 }
105
106 pub fn subspace(&self) -> &AreaSubspace<S> {
110 &self.subspace
111 }
112
113 pub fn path(&self) -> &Path<MCL, MCC, MPL> {
117 &self.path
118 }
119
120 pub fn times(&self) -> &Range<Timestamp> {
124 &self.times
125 }
126
127 pub fn new_full() -> Self {
131 Self {
132 subspace: AreaSubspace::Any,
133 path: Path::new_empty(),
134 times: Range::new_open(0),
135 }
136 }
137
138 pub fn new_subspace(sub: S) -> Self {
142 Self {
143 subspace: AreaSubspace::Id(sub),
144 path: Path::new_empty(),
145 times: Range::new_open(0),
146 }
147 }
148}
149
150impl<const MCL: usize, const MCC: usize, const MPL: usize, S> Area<MCL, MCC, MPL, S>
151where
152 S: PartialEq,
153{
154 pub fn includes_entry<N, PD>(&self, entry: &Entry<MCL, MCC, MPL, N, S, PD>) -> bool {
156 self.includes_triplet(entry.subspace_id(), entry.path(), entry.timestamp())
157 }
158
159 pub fn includes_triplet(
161 &self,
162 subspace_id: &S,
163 path: &Path<MCL, MCC, MPL>,
164 timestamp: Timestamp,
165 ) -> bool {
166 self.subspace.includes(subspace_id)
167 && self.path.is_prefix_of(path)
168 && self.times.includes(×tamp)
169 }
170
171 pub fn includes_area(&self, area: &Self) -> bool {
173 match (&self.subspace, &area.subspace) {
174 (AreaSubspace::Any, AreaSubspace::Any) => {
175 self.path.is_prefix_of(&area.path) && self.times.includes_range(&area.times)
176 }
177 (AreaSubspace::Any, AreaSubspace::Id(_)) => {
178 self.path.is_prefix_of(&area.path) && self.times.includes_range(&area.times)
179 }
180 (AreaSubspace::Id(_), AreaSubspace::Any) => false,
181 (AreaSubspace::Id(subspace_a), AreaSubspace::Id(subspace_b)) => {
182 subspace_a == subspace_b
183 && self.path.is_prefix_of(&area.path)
184 && self.times.includes_range(&area.times)
185 }
186 }
187 }
188
189 pub fn could_be_pruned_by<N, PD>(&self, entry: &Entry<MCL, MCC, MPL, N, S, PD>) -> bool {
191 self.could_be_pruned_by_triplet(entry.subspace_id(), entry.path(), entry.timestamp())
192 }
193
194 pub fn could_be_pruned_by_triplet(
196 &self,
197 subspace_id: &S,
198 path: &Path<MCL, MCC, MPL>,
199 timestamp: Timestamp,
200 ) -> bool {
201 if let AreaSubspace::Id(my_subspace_id) = self.subspace() {
202 if my_subspace_id != subspace_id {
203 return false;
204 }
205 }
206
207 if !(self.times().includes(×tamp) || timestamp < self.times().start) {
208 return false;
209 }
210
211 path.is_prefix_of(&self.path) || path.is_prefixed_by(&self.path)
212 }
213}
214
215impl<const MCL: usize, const MCC: usize, const MPL: usize, S> Area<MCL, MCC, MPL, S>
216where
217 S: PartialEq + Clone,
218{
219 pub fn intersection(&self, other: &Area<MCL, MCC, MPL, S>) -> Option<Self> {
223 let subspace_id = self.subspace.intersection(&other.subspace)?;
224 let path = if self.path.is_prefix_of(&other.path) {
225 Some(other.path.clone())
226 } else if self.path.is_prefixed_by(&other.path) {
227 Some(self.path.clone())
228 } else {
229 None
230 }?;
231 let times = self.times.intersection(&other.times)?;
232 Some(Self {
233 subspace: subspace_id,
234 times,
235 path,
236 })
237 }
238
239 pub fn almost_includes_area(&self, other: &Area<MCL, MCC, MPL, S>) -> bool {
241 let subspace_is_fine = match (self.subspace(), other.subspace()) {
242 (AreaSubspace::Id(self_id), AreaSubspace::Id(other_id)) => self_id == other_id,
243 _ => true,
244 };
245 subspace_is_fine
246 && self.path.is_prefix_of(&other.path)
247 && self.times.includes_range(&other.times)
248 }
249}
250
251#[cfg(feature = "dev")]
252pub fn arbitrary_included_area<'a, const MCL: usize, const MCC: usize, const MPL: usize, S>(
253 area: &Area<MCL, MCC, MPL, S>,
254 u: &mut arbitrary::Unstructured<'a>,
255) -> arbitrary::Result<Area<MCL, MCC, MPL, S>>
256where
257 S: SubspaceId + Arbitrary<'a>,
258{
259 let suffix: Path<MCL, MCC, MPL> = Arbitrary::arbitrary(u)?;
260
261 let total_length = area.path().path_length() + suffix.path_length();
262 let total_component_length = area.path().component_count() + suffix.component_count();
263
264 let included_path = if let Ok(mut builder) = PathBuilder::new_from_prefix(
265 total_length,
266 total_component_length,
267 area.path(),
268 area.path.component_count(),
269 ) {
270 for component in suffix.components() {
271 builder.append_component(component)
272 }
273
274 builder.build()
275 } else {
276 area.path().clone()
277 };
278
279 let subspace = match area.subspace() {
280 AreaSubspace::Any => {
281 let is_subspace: bool = Arbitrary::arbitrary(u)?;
282
283 if is_subspace {
284 let subspace: S = Arbitrary::arbitrary(u)?;
285 AreaSubspace::Id(subspace)
286 } else {
287 AreaSubspace::Any
288 }
289 }
290 AreaSubspace::Id(id) => AreaSubspace::Id(id.clone()),
291 };
292
293 let start_offset: u64 = Arbitrary::arbitrary(u)?;
294
295 let new_start = area
296 .times()
297 .start
298 .checked_add(start_offset)
299 .map_or(area.times().start, |res| res);
300
301 let end_offset: u64 = Arbitrary::arbitrary(u)?;
302
303 let new_end = match area.times().end {
304 crate::grouping::RangeEnd::Closed(end) => {
305 RangeEnd::Closed(end.checked_sub(end_offset).map_or(end, |res| res))
306 }
307 crate::grouping::RangeEnd::Open => {
308 let is_any: bool = Arbitrary::arbitrary(u)?;
309
310 if is_any {
311 RangeEnd::Open
312 } else {
313 RangeEnd::Closed(end_offset)
314 }
315 }
316 };
317
318 let times = if new_start <= new_end {
319 Range::new(new_start, new_end)
320 } else {
321 *area.times()
322 };
323
324 Ok(Area::new(subspace, included_path, times))
325}
326
327#[cfg(test)]
328mod tests {
329
330 use crate::path::Component;
331
332 use super::*;
333
334 const MCL: usize = 8;
335 const MCC: usize = 4;
336 const MPL: usize = 16;
337
338 #[test]
339 fn subspace_area_includes() {
340 assert!(AreaSubspace::<u64>::Any.includes(&5));
341 assert!(AreaSubspace::<u64>::Id(5).includes(&5));
342 assert!(!AreaSubspace::<u64>::Id(8).includes(&5));
343 }
344
345 #[test]
346 fn subspace_area_intersects() {
347 let any_any_intersection = AreaSubspace::<u64>::Any.intersection(&AreaSubspace::<u64>::Any);
349
350 assert!(any_any_intersection.is_some());
351
352 assert!(any_any_intersection.unwrap() == AreaSubspace::<u64>::Any);
353
354 let any_id_intersection =
355 AreaSubspace::<u64>::Any.intersection(&AreaSubspace::<u64>::Id(6));
356
357 assert!(any_id_intersection.is_some());
358
359 assert!(any_id_intersection.unwrap() == AreaSubspace::<u64>::Id(6));
360
361 let id_id_intersection =
362 AreaSubspace::<u64>::Id(6).intersection(&AreaSubspace::<u64>::Id(6));
363
364 assert!(id_id_intersection.is_some());
365
366 assert!(id_id_intersection.unwrap() == AreaSubspace::<u64>::Id(6));
367
368 let empty_id_id_intersection =
371 AreaSubspace::<u64>::Id(7).intersection(&AreaSubspace::<u64>::Id(6));
372
373 assert!(empty_id_id_intersection.is_none())
374 }
375
376 #[test]
377 fn area_full() {
378 let full_area = Area::<MCL, MCC, MPL, u64>::new_full();
379
380 assert_eq!(
381 full_area,
382 Area {
383 subspace: AreaSubspace::Any,
384 path: Path::new_empty(),
385 times: Range::new_open(0)
386 }
387 )
388 }
389
390 #[test]
391 fn area_subspace() {
392 let subspace_area = Area::<MCL, MCC, MPL, u64>::new_subspace(7);
393
394 assert_eq!(
395 subspace_area,
396 Area {
397 subspace: AreaSubspace::Id(7),
398 path: Path::new_empty(),
399 times: Range::new_open(0)
400 }
401 )
402 }
403
404 #[test]
405 fn area_intersects() {
406 let empty_intersection_subspace = Area::<MCL, MCC, MPL, u64> {
407 subspace: AreaSubspace::Id(1),
408 path: Path::new_empty(),
409 times: Range::new_open(0),
410 }
411 .intersection(&Area {
412 subspace: AreaSubspace::Id(2),
413 path: Path::new_empty(),
414 times: Range::new_open(0),
415 });
416
417 assert!(empty_intersection_subspace.is_none());
418
419 let empty_intersection_path = Area::<MCL, MCC, MPL, u64> {
420 subspace: AreaSubspace::Id(1),
421 path: Path::new_from_slice(&[Component::new(b"0").unwrap()]).unwrap(),
422 times: Range::new_open(0),
423 }
424 .intersection(&Area {
425 subspace: AreaSubspace::Id(1),
426 path: Path::new_from_slice(&[Component::new(b"1").unwrap()]).unwrap(),
427 times: Range::new_open(0),
428 });
429
430 assert!(empty_intersection_path.is_none());
431
432 let empty_intersection_time = Area::<MCL, MCC, MPL, u64> {
433 subspace: AreaSubspace::Id(1),
434 path: Path::new_empty(),
435 times: Range::new_closed(0, 1).unwrap(),
436 }
437 .intersection(&Area {
438 subspace: AreaSubspace::Id(1),
439 path: Path::new_empty(),
440 times: Range::new_closed(2, 3).unwrap(),
441 });
442
443 assert!(empty_intersection_time.is_none());
444
445 let intersection = Area::<MCL, MCC, MPL, u64> {
446 subspace: AreaSubspace::Any,
447 path: Path::new_from_slice(&[Component::new(b"1").unwrap()]).unwrap(),
448 times: Range::new_closed(0, 10).unwrap(),
449 }
450 .intersection(&Area {
451 subspace: AreaSubspace::Id(1),
452 path: Path::new_from_slice(&[
453 Component::new(b"1").unwrap(),
454 Component::new(b"2").unwrap(),
455 ])
456 .unwrap(),
457 times: Range::new_closed(5, 15).unwrap(),
458 });
459
460 assert!(intersection.is_some());
461
462 assert_eq!(
463 intersection.unwrap(),
464 Area {
465 subspace: AreaSubspace::Id(1),
466 path: Path::new_from_slice(&[
467 Component::new(b"1").unwrap(),
468 Component::new(b"2").unwrap(),
469 ])
470 .unwrap(),
471 times: Range::new_closed(5, 10).unwrap(),
472 }
473 )
474 }
475}