use crate::{Dt, Scale};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "js", derive(tsify::Tsify))]
#[derive(Clone, Debug)]
pub struct Every {
pub(crate) start: Dt,
pub(crate) step: Dt,
}
impl Dt {
#[inline]
pub const fn every(self, step: Dt) -> Every {
Every { start: self, step }
}
#[inline]
pub const fn range(self, end: Dt, step: Dt) -> TimeRange {
TimeRange::exclusive(self, end, step)
}
#[inline]
pub const fn every_sec(self) -> Every {
self.every(Dt::from_sec(1, Scale::TAI))
}
#[inline]
pub const fn every_min(self) -> Every {
self.every(Dt::from_min(1, Scale::TAI))
}
#[inline]
pub const fn every_hr(self) -> Every {
self.every(Dt::from_hr(1, Scale::TAI))
}
#[inline]
pub const fn every_day(self) -> Every {
self.every(Dt::from_hr(24, Scale::TAI))
}
#[inline]
pub fn next_n(self, n: usize, step: Dt) -> impl ExactSizeIterator<Item = Dt> {
(self + step).for_n_steps(n, step)
}
#[inline]
pub fn for_n_steps(self, n: usize, step: Dt) -> impl ExactSizeIterator<Item = Dt> {
let end = self + step * (n as i64);
TimeRange::exclusive(self, end, step).take(n)
}
}
impl Every {
#[inline]
pub fn to_including(self, end: Dt) -> TimeRange {
TimeRange::new(self.start, end, self.step, true)
}
#[inline]
pub fn to_excluding(self, end: Dt) -> TimeRange {
TimeRange::new(self.start, end, self.step, false)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "js", derive(tsify::Tsify))]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct TimeRange {
pub(crate) start: Dt,
pub(crate) current: Dt,
pub(crate) end: Dt,
pub(crate) step: Dt,
pub(crate) inclusive: bool,
pub(crate) finished: bool,
}
impl TimeRange {
#[inline]
pub const fn inclusive(start: Dt, end: Dt, step: Dt) -> Self {
Self::new(start, end, step, true)
}
#[inline]
pub const fn exclusive(start: Dt, end: Dt, step: Dt) -> Self {
Self::new(start, end, step, false)
}
#[inline]
pub const fn new(start: Dt, end: Dt, step: Dt, inclusive: bool) -> Self {
Self {
start,
current: start,
end,
step,
inclusive,
finished: false,
}
}
}
impl Iterator for TimeRange {
type Item = Dt;
fn next(&mut self) -> Option<Self::Item> {
if self.finished {
return None;
}
if self.step.is_zero() {
self.finished = true;
if self.start == self.end && self.inclusive {
return Some(self.start);
}
return None;
}
let item = self.current;
let to_end = self.current.to_diff_raw(self.end);
let step_positive = self.step.is_positive();
let beyond_end = if step_positive {
to_end > Dt::ZERO
} else {
to_end < Dt::ZERO
};
if beyond_end {
self.finished = true;
return None;
}
if !self.inclusive && self.current == self.end {
self.finished = true;
return None;
}
self.current += self.step;
Some(item)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
}
impl DoubleEndedIterator for TimeRange {
fn next_back(&mut self) -> Option<Self::Item> {
if self.finished {
return None;
}
let mut rev = *self;
rev.step = rev.step.neg();
let item = rev.next();
if item.is_some() {
self.current = rev.current;
}
item
}
}
impl ExactSizeIterator for TimeRange {
fn len(&self) -> usize {
if self.finished {
return 0;
}
if self.step.is_zero() {
return if self.current == self.end && self.inclusive {
1
} else {
0
};
}
let to_end = self.current.to_diff_raw(self.end);
let step_positive = self.step.is_positive();
let beyond_end = if step_positive {
to_end > Dt::ZERO
} else {
to_end < Dt::ZERO
};
if beyond_end {
return 0;
}
if !self.inclusive && self.current == self.end {
return 0;
}
let diff = self.end.to_diff_raw(self.current);
let intervals = diff.abs_div_floor(self.step);
if self.inclusive {
intervals.saturating_add(1)
} else {
if intervals == 0 {
1
} else {
let reached = self.current + (self.step * (intervals as i64));
if reached == self.end {
intervals
} else {
intervals.saturating_add(1)
}
}
}
}
}