use crate::error::Error;
use core::fmt;
use jiff::{Span, civil::DateTime};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Event {
start: DateTime,
end: Option<DateTime>,
}
impl Event {
#[inline]
pub fn at(instant: DateTime) -> Event {
Event::new_unchecked(instant, None)
}
#[inline]
pub fn new(start: DateTime, end: DateTime) -> Event {
Event::try_new(start, end).expect("invalid event end")
}
#[inline]
pub fn try_new(start: DateTime, end: DateTime) -> Result<Event, Error> {
if start >= end {
return Err(Error::datetime_range("event", start..end));
}
Ok(Event::new_unchecked(start, Some(end)))
}
#[inline]
pub(crate) fn new_unchecked(start: DateTime, end: Option<DateTime>) -> Event {
Event { start, end }
}
#[inline]
pub fn start(&self) -> DateTime {
self.start
}
#[inline]
pub fn end(&self) -> Option<DateTime> {
self.end
}
#[inline]
pub fn duration(&self) -> Span {
self.end
.and_then(|end| self.start.until(end).ok())
.unwrap_or_default()
}
#[inline]
pub fn contains(&self, instant: DateTime) -> bool {
if let Some(end) = self.end {
instant >= self.start && instant < end
} else {
instant == self.start
}
}
}
impl fmt::Display for Event {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.start.fmt(f)?;
if let Some(end) = &self.end {
f.write_str(" - ")?;
end.fmt(f)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::ToString;
use jiff::civil::date;
use pretty_assertions::assert_eq;
#[test]
fn event_end_before_start() {
let start = date(2025, 1, 2).at(0, 0, 0, 0);
let end = date(2025, 1, 1).at(0, 0, 0, 0);
assert!(Event::try_new(start, end).is_err());
}
#[test]
fn event_display() {
assert_eq!(
Event::at(date(2025, 1, 1).at(0, 0, 0, 0)).to_string(),
"2025-01-01T00:00:00"
);
assert_eq!(
Event::new(
date(2025, 1, 1).at(0, 0, 0, 0),
date(2025, 1, 1).at(12, 0, 0, 0)
)
.to_string(),
"2025-01-01T00:00:00 - 2025-01-01T12:00:00"
);
}
}