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 {
start: Dt,
step: Dt,
}
impl Dt {
#[inline]
pub const fn every(self, step: Dt) -> Every {
Every { start: self, step }
}
#[inline]
pub const fn range_to(self, end: Dt, step: Dt) -> TimeRange {
TimeRange::inclusive(self, end, step)
}
#[inline]
pub const fn range_until(self, end: Dt, step: Dt) -> TimeRange {
TimeRange::exclusive(self, end, step)
}
#[inline]
pub const fn every_second(self) -> Every {
self.every(Dt::from_sec(1, Scale::TAI))
}
#[inline]
pub const fn every_minute(self) -> Every {
self.every(Dt::from_min(1, Scale::TAI))
}
#[inline]
pub const fn every_hour(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 Iterator<Item = Dt> {
(self + step).for_n_steps(n, step)
}
#[inline]
pub fn for_n_steps(self, n: usize, step: Dt) -> impl Iterator<Item = Dt> {
let end = self + step * (n as i64);
TimeRange::exclusive(self, end, step).take(n)
}
}
impl Every {
#[inline]
pub fn until(self, end: Dt) -> TimeRange {
TimeRange::new(self.start, end, self.step, true)
}
#[inline]
pub fn up_to(self, end: Dt) -> TimeRange {
TimeRange::new(self.start, end, self.step, false)
}
#[inline]
pub fn down_to(self, end: Dt) -> TimeRange {
TimeRange::new(self.start, end, self.step, true)
}
}
#[cfg(feature = "wire")]
impl Every {
pub const WIRE_SIZE: usize = Dt::WIRE_SIZE + Dt::WIRE_SIZE;
pub fn to_wire_bytes(&self) -> [u8; Self::WIRE_SIZE] {
let mut buf = [0u8; Self::WIRE_SIZE];
let start = self.start.to_wire_bytes();
let step = self.step.to_wire_bytes();
buf[0..17].copy_from_slice(&start);
buf[17..33].copy_from_slice(&step);
buf
}
pub fn from_wire_bytes(bytes: &[u8]) -> Option<Self> {
if bytes.len() != Self::WIRE_SIZE {
return None;
}
let start = Dt::from_wire_bytes(&bytes[0..17])?;
let step = Dt::from_wire_bytes(&bytes[17..33])?;
Some(Self { start, step })
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "js", derive(tsify::Tsify))]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct TimeRange {
start: Dt,
current: Dt,
end: Dt,
step: Dt,
inclusive: bool,
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]
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;
}
let item = self.current;
let to_end = self.current.to_diff_raw(self.end);
let passed = if self.step.is_zero() {
true
} else if self.step.sec > 0 || (self.step.sec == 0 && self.step.attos > 0) {
to_end > Dt::ZERO
} else {
to_end < Dt::ZERO
};
if passed {
self.finished = true;
if self.inclusive && self.current == self.end {
return Some(item);
}
return None;
}
self.current = self.current + self.step;
Some(item)
}
}
impl DoubleEndedIterator for TimeRange {
fn next_back(&mut self) -> Option<Self::Item> {
if self.finished {
return None;
}
let mut rev = self.clone();
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.step.is_zero() {
return if self.start == self.end && self.inclusive {
1
} else {
0
};
}
let total = self.end.to_diff_raw(self.start);
let steps = total.abs_div_floor(self.step);
if self.inclusive {
steps.saturating_add(1)
} else {
steps
}
}
}
#[cfg(feature = "wire")]
impl TimeRange {
pub const WIRE_VERSION: u8 = 1;
pub const WIRE_SIZE: usize = 1 + 2 * Dt::WIRE_SIZE + Dt::WIRE_SIZE + 1;
pub fn to_wire_bytes(&self) -> [u8; Self::WIRE_SIZE] {
let mut buf = [0u8; Self::WIRE_SIZE];
buf[0] = Self::WIRE_VERSION;
let start = self.start.to_wire_bytes();
let end = self.end.to_wire_bytes();
let step = self.step.to_wire_bytes();
let tp_size = Dt::WIRE_SIZE;
let span_size = Dt::WIRE_SIZE;
buf[1..1 + tp_size].copy_from_slice(&start);
buf[1 + tp_size..1 + 2 * tp_size].copy_from_slice(&end);
buf[1 + 2 * tp_size..1 + 2 * tp_size + span_size].copy_from_slice(&step);
buf[1 + 2 * tp_size + span_size] = if self.inclusive { 1 } else { 0 };
buf
}
pub fn from_wire_bytes(bytes: &[u8]) -> Option<Self> {
if bytes.len() != Self::WIRE_SIZE {
return None;
}
if bytes[0] != Self::WIRE_VERSION {
return None;
}
let tp_size = Dt::WIRE_SIZE;
let span_size = Dt::WIRE_SIZE;
let start = Dt::from_wire_bytes(&bytes[1..1 + tp_size])?;
let end = Dt::from_wire_bytes(&bytes[1 + tp_size..1 + 2 * tp_size])?;
let step = Dt::from_wire_bytes(&bytes[1 + 2 * tp_size..1 + 2 * tp_size + span_size])?;
let inclusive = bytes[1 + 2 * tp_size + span_size] != 0;
Some(Self::new(start, end, step, inclusive))
}
}