use crate::{empty::Empty, timestamp::Timestamp};
use core::{fmt, ops::Range, time::Duration};
#[derive(Clone)]
pub struct Extent {
range: Range<Timestamp>,
is_span: bool,
}
impl Extent {
pub fn point(ts: Timestamp) -> Self {
Extent {
range: ts..ts,
is_span: false,
}
}
pub fn span(ts: Range<Timestamp>) -> Self {
Extent {
range: ts,
is_span: true,
}
}
pub fn as_range(&self) -> &Range<Timestamp> {
&self.range
}
pub fn as_point(&self) -> &Timestamp {
&self.range.end
}
pub fn as_span(&self) -> Option<&Range<Timestamp>> {
if self.is_span() {
Some(&self.range)
} else {
None
}
}
pub fn len(&self) -> Option<Duration> {
if self.is_span() {
self.range.end.duration_since(self.range.start)
} else {
None
}
}
pub fn is_point(&self) -> bool {
!self.is_span()
}
pub fn is_span(&self) -> bool {
self.is_span
}
}
impl fmt::Debug for Extent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_span() {
fmt::Debug::fmt(&self.range.start, f)?;
f.write_str("..")?;
fmt::Debug::fmt(&self.range.end, f)
} else {
fmt::Debug::fmt(&self.range.end, f)
}
}
}
impl fmt::Display for Extent {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.is_span() {
fmt::Display::fmt(&self.range.start, f)?;
f.write_str("..")?;
fmt::Display::fmt(&self.range.end, f)
} else {
fmt::Display::fmt(&self.range.end, f)
}
}
}
pub trait ToExtent {
fn to_extent(&self) -> Option<Extent>;
}
impl<'a, T: ToExtent + ?Sized> ToExtent for &'a T {
fn to_extent(&self) -> Option<Extent> {
(**self).to_extent()
}
}
impl ToExtent for Empty {
fn to_extent(&self) -> Option<Extent> {
None
}
}
impl<T: ToExtent> ToExtent for Option<T> {
fn to_extent(&self) -> Option<Extent> {
self.as_ref().and_then(|ts| ts.to_extent())
}
}
impl ToExtent for Extent {
fn to_extent(&self) -> Option<Extent> {
Some(self.clone())
}
}
impl ToExtent for Timestamp {
fn to_extent(&self) -> Option<Extent> {
Some(Extent::point(*self))
}
}
impl ToExtent for Range<Timestamp> {
fn to_extent(&self) -> Option<Extent> {
Some(Extent::span(self.clone()))
}
}
impl ToExtent for Range<Option<Timestamp>> {
fn to_extent(&self) -> Option<Extent> {
match (self.start, self.end) {
(Some(start), Some(end)) => (start..end).to_extent(),
(Some(start), None) => start.to_extent(),
(None, Some(end)) => end.to_extent(),
(None, None) => None::<Timestamp>.to_extent(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn point() {
let ts = Extent::point(Timestamp::MIN);
assert!(ts.is_point());
assert!(!ts.is_span());
assert_eq!(&Timestamp::MIN, ts.as_point());
assert_eq!(&(Timestamp::MIN..Timestamp::MIN), ts.as_range());
assert_eq!(None, ts.as_span());
assert_eq!(None, ts.len());
}
#[test]
fn span() {
let ts = Extent::span(Timestamp::MIN..Timestamp::MIN + Duration::from_secs(1));
assert!(!ts.is_point());
assert!(ts.is_span());
assert_eq!(&(Timestamp::MIN + Duration::from_secs(1)), ts.as_point());
assert_eq!(
&(Timestamp::MIN..Timestamp::MIN + Duration::from_secs(1)),
ts.as_range()
);
assert_eq!(
Some(&(Timestamp::MIN..Timestamp::MIN + Duration::from_secs(1))),
ts.as_span()
);
assert_eq!(Some(Duration::from_secs(1)), ts.len());
}
#[test]
fn span_empty() {
let ts = Extent::span(Timestamp::MIN..Timestamp::MIN);
assert!(!ts.is_point());
assert!(ts.is_span());
assert_eq!(&Timestamp::MIN, ts.as_point());
assert_eq!(Some(Duration::from_secs(0)), ts.len());
}
#[test]
fn span_backwards() {
let ts = Extent::span(Timestamp::MAX..Timestamp::MIN);
assert!(!ts.is_point());
assert!(ts.is_span());
assert_eq!(&Timestamp::MIN, ts.as_point());
assert!(ts.len().is_none());
}
}