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
182impl Props for Extent {
183 fn for_each<'kv, F: FnMut(Str<'kv>, Value<'kv>) -> ControlFlow<()>>(
184 &'kv self,
185 mut for_each: F,
186 ) -> ControlFlow<()> {
187 if let Some(range) = self.as_range() {
188 for_each(KEY_TS_START.to_str(), range.start.to_value())?;
189 for_each(KEY_TS.to_str(), range.end.to_value())
190 } else {
191 for_each(KEY_TS.to_str(), self.as_point().to_value())
192 }
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199
200 #[test]
201 fn point() {
202 let ts = Extent::point(Timestamp::MIN);
203
204 assert!(ts.is_point());
205 assert!(!ts.is_range());
206
207 assert_eq!(&Timestamp::MIN, ts.as_point());
208
209 assert_eq!(None, ts.as_range());
210 assert_eq!(None, ts.len());
211 }
212
213 #[test]
214 fn range() {
215 let ts = Extent::range(Timestamp::MIN..Timestamp::MIN + Duration::from_secs(1));
216
217 assert!(!ts.is_point());
218 assert!(ts.is_range());
219
220 assert_eq!(&(Timestamp::MIN + Duration::from_secs(1)), ts.as_point());
221
222 assert_eq!(
223 &(Timestamp::MIN..Timestamp::MIN + Duration::from_secs(1)),
224 ts.as_range().unwrap(),
225 );
226 assert_eq!(
227 Some(&(Timestamp::MIN..Timestamp::MIN + Duration::from_secs(1))),
228 ts.as_range()
229 );
230 assert_eq!(Some(Duration::from_secs(1)), ts.len());
231 }
232
233 #[test]
234 fn range_empty() {
235 let ts = Extent::range(Timestamp::MIN..Timestamp::MIN);
236
237 assert!(!ts.is_point());
238 assert!(ts.is_range());
239
240 assert_eq!(&Timestamp::MIN, ts.as_point());
241
242 assert_eq!(Some(Duration::from_secs(0)), ts.len());
243 }
244
245 #[test]
246 fn range_backwards() {
247 let ts = Extent::range(Timestamp::MAX..Timestamp::MIN);
248
249 assert!(!ts.is_point());
250 assert!(ts.is_range());
251
252 assert_eq!(&Timestamp::MIN, ts.as_point());
253
254 assert!(ts.len().is_none());
255 }
256
257 #[test]
258 fn as_props() {
259 let ts = Extent::point(Timestamp::MIN);
260
261 assert_eq!(Timestamp::MIN, ts.pull::<Timestamp, _>("ts").unwrap());
262
263 let ts = Extent::range(Timestamp::MIN..Timestamp::MAX);
264
265 assert_eq!(Timestamp::MAX, ts.pull::<Timestamp, _>("ts").unwrap());
266 assert_eq!(Timestamp::MIN, ts.pull::<Timestamp, _>("ts_start").unwrap());
267 }
268}