1#[cfg(feature = "dev")]
2use arbitrary::{size_hint::and_all, Arbitrary};
3
4use crate::{
5 entry::{Entry, Timestamp},
6 parameters::{NamespaceId, PayloadDigest, SubspaceId},
7 path::Path,
8};
9
10use super::range::Range;
11
12#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
14pub enum AreaSubspace<S: SubspaceId> {
15 Any,
17 Id(S),
19}
20
21impl<S: SubspaceId> AreaSubspace<S> {
22 pub fn includes(&self, sub: &S) -> bool {
24 match self {
25 AreaSubspace::Any => true,
26 AreaSubspace::Id(id) => sub == id,
27 }
28 }
29
30 fn intersection(&self, other: &Self) -> Option<Self> {
32 match (self, other) {
33 (Self::Any, Self::Any) => Some(Self::Any),
34 (Self::Id(a), Self::Any) => Some(Self::Id(a.clone())),
35 (Self::Any, Self::Id(b)) => Some(Self::Id(b.clone())),
36 (Self::Id(a), Self::Id(b)) if a == b => Some(Self::Id(a.clone())),
37 (Self::Id(_a), Self::Id(_b)) => None,
38 }
39 }
40
41 pub fn is_any(&self) -> bool {
43 matches!(self, AreaSubspace::Any)
44 }
45}
46
47#[cfg(feature = "dev")]
48impl<'a, S> Arbitrary<'a> for AreaSubspace<S>
49where
50 S: SubspaceId + Arbitrary<'a>,
51{
52 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
53 let is_any: bool = Arbitrary::arbitrary(u)?;
54
55 if !is_any {
56 let subspace: S = Arbitrary::arbitrary(u)?;
57
58 return Ok(Self::Id(subspace));
59 }
60
61 Ok(Self::Any)
62 }
63}
64
65#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
68pub struct Area<const MCL: usize, const MCC: usize, const MPL: usize, S: SubspaceId> {
69 subspace: AreaSubspace<S>,
71 path: Path<MCL, MCC, MPL>,
73 times: Range<Timestamp>,
75}
76
77impl<const MCL: usize, const MCC: usize, const MPL: usize, S: SubspaceId> Area<MCL, MCC, MPL, S> {
78 pub fn new(
80 subspace: AreaSubspace<S>,
81 path: Path<MCL, MCC, MPL>,
82 times: Range<Timestamp>,
83 ) -> Self {
84 Area {
85 subspace,
86 path,
87 times,
88 }
89 }
90
91 pub fn subspace(&self) -> &AreaSubspace<S> {
95 &self.subspace
96 }
97
98 pub fn path(&self) -> &Path<MCL, MCC, MPL> {
102 &self.path
103 }
104
105 pub fn times(&self) -> &Range<Timestamp> {
109 &self.times
110 }
111
112 pub fn new_full() -> Self {
115 Self {
116 subspace: AreaSubspace::Any,
117 path: Path::new_empty(),
118 times: Range::new_open(0),
119 }
120 }
121
122 pub fn new_subspace(sub: S) -> Self {
125 Self {
126 subspace: AreaSubspace::Id(sub),
127 path: Path::new_empty(),
128 times: Range::new_open(0),
129 }
130 }
131
132 pub fn includes_entry<N: NamespaceId, PD: PayloadDigest>(
134 &self,
135 entry: &Entry<MCL, MCC, MPL, N, S, PD>,
136 ) -> bool {
137 self.subspace.includes(entry.subspace_id())
138 && self.path.is_prefix_of(entry.path())
139 && self.times.includes(&entry.timestamp())
140 }
141
142 pub fn includes_area(&self, area: &Self) -> bool {
144 match (&self.subspace, &area.subspace) {
145 (AreaSubspace::Any, AreaSubspace::Any) => {
146 self.path.is_prefix_of(&area.path) && self.times.includes_range(&area.times)
147 }
148 (AreaSubspace::Any, AreaSubspace::Id(_)) => {
149 self.path.is_prefix_of(&area.path) && self.times.includes_range(&area.times)
150 }
151 (AreaSubspace::Id(_), AreaSubspace::Any) => false,
152 (AreaSubspace::Id(subspace_a), AreaSubspace::Id(subspace_b)) => {
153 subspace_a == subspace_b
154 && self.path.is_prefix_of(&area.path)
155 && self.times.includes_range(&area.times)
156 }
157 }
158 }
159
160 pub fn intersection(&self, other: &Area<MCL, MCC, MPL, S>) -> Option<Self> {
163 let subspace_id = self.subspace.intersection(&other.subspace)?;
164 let path = if self.path.is_prefix_of(&other.path) {
165 Some(other.path.clone())
166 } else if self.path.is_prefixed_by(&other.path) {
167 Some(self.path.clone())
168 } else {
169 None
170 }?;
171 let times = self.times.intersection(&other.times)?;
172 Some(Self {
173 subspace: subspace_id,
174 times,
175 path,
176 })
177 }
178}
179
180#[cfg(feature = "dev")]
181impl<'a, const MCL: usize, const MCC: usize, const MPL: usize, S> Arbitrary<'a>
182 for Area<MCL, MCC, MPL, S>
183where
184 S: SubspaceId + Arbitrary<'a>,
185{
186 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
187 let subspace: AreaSubspace<S> = Arbitrary::arbitrary(u)?;
188 let path: Path<MCL, MCC, MPL> = Arbitrary::arbitrary(u)?;
189 let times: Range<u64> = Arbitrary::arbitrary(u)?;
190
191 Ok(Self {
192 subspace,
193 path,
194 times,
195 })
196 }
197
198 fn size_hint(depth: usize) -> (usize, Option<usize>) {
199 and_all(&[
200 AreaSubspace::<S>::size_hint(depth),
201 Path::<MCL, MCC, MPL>::size_hint(depth),
202 Range::<u64>::size_hint(depth),
203 ])
204 }
205}
206
207#[cfg(test)]
208mod tests {
209
210 use crate::path::Component;
211
212 use super::*;
213
214 #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Clone)]
215 struct FakeSubspaceId(usize);
216 impl SubspaceId for FakeSubspaceId {
217 fn successor(&self) -> Option<Self> {
218 Some(FakeSubspaceId(self.0 + 1))
219 }
220 }
221
222 const MCL: usize = 8;
223 const MCC: usize = 4;
224 const MPL: usize = 16;
225
226 #[test]
227 fn subspace_area_includes() {
228 assert!(AreaSubspace::<FakeSubspaceId>::Any.includes(&FakeSubspaceId(5)));
229 assert!(AreaSubspace::<FakeSubspaceId>::Id(FakeSubspaceId(5)).includes(&FakeSubspaceId(5)));
230 assert!(!AreaSubspace::<FakeSubspaceId>::Id(FakeSubspaceId(8)).includes(&FakeSubspaceId(5)));
231 }
232
233 #[test]
234 fn subspace_area_intersects() {
235 let any_any_intersection =
237 AreaSubspace::<FakeSubspaceId>::Any.intersection(&AreaSubspace::<FakeSubspaceId>::Any);
238
239 assert!(any_any_intersection.is_some());
240
241 assert!(any_any_intersection.unwrap() == AreaSubspace::<FakeSubspaceId>::Any);
242
243 let any_id_intersection = AreaSubspace::<FakeSubspaceId>::Any
244 .intersection(&AreaSubspace::<FakeSubspaceId>::Id(FakeSubspaceId(6)));
245
246 assert!(any_id_intersection.is_some());
247
248 assert!(
249 any_id_intersection.unwrap() == AreaSubspace::<FakeSubspaceId>::Id(FakeSubspaceId(6))
250 );
251
252 let id_id_intersection = AreaSubspace::<FakeSubspaceId>::Id(FakeSubspaceId(6))
253 .intersection(&AreaSubspace::<FakeSubspaceId>::Id(FakeSubspaceId(6)));
254
255 assert!(id_id_intersection.is_some());
256
257 assert!(
258 id_id_intersection.unwrap() == AreaSubspace::<FakeSubspaceId>::Id(FakeSubspaceId(6))
259 );
260
261 let empty_id_id_intersection = AreaSubspace::<FakeSubspaceId>::Id(FakeSubspaceId(7))
264 .intersection(&AreaSubspace::<FakeSubspaceId>::Id(FakeSubspaceId(6)));
265
266 assert!(empty_id_id_intersection.is_none())
267 }
268
269 #[test]
270 fn area_full() {
271 let full_area = Area::<MCL, MCC, MPL, FakeSubspaceId>::new_full();
272
273 assert_eq!(
274 full_area,
275 Area {
276 subspace: AreaSubspace::Any,
277 path: Path::new_empty(),
278 times: Range::new_open(0)
279 }
280 )
281 }
282
283 #[test]
284 fn area_subspace() {
285 let subspace_area = Area::<MCL, MCC, MPL, FakeSubspaceId>::new_subspace(FakeSubspaceId(7));
286
287 assert_eq!(
288 subspace_area,
289 Area {
290 subspace: AreaSubspace::Id(FakeSubspaceId(7)),
291 path: Path::new_empty(),
292 times: Range::new_open(0)
293 }
294 )
295 }
296
297 #[test]
298 fn area_intersects() {
299 let empty_intersection_subspace = Area::<MCL, MCC, MPL, FakeSubspaceId> {
300 subspace: AreaSubspace::Id(FakeSubspaceId(1)),
301 path: Path::new_empty(),
302 times: Range::new_open(0),
303 }
304 .intersection(&Area {
305 subspace: AreaSubspace::Id(FakeSubspaceId(2)),
306 path: Path::new_empty(),
307 times: Range::new_open(0),
308 });
309
310 assert!(empty_intersection_subspace.is_none());
311
312 let empty_intersection_path = Area::<MCL, MCC, MPL, FakeSubspaceId> {
313 subspace: AreaSubspace::Id(FakeSubspaceId(1)),
314 path: Path::new_from_slice(&[Component::new(b"0").unwrap()]).unwrap(),
315 times: Range::new_open(0),
316 }
317 .intersection(&Area {
318 subspace: AreaSubspace::Id(FakeSubspaceId(1)),
319 path: Path::new_from_slice(&[Component::new(b"1").unwrap()]).unwrap(),
320 times: Range::new_open(0),
321 });
322
323 assert!(empty_intersection_path.is_none());
324
325 let empty_intersection_time = Area::<MCL, MCC, MPL, FakeSubspaceId> {
326 subspace: AreaSubspace::Id(FakeSubspaceId(1)),
327 path: Path::new_empty(),
328 times: Range::new_closed(0, 1).unwrap(),
329 }
330 .intersection(&Area {
331 subspace: AreaSubspace::Id(FakeSubspaceId(1)),
332 path: Path::new_empty(),
333 times: Range::new_closed(2, 3).unwrap(),
334 });
335
336 assert!(empty_intersection_time.is_none());
337
338 let intersection = Area::<MCL, MCC, MPL, FakeSubspaceId> {
339 subspace: AreaSubspace::Any,
340 path: Path::new_from_slice(&[Component::new(b"1").unwrap()]).unwrap(),
341 times: Range::new_closed(0, 10).unwrap(),
342 }
343 .intersection(&Area {
344 subspace: AreaSubspace::Id(FakeSubspaceId(1)),
345 path: Path::new_from_slice(&[
346 Component::new(b"1").unwrap(),
347 Component::new(b"2").unwrap(),
348 ])
349 .unwrap(),
350 times: Range::new_closed(5, 15).unwrap(),
351 });
352
353 assert!(intersection.is_some());
354
355 assert_eq!(
356 intersection.unwrap(),
357 Area {
358 subspace: AreaSubspace::Id(FakeSubspaceId(1)),
359 path: Path::new_from_slice(&[
360 Component::new(b"1").unwrap(),
361 Component::new(b"2").unwrap(),
362 ])
363 .unwrap(),
364 times: Range::new_closed(5, 10).unwrap(),
365 }
366 )
367 }
368}