1use crate::{
10 empty::Empty,
11 props::Props,
12 str::{Str, ToStr},
13 timestamp::Timestamp,
14 value::{ToValue, Value},
15 well_known::{KEY_TS, KEY_TS_START},
16};
17use core::{fmt, ops::ControlFlow, ops::Range, time::Duration};
18
19#[derive(Clone)]
23pub struct Extent {
24 range: Range<Timestamp>,
25 is_range: bool,
26}
27
28impl Extent {
29 pub fn point(ts: Timestamp) -> Self {
33 Extent {
34 range: ts..ts,
35 is_range: false,
36 }
37 }
38
39 pub fn range(ts: Range<Timestamp>) -> Self {
45 Extent {
46 range: ts,
47 is_range: true,
48 }
49 }
50
51 pub fn as_point(&self) -> &Timestamp {
57 &self.range.end
58 }
59
60 pub fn as_range(&self) -> Option<&Range<Timestamp>> {
66 if self.is_range() {
67 Some(&self.range)
68 } else {
69 None
70 }
71 }
72
73 pub fn len(&self) -> Option<Duration> {
79 if self.is_range() {
80 self.range.end.duration_since(self.range.start)
81 } else {
82 None
83 }
84 }
85
86 pub fn is_point(&self) -> bool {
90 !self.is_range()
91 }
92
93 pub fn is_range(&self) -> bool {
97 self.is_range
98 }
99}
100
101impl fmt::Debug for Extent {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 if self.is_range() {
104 fmt::Debug::fmt(&self.range.start, f)?;
105 f.write_str("..")?;
106 fmt::Debug::fmt(&self.range.end, f)
107 } else {
108 fmt::Debug::fmt(&self.range.end, f)
109 }
110 }
111}
112
113impl fmt::Display for Extent {
114 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115 if self.is_range() {
116 fmt::Display::fmt(&self.range.start, f)?;
117 f.write_str("..")?;
118 fmt::Display::fmt(&self.range.end, f)
119 } else {
120 fmt::Display::fmt(&self.range.end, f)
121 }
122 }
123}
124
125pub trait ToExtent {
129 fn to_extent(&self) -> Option<Extent>;
133}
134
135impl<'a, T: ToExtent + ?Sized> ToExtent for &'a T {
136 fn to_extent(&self) -> Option<Extent> {
137 (**self).to_extent()
138 }
139}
140
141impl ToExtent for Empty {
142 fn to_extent(&self) -> Option<Extent> {
143 None
144 }
145}
146
147impl<T: ToExtent> ToExtent for Option<T> {
148 fn to_extent(&self) -> Option<Extent> {
149 self.as_ref().and_then(|ts| ts.to_extent())
150 }
151}
152
153impl ToExtent for Extent {
154 fn to_extent(&self) -> Option<Extent> {
155 Some(self.clone())
156 }
157}
158
159impl ToExtent for Timestamp {
160 fn to_extent(&self) -> Option<Extent> {
161 Some(Extent::point(*self))
162 }
163}
164
165impl ToExtent for Range<Timestamp> {
166 fn to_extent(&self) -> Option<Extent> {
167 Some(Extent::range(self.clone()))
168 }
169}
170
171impl ToExtent for Range<Option<Timestamp>> {
172 fn to_extent(&self) -> Option<Extent> {
173 match (self.start, self.end) {
174 (Some(start), Some(end)) => (start..end).to_extent(),
175 (Some(start), None) => start.to_extent(),
176 (None, Some(end)) => end.to_extent(),
177 (None, None) => None::<Timestamp>.to_extent(),
178 }
179 }
180}
181
182#[cfg(feature = "std")]
183impl ToExtent for std::time::SystemTime {
184 fn to_extent(&self) -> Option<Extent> {
185 Timestamp::from_system_time(*self).to_extent()
186 }
187}
188
189#[cfg(feature = "std")]
190impl ToExtent for Range<std::time::SystemTime> {
191 fn to_extent(&self) -> Option<Extent> {
192 (Timestamp::from_system_time(self.start)..Timestamp::from_system_time(self.end)).to_extent()
193 }
194}
195
196impl Props for Extent {
197 fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
198 &'kv self,
199 mut for_each: F,
200 ) -> ControlFlow<()> {
201 if let Some(range) = self.as_range() {
202 for_each(KEY_TS_START.to_str(), range.start.to_value())?;
203 for_each(KEY_TS.to_str(), range.end.to_value())
204 } else {
205 for_each(KEY_TS.to_str(), self.as_point().to_value())
206 }
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213
214 #[test]
215 fn point() {
216 let ts = Extent::point(Timestamp::MIN);
217
218 assert!(ts.is_point());
219 assert!(!ts.is_range());
220
221 assert_eq!(&Timestamp::MIN, ts.as_point());
222
223 assert_eq!(None, ts.as_range());
224 assert_eq!(None, ts.len());
225 }
226
227 #[test]
228 fn range() {
229 let ts = Extent::range(Timestamp::MIN..Timestamp::MIN + Duration::from_secs(1));
230
231 assert!(!ts.is_point());
232 assert!(ts.is_range());
233
234 assert_eq!(&(Timestamp::MIN + Duration::from_secs(1)), ts.as_point());
235
236 assert_eq!(
237 &(Timestamp::MIN..Timestamp::MIN + Duration::from_secs(1)),
238 ts.as_range().unwrap(),
239 );
240 assert_eq!(
241 Some(&(Timestamp::MIN..Timestamp::MIN + Duration::from_secs(1))),
242 ts.as_range()
243 );
244 assert_eq!(Some(Duration::from_secs(1)), ts.len());
245 }
246
247 #[test]
248 fn range_empty() {
249 let ts = Extent::range(Timestamp::MIN..Timestamp::MIN);
250
251 assert!(!ts.is_point());
252 assert!(ts.is_range());
253
254 assert_eq!(&Timestamp::MIN, ts.as_point());
255
256 assert_eq!(Some(Duration::from_secs(0)), ts.len());
257 }
258
259 #[test]
260 fn range_backwards() {
261 let ts = Extent::range(Timestamp::MAX..Timestamp::MIN);
262
263 assert!(!ts.is_point());
264 assert!(ts.is_range());
265
266 assert_eq!(&Timestamp::MIN, ts.as_point());
267
268 assert!(ts.len().is_none());
269 }
270
271 #[test]
272 #[cfg(all(
273 feature = "std",
274 not(all(
275 target_arch = "wasm32",
276 target_vendor = "unknown",
277 target_os = "unknown"
278 ))
279 ))]
280 fn point_system_time() {
281 let ts_sys = std::time::SystemTime::UNIX_EPOCH + Duration::from_secs(30);
282
283 let ts = ts_sys.to_extent().unwrap();
284
285 assert_eq!(ts_sys, ts.as_point().to_system_time());
286 }
287
288 #[test]
289 #[cfg(all(
290 feature = "std",
291 not(all(
292 target_arch = "wasm32",
293 target_vendor = "unknown",
294 target_os = "unknown"
295 ))
296 ))]
297 fn range_system_time() {
298 let ts_sys_start = std::time::SystemTime::UNIX_EPOCH + Duration::from_secs(0);
299 let ts_sys_end = std::time::SystemTime::UNIX_EPOCH + Duration::from_secs(30);
300
301 let ts = (ts_sys_start..ts_sys_end).to_extent().unwrap();
302 let ts_range = ts.as_range().unwrap();
303
304 assert_eq!(
305 ts_sys_start..ts_sys_end,
306 ts_range.start.to_system_time()..ts_range.end.to_system_time()
307 );
308 }
309
310 #[test]
311 fn as_props() {
312 let ts = Extent::point(Timestamp::MIN);
313
314 assert_eq!(Timestamp::MIN, ts.pull::<Timestamp, _>("ts").unwrap());
315
316 let ts = Extent::range(Timestamp::MIN..Timestamp::MAX);
317
318 assert_eq!(Timestamp::MAX, ts.pull::<Timestamp, _>("ts").unwrap());
319 assert_eq!(Timestamp::MIN, ts.pull::<Timestamp, _>("ts_start").unwrap());
320 }
321}