use crate::Pattern;
use crate::error::{Error, err};
use crate::series::Series;
use core::ops::RangeBounds;
use jiff::{
Zoned,
civil::{Date, DateTime},
};
#[derive(Debug, Clone, Copy, Default)]
pub enum SplitMode {
#[default]
At,
NextAfter,
PreviousBefore,
ClosestTo,
}
#[derive(Debug, Clone, Copy)]
pub struct SeriesSplit {
instant: DateTime,
mode: SplitMode,
}
impl SeriesSplit {
#[inline]
pub fn new(instant: DateTime) -> SeriesSplit {
SeriesSplit {
instant,
mode: SplitMode::default(),
}
}
#[must_use]
pub fn mode(mut self, mode: SplitMode) -> SeriesSplit {
self.mode = mode;
self
}
pub(crate) fn split_off<P: Pattern>(self, series: &mut Series<P>) -> Result<Series<P>, Error> {
let cutoff_point = self.find_cutoff_point(series)?;
let left = series.with().end(cutoff_point).build()?;
let right = series
.with()
.start(cutoff_point)
.fixpoint(series.fixpoint())
.build()?;
*series = left;
Ok(right)
}
fn find_cutoff_point<P: Pattern>(&self, series: &Series<P>) -> Result<DateTime, Error> {
match self.mode {
SplitMode::At => {
if series.range.contains(&self.instant) {
Ok(self.instant)
} else {
Err(err!("{} not within series range", self.instant))
}
}
SplitMode::NextAfter => series
.pattern()
.next_after(self.instant, series.range)
.ok_or_else(|| err!("no series event after {}", self.instant)),
SplitMode::PreviousBefore => series
.pattern()
.previous_before(self.instant, series.range)
.ok_or_else(|| err!("no series event before {}", self.instant)),
SplitMode::ClosestTo => series
.pattern()
.closest_to(self.instant, series.range)
.ok_or_else(|| err!("no series event close to {}", self.instant)),
}
}
}
impl From<DateTime> for SeriesSplit {
fn from(instant: DateTime) -> Self {
SeriesSplit::new(instant)
}
}
impl From<Date> for SeriesSplit {
fn from(date: Date) -> Self {
SeriesSplit::new(date.into())
}
}
impl From<Zoned> for SeriesSplit {
fn from(zoned: Zoned) -> Self {
SeriesSplit::new(zoned.datetime())
}
}
impl From<&Zoned> for SeriesSplit {
fn from(zoned: &Zoned) -> Self {
SeriesSplit::new(zoned.datetime())
}
}
impl<T: Into<SeriesSplit>> From<(SplitMode, T)> for SeriesSplit {
fn from((mode, split): (SplitMode, T)) -> Self {
let split: SeriesSplit = split.into();
split.mode(mode)
}
}