1#[cfg(feature = "dev")]
2use arbitrary::Arbitrary;
3
4use crate::{
5 entry::{Entry, Timestamp},
6 grouping::{area::Area, range::Range},
7 parameters::{NamespaceId, PayloadDigest, SubspaceId},
8 path::Path,
9};
10
11#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
14pub struct Range3d<const MCL: usize, const MCC: usize, const MPL: usize, S: SubspaceId> {
15 subspaces: Range<S>,
18 paths: Range<Path<MCL, MCC, MPL>>,
21 times: Range<Timestamp>,
24}
25
26impl<const MCL: usize, const MCC: usize, const MPL: usize, S: SubspaceId>
27 Range3d<MCL, MCC, MPL, S>
28{
29 pub fn new(
31 subspaces: Range<S>,
32 paths: Range<Path<MCL, MCC, MPL>>,
33 times: Range<Timestamp>,
34 ) -> Self {
35 Range3d {
36 subspaces,
37 paths,
38 times,
39 }
40 }
41
42 pub fn subspaces(&self) -> &Range<S> {
45 &self.subspaces
46 }
47
48 pub fn paths(&self) -> &Range<Path<MCL, MCC, MPL>> {
51 &self.paths
52 }
53
54 pub fn times(&self) -> &Range<Timestamp> {
57 &self.times
58 }
59
60 pub fn includes_entry<N: NamespaceId, PD: PayloadDigest>(
62 &self,
63 entry: &Entry<MCL, MCC, MPL, N, S, PD>,
64 ) -> bool {
65 self.subspaces.includes(entry.subspace_id())
66 && self.paths.includes(entry.path())
67 && self.times.includes(&entry.timestamp())
68 }
69
70 pub fn intersection(&self, other: &Range3d<MCL, MCC, MPL, S>) -> Option<Self> {
72 let paths = self.paths.intersection(&other.paths)?;
73 let times = self.times.intersection(&other.times)?;
74 let subspaces = self.subspaces.intersection(&other.subspaces)?;
75 Some(Self {
76 paths,
77 times,
78 subspaces,
79 })
80 }
81}
82
83impl<const MCL: usize, const MCC: usize, const MPL: usize, S: SubspaceId> Default
84 for Range3d<MCL, MCC, MPL, S>
85{
86 fn default() -> Self {
88 Self {
89 subspaces: Range::<S>::default(),
90 paths: Range::new_open(Path::new_empty()),
91 times: Range::new_open(0),
92 }
93 }
94}
95
96impl<const MCL: usize, const MCC: usize, const MPL: usize, S: SubspaceId>
97 From<Area<MCL, MCC, MPL, S>> for Range3d<MCL, MCC, MPL, S>
98{
99 fn from(_value: Area<MCL, MCC, MPL, S>) -> Self {
100 todo!("Need to add successor fns to SubspaceId and Paths first.")
101 }
102}
103
104#[cfg(feature = "dev")]
105impl<'a, const MCL: usize, const MCC: usize, const MPL: usize, S> Arbitrary<'a>
106 for Range3d<MCL, MCC, MPL, S>
107where
108 S: SubspaceId + Arbitrary<'a>,
109{
110 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
111 Ok(Self {
112 subspaces: Arbitrary::arbitrary(u)?,
113 paths: Arbitrary::arbitrary(u)?,
114 times: Arbitrary::arbitrary(u)?,
115 })
116 }
117}
118
119#[cfg(test)]
120mod tests {
121
122 use crate::path::Component;
123
124 use super::*;
125
126 #[derive(Default, PartialEq, Eq, Clone)]
127 struct FakeNamespaceId(usize);
128 impl NamespaceId for FakeNamespaceId {}
129
130 #[derive(Default, PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
131 struct FakeSubspaceId(usize);
132 impl SubspaceId for FakeSubspaceId {
133 fn successor(&self) -> Option<Self> {
134 Some(FakeSubspaceId(self.0 + 1))
135 }
136 }
137
138 #[derive(Default, PartialEq, Eq, PartialOrd, Ord, Clone)]
139 struct FakePayloadDigest(usize);
140 impl PayloadDigest for FakePayloadDigest {}
141
142 const MCL: usize = 8;
143 const MCC: usize = 4;
144 const MPL: usize = 16;
145
146 #[test]
147 fn includes_entry() {
148 let entry = Entry::new(
149 FakeNamespaceId::default(),
150 FakeSubspaceId(10),
151 Path::<MCL, MCC, MPL>::new_from_slice(&[Component::new(b"a").unwrap()]).unwrap(),
152 500,
153 10,
154 FakePayloadDigest::default(),
155 );
156
157 let range_including = Range3d {
158 subspaces: Range::<FakeSubspaceId>::new_closed(FakeSubspaceId(9), FakeSubspaceId(11))
159 .unwrap(),
160 paths: Range::<Path<MCL, MCC, MPL>>::new_closed(
161 Path::new_from_slice(&[Component::new(b"0").unwrap()]).unwrap(),
162 Path::new_from_slice(&[Component::new(b"b").unwrap()]).unwrap(),
163 )
164 .unwrap(),
165 times: Range::<Timestamp>::new_closed(400, 600).unwrap(),
166 };
167
168 assert!(range_including.includes_entry(&entry));
169
170 let range_subspaces_excluding = Range3d {
171 subspaces: Range::<FakeSubspaceId>::new_closed(FakeSubspaceId(11), FakeSubspaceId(13))
172 .unwrap(),
173 paths: Range::<Path<MCL, MCC, MPL>>::new_closed(
174 Path::new_from_slice(&[Component::new(b"0").unwrap()]).unwrap(),
175 Path::new_from_slice(&[Component::new(b"b").unwrap()]).unwrap(),
176 )
177 .unwrap(),
178 times: Range::<Timestamp>::new_closed(400, 600).unwrap(),
179 };
180
181 assert!(!range_subspaces_excluding.includes_entry(&entry));
182
183 let range_paths_excluding = Range3d {
184 subspaces: Range::<FakeSubspaceId>::new_closed(FakeSubspaceId(9), FakeSubspaceId(11))
185 .unwrap(),
186 paths: Range::<Path<MCL, MCC, MPL>>::new_closed(
187 Path::new_from_slice(&[Component::new(b"0").unwrap()]).unwrap(),
188 Path::new_from_slice(&[Component::new(b"1").unwrap()]).unwrap(),
189 )
190 .unwrap(),
191 times: Range::<Timestamp>::new_closed(400, 600).unwrap(),
192 };
193
194 assert!(!range_paths_excluding.includes_entry(&entry));
195
196 let range_times_excluding = Range3d {
197 subspaces: Range::<FakeSubspaceId>::new_closed(FakeSubspaceId(9), FakeSubspaceId(11))
198 .unwrap(),
199 paths: Range::<Path<MCL, MCC, MPL>>::new_closed(
200 Path::new_from_slice(&[Component::new(b"0").unwrap()]).unwrap(),
201 Path::new_from_slice(&[Component::new(b"b").unwrap()]).unwrap(),
202 )
203 .unwrap(),
204 times: Range::<Timestamp>::new_closed(100, 200).unwrap(),
205 };
206
207 assert!(!range_times_excluding.includes_entry(&entry));
208 }
209
210 #[test]
211 fn intersection() {
212 let empty_intersection_subspace = Range3d {
213 subspaces: Range::<FakeSubspaceId>::new_closed(FakeSubspaceId(11), FakeSubspaceId(13))
214 .unwrap(),
215 paths: Range::<Path<MCL, MCC, MPL>>::new_closed(
216 Path::new_from_slice(&[Component::new(b"0").unwrap()]).unwrap(),
217 Path::new_from_slice(&[Component::new(b"1").unwrap()]).unwrap(),
218 )
219 .unwrap(),
220 times: Range::<Timestamp>::new_closed(0, 100).unwrap(),
221 }
222 .intersection(&Range3d {
223 subspaces: Range::<FakeSubspaceId>::new_closed(FakeSubspaceId(0), FakeSubspaceId(3))
224 .unwrap(),
225 paths: Range::<Path<MCL, MCC, MPL>>::new_closed(
226 Path::new_from_slice(&[Component::new(b"0").unwrap()]).unwrap(),
227 Path::new_from_slice(&[Component::new(b"1").unwrap()]).unwrap(),
228 )
229 .unwrap(),
230 times: Range::<Timestamp>::new_closed(0, 100).unwrap(),
231 });
232
233 assert!(empty_intersection_subspace.is_none());
234
235 let empty_intersection_path = Range3d {
236 subspaces: Range::<FakeSubspaceId>::new_closed(FakeSubspaceId(0), FakeSubspaceId(3))
237 .unwrap(),
238 paths: Range::<Path<MCL, MCC, MPL>>::new_closed(
239 Path::new_from_slice(&[Component::new(b"0").unwrap()]).unwrap(),
240 Path::new_from_slice(&[Component::new(b"1").unwrap()]).unwrap(),
241 )
242 .unwrap(),
243 times: Range::<Timestamp>::new_closed(0, 100).unwrap(),
244 }
245 .intersection(&Range3d {
246 subspaces: Range::<FakeSubspaceId>::new_closed(FakeSubspaceId(0), FakeSubspaceId(3))
247 .unwrap(),
248 paths: Range::<Path<MCL, MCC, MPL>>::new_closed(
249 Path::new_from_slice(&[Component::new(b"4").unwrap()]).unwrap(),
250 Path::new_from_slice(&[Component::new(b"6").unwrap()]).unwrap(),
251 )
252 .unwrap(),
253 times: Range::<Timestamp>::new_closed(0, 100).unwrap(),
254 });
255
256 assert!(empty_intersection_path.is_none());
257
258 let empty_intersection_time = Range3d {
259 subspaces: Range::<FakeSubspaceId>::new_closed(FakeSubspaceId(0), FakeSubspaceId(3))
260 .unwrap(),
261 paths: Range::<Path<MCL, MCC, MPL>>::new_closed(
262 Path::new_from_slice(&[Component::new(b"0").unwrap()]).unwrap(),
263 Path::new_from_slice(&[Component::new(b"1").unwrap()]).unwrap(),
264 )
265 .unwrap(),
266 times: Range::<Timestamp>::new_closed(0, 100).unwrap(),
267 }
268 .intersection(&Range3d {
269 subspaces: Range::<FakeSubspaceId>::new_closed(FakeSubspaceId(0), FakeSubspaceId(3))
270 .unwrap(),
271 paths: Range::<Path<MCL, MCC, MPL>>::new_closed(
272 Path::new_from_slice(&[Component::new(b"0").unwrap()]).unwrap(),
273 Path::new_from_slice(&[Component::new(b"1").unwrap()]).unwrap(),
274 )
275 .unwrap(),
276 times: Range::<Timestamp>::new_closed(200, 300).unwrap(),
277 });
278
279 assert!(empty_intersection_time.is_none());
280
281 let intersection = Range3d {
282 subspaces: Range::<FakeSubspaceId>::new_closed(FakeSubspaceId(0), FakeSubspaceId(3))
283 .unwrap(),
284 paths: Range::<Path<MCL, MCC, MPL>>::new_closed(
285 Path::new_from_slice(&[Component::new(b"0").unwrap()]).unwrap(),
286 Path::new_from_slice(&[Component::new(b"6").unwrap()]).unwrap(),
287 )
288 .unwrap(),
289 times: Range::<Timestamp>::new_closed(0, 100).unwrap(),
290 }
291 .intersection(&Range3d {
292 subspaces: Range::<FakeSubspaceId>::new_closed(FakeSubspaceId(2), FakeSubspaceId(4))
293 .unwrap(),
294 paths: Range::<Path<MCL, MCC, MPL>>::new_closed(
295 Path::new_from_slice(&[Component::new(b"3").unwrap()]).unwrap(),
296 Path::new_from_slice(&[Component::new(b"9").unwrap()]).unwrap(),
297 )
298 .unwrap(),
299 times: Range::<Timestamp>::new_closed(50, 150).unwrap(),
300 });
301
302 assert!(intersection.is_some());
303
304 assert_eq!(
305 intersection.unwrap(),
306 Range3d {
307 subspaces: Range::<FakeSubspaceId>::new_closed(
308 FakeSubspaceId(2),
309 FakeSubspaceId(3)
310 )
311 .unwrap(),
312 paths: Range::<Path<MCL, MCC, MPL>>::new_closed(
313 Path::new_from_slice(&[Component::new(b"3").unwrap()]).unwrap(),
314 Path::new_from_slice(&[Component::new(b"6").unwrap()]).unwrap(),
315 )
316 .unwrap(),
317 times: Range::<Timestamp>::new_closed(50, 100).unwrap(),
318 }
319 )
320 }
321}