use std::ops::Deref;
use std::sync::Arc;
use std::sync::Mutex;
use std::vec::IntoIter;
use time::{format_description, Date, OffsetDateTime};
#[derive(Clone, Debug)]
pub enum Frequency {
Second,
Daily,
Fixed,
}
impl From<Frequency> for u8 {
fn from(freq: Frequency) -> Self {
match freq {
Frequency::Second => 0,
Frequency::Daily => 1,
Frequency::Fixed => 3,
}
}
}
impl From<Frequency> for String {
fn from(freq: Frequency) -> Self {
match freq {
Frequency::Second => "SECOND".to_string(),
Frequency::Daily => "DAILY".to_string(),
Frequency::Fixed => "FIXED".to_string(),
}
}
}
pub enum Weekday {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday,
}
impl From<time::Weekday> for Weekday {
fn from(v: time::Weekday) -> Self {
match v {
time::Weekday::Monday => Weekday::Monday,
time::Weekday::Tuesday => Weekday::Tuesday,
time::Weekday::Wednesday => Weekday::Wednesday,
time::Weekday::Thursday => Weekday::Thursday,
time::Weekday::Friday => Weekday::Friday,
time::Weekday::Saturday => Weekday::Saturday,
time::Weekday::Sunday => Weekday::Sunday,
}
}
}
pub enum Month {
January,
February,
March,
April,
May,
June,
July,
August,
September,
October,
November,
December,
}
impl From<time::Month> for Month {
fn from(v: time::Month) -> Self {
match v {
time::Month::January => Month::January,
time::Month::February => Month::February,
time::Month::March => Month::March,
time::Month::April => Month::April,
time::Month::May => Month::May,
time::Month::June => Month::June,
time::Month::July => Month::July,
time::Month::August => Month::August,
time::Month::September => Month::September,
time::Month::October => Month::October,
time::Month::November => Month::November,
time::Month::December => Month::December,
}
}
}
impl From<Month> for u8 {
fn from(v: Month) -> Self {
match v {
Month::January => 1,
Month::February => 2,
Month::March => 3,
Month::April => 4,
Month::May => 5,
Month::June => 6,
Month::July => 7,
Month::August => 8,
Month::September => 9,
Month::October => 10,
Month::November => 11,
Month::December => 12,
}
}
}
#[derive(Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Copy, Ord)]
pub struct DateTime(i64);
impl DateTime {
pub fn day(&self) -> u8 {
let date: OffsetDateTime = (*self).into();
date.day()
}
pub fn weekday(&self) -> Weekday {
let date: OffsetDateTime = (*self).into();
date.weekday().into()
}
pub fn month(&self) -> Month {
let date: OffsetDateTime = (*self).into();
date.month().into()
}
pub fn from_date_string(val: &str, date_fmt: &str) -> Self {
let format = format_description::parse(date_fmt).unwrap();
let parsed_date = Date::parse(val, &format).unwrap();
let parsed_time = parsed_date.with_time(time::macros::time!(09:00));
Self::from(parsed_time.assume_utc().unix_timestamp())
}
}
impl Deref for DateTime {
type Target = i64;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<OffsetDateTime> for DateTime {
fn from(value: OffsetDateTime) -> Self {
value.unix_timestamp().into()
}
}
impl From<DateTime> for OffsetDateTime {
fn from(v: DateTime) -> Self {
if let Ok(date) = OffsetDateTime::from_unix_timestamp(i64::from(v)) {
date
} else {
panic!("Tried to convert non-date value");
}
}
}
impl From<DateTime> for i64 {
fn from(v: DateTime) -> Self {
v.0
}
}
impl From<i64> for DateTime {
fn from(v: i64) -> Self {
DateTime(v)
}
}
#[doc(hidden)]
#[derive(Debug)]
pub struct ClockInner {
pos: usize,
dates: Vec<DateTime>,
}
#[derive(Debug)]
pub struct Clock {
inner: Arc<Mutex<ClockInner>>,
frequency: Frequency,
}
impl Clone for Clock {
fn clone(&self) -> Self {
Clock {
inner: Arc::clone(&self.inner),
frequency: self.frequency.clone(),
}
}
}
unsafe impl Send for Clock {}
impl Clock {
pub fn now(&self) -> DateTime {
let inner = self.inner.lock().unwrap();
*inner.dates.get(inner.pos).unwrap()
}
pub fn has_next(&self) -> bool {
let inner = self.inner.lock().unwrap();
inner.pos < inner.dates.len() - 1
}
pub fn tick(&mut self) {
let mut inner_mut = self.inner.lock().unwrap();
inner_mut.pos += 1;
if inner_mut.pos == inner_mut.dates.len() {
panic!("Client has ticked past the number of dates");
}
}
pub fn peek(&self) -> IntoIter<DateTime> {
let inner = self.inner.lock().unwrap();
inner.dates.clone().into_iter()
}
pub fn len(&self) -> usize {
let inner = self.inner.lock().unwrap();
inner.dates.len()
}
pub fn frequency(&self) -> &Frequency {
&self.frequency
}
pub fn is_empty(&self) -> bool {
let inner = self.inner.lock().unwrap();
inner.dates.is_empty()
}
pub fn from_fixed(dates: Vec<DateTime>) -> Self {
Self {
inner: Arc::new(Mutex::new(ClockInner { dates, pos: 0 })),
frequency: Frequency::Fixed,
}
}
pub fn new(dates: Vec<DateTime>, frequency: Frequency) -> Self {
Self {
inner: Arc::new(Mutex::new(ClockInner { dates, pos: 0 })),
frequency,
}
}
}
pub struct ClockBuilder {
pub start: DateTime,
pub end: DateTime,
pub dates: Vec<DateTime>,
pub frequency: Frequency,
}
impl ClockBuilder {
const SECS_IN_DAY: i64 = 86_400;
pub fn build(self) -> Clock {
Clock::new(self.dates, self.frequency)
}
pub fn with_frequency(&self, freq: &Frequency) -> Self {
match freq {
Frequency::Daily => {
let dates: Vec<DateTime> = (i64::from(self.start)
..i64::from(self.end) + ClockBuilder::SECS_IN_DAY)
.step_by(ClockBuilder::SECS_IN_DAY as usize)
.map(DateTime::from)
.collect();
Self {
start: self.start,
end: self.end,
dates,
frequency: Frequency::Daily,
}
}
Frequency::Second => {
let dates: Vec<DateTime> = (i64::from(self.start)..i64::from(self.end) + 1)
.map(DateTime::from)
.collect();
Self {
start: self.start,
end: self.end,
dates,
frequency: Frequency::Second,
}
}
_ => panic!("Clock frequencies apart from Daily/Second are not supported"),
}
}
pub fn with_length_in_seconds(start: impl Into<DateTime>, length_in_seconds: i64) -> Self {
let start_val = start.into();
let end = DateTime::from(*start_val + length_in_seconds);
Self {
start: start_val,
end,
dates: Vec::new(),
frequency: Frequency::Second,
}
}
pub fn with_length_in_days(start: impl Into<DateTime>, length_in_days: i64) -> Self {
let start_val = start.into();
let end = DateTime::from(*start_val + (length_in_days * ClockBuilder::SECS_IN_DAY));
Self {
start: start_val,
end,
dates: Vec::new(),
frequency: Frequency::Daily,
}
}
}
#[cfg(test)]
mod tests {
use super::{ClockBuilder, Frequency};
#[test]
#[should_panic]
fn test_that_ticking_past_the_length_of_dates_triggers_panic() {
let mut clock = ClockBuilder::with_length_in_seconds(1, 2)
.with_frequency(&Frequency::Second)
.build();
clock.tick();
clock.tick();
clock.tick();
}
#[test]
fn test_that_there_isnt_next_when_tick_at_end() {
let mut clock = ClockBuilder::with_length_in_seconds(1, 2)
.with_frequency(&Frequency::Second)
.build();
assert!(clock.has_next());
clock.tick();
clock.tick();
assert!(!clock.has_next());
}
#[test]
fn test_that_clock_created_from_fixed_peeks_correctly() {
let start = 1;
let clock = ClockBuilder::with_length_in_days(start, 3)
.with_frequency(&Frequency::Daily)
.build();
let mut dates: Vec<i64> = Vec::new();
for date in clock.peek() {
dates.push(i64::from(date));
}
assert!(dates == vec![1, 86401, 172801, 259201]);
}
#[test]
fn test_that_clock_created_from_length_peeks_correctly() {
let clock = ClockBuilder::with_length_in_seconds(1, 10)
.with_frequency(&Frequency::Second)
.build();
let mut count = 0;
for _i in clock.peek() {
count += 1;
}
println!("{:?}", count);
assert!(count == 11);
}
}