use crate::pattern::Interval;
use crate::{DateTimeRange, Error, Pattern, private};
use jiff::{
ToSpan,
civil::{DateTime, Time},
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Daily {
interval: Interval,
at: Option<Time>,
}
impl Daily {
pub fn new<I: ToSpan>(interval: I) -> Daily {
Daily {
interval: Interval::new(interval.days()),
at: None,
}
}
pub fn try_new<I: ToSpan>(interval: I) -> Result<Daily, Error> {
Ok(Daily {
interval: Interval::try_new(interval.days())?,
at: None,
})
}
#[must_use]
pub fn at<T: Into<Time>>(mut self, time: T) -> Daily {
self.at = Some(time.into());
self
}
fn range_adjusted<F>(&self, f: F, instant: DateTime, range: DateTimeRange) -> Option<DateTime>
where
F: FnOnce(&Interval, DateTime, DateTimeRange) -> Option<DateTime>,
{
let Some(time) = self.at else {
return f(&self.interval, instant, range);
};
let start = if range.start.time() <= time {
range.start
} else {
self.interval.next_after(range.start, range)?
};
let start = start.with().time(time).build().ok()?;
let range = DateTimeRange::from(start..range.end);
f(&self.interval, instant, range)
}
}
impl Pattern for Daily {
fn next_after(&self, instant: DateTime, range: DateTimeRange) -> Option<DateTime> {
self.range_adjusted(Interval::next_after, instant, range)
}
fn previous_before(&self, instant: DateTime, range: DateTimeRange) -> Option<DateTime> {
self.range_adjusted(Interval::previous_before, instant, range)
}
fn closest_to(&self, instant: DateTime, range: DateTimeRange) -> Option<DateTime> {
self.range_adjusted(Interval::closest_to, instant, range)
}
}
impl private::Sealed for Daily {}