#![doc = include_str!("../README.md")]
#![no_std]
#![warn(missing_docs)]
#![warn(clippy::pedantic)]
#![allow(clippy::must_use_candidate, clippy::struct_field_names)]
extern crate alloc;
mod error;
mod event;
pub mod pattern;
mod range;
pub mod series;
use core::ops::{Bound, Range, RangeBounds, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive};
pub use error::Error;
pub use event::Event;
use jiff::civil::{Date, DateTime, time};
use jiff::{ToSpan, Zoned};
use pattern::Combined;
pub use range::DateTimeRange;
#[doc(inline)]
pub use series::Series;
mod private {
pub trait Sealed {}
}
pub trait Pattern: private::Sealed + Clone {
fn next_after(&self, instant: DateTime, range: DateTimeRange) -> Option<DateTime>;
fn previous_before(&self, instant: DateTime, range: DateTimeRange) -> Option<DateTime>;
fn closest_to(&self, instant: DateTime, range: DateTimeRange) -> Option<DateTime>;
}
pub trait Combine: Pattern + Sized {
#[must_use]
fn and<P: Pattern>(self, other: P) -> Combined<Self, P> {
Combined::new(self, other)
}
}
impl<T: Pattern> Combine for T {}
pub trait ToSeries {
fn to_series<P: Pattern>(&self, pattern: P) -> Result<Series<P>, Error>;
}
impl ToSeries for Event {
fn to_series<P: Pattern>(&self, pattern: P) -> Result<Series<P>, Error> {
Series::builder(self.start().., pattern)
.event_duration(self.duration())
.build()
}
}
impl ToSeries for DateTime {
fn to_series<P: Pattern>(&self, pattern: P) -> Result<Series<P>, Error> {
Series::try_new(*self.., pattern)
}
}
impl ToSeries for Date {
fn to_series<P: Pattern>(&self, pattern: P) -> Result<Series<P>, Error> {
self.to_datetime(time(0, 0, 0, 0)).to_series(pattern)
}
}
impl ToSeries for Zoned {
fn to_series<P: Pattern>(&self, pattern: P) -> Result<Series<P>, Error> {
self.datetime().to_series(pattern)
}
}
macro_rules! impl_range_to_series {
($($(#[doc = $doc:expr])+ $ty:ty,)+) => {
$(
impl ToSeries for $ty {
/// Converts a `
#[doc = stringify!($ty)]
$(#[doc = $doc])*
fn to_series<P: Pattern>(&self, pattern: P) -> Result<Series<P>, Error> {
Series::try_new(self.clone(), pattern)
}
}
)*
};
}
impl_range_to_series!(
Range<DateTime>,
RangeFrom<DateTime>,
RangeInclusive<DateTime>,
RangeTo<DateTime>,
RangeToInclusive<DateTime>,
(Bound<DateTime>, Bound<DateTime>),
);
trait IntoBounds<T> {
fn into_bounds(self) -> (Bound<T>, Bound<T>);
}
impl<B: RangeBounds<T>, T: Clone> IntoBounds<T> for B {
fn into_bounds(self) -> (Bound<T>, Bound<T>) {
(self.start_bound().cloned(), self.end_bound().cloned())
}
}
fn try_simplify_range<B: RangeBounds<DateTime>>(bounds: B) -> Result<DateTimeRange, Error> {
let start = match bounds.start_bound() {
Bound::Unbounded => DateTime::MIN,
Bound::Included(start) => *start,
Bound::Excluded(start) => start.checked_add(1.nanosecond())?,
};
let end = match bounds.end_bound() {
Bound::Unbounded => DateTime::MAX,
Bound::Included(end) => end.checked_add(1.nanosecond())?,
Bound::Excluded(end) => *end,
};
Ok(DateTimeRange::new(start, end))
}