bronzeflow_time/
schedule_time.rs

1use crate::prelude::ScheduleExpr;
2use bronzeflow_utils::{debug, BronzeError};
3use chrono::{DateTime, Duration, Local, Utc};
4use cron::ScheduleIterator;
5use std::cmp::Ordering;
6use std::iter::Take;
7use std::str::FromStr;
8
9type InternalDateTime = DateTime<Utc>;
10
11#[allow(dead_code)]
12const DEFAULT_DATETIME_FORMAT: &str = "%Y-%m-%d %H:%M:%S %z";
13
14#[derive(Debug, Clone)]
15pub struct ScheduleTime {
16    pub(crate) dt: InternalDateTime,
17}
18
19#[derive(Debug, Clone)]
20pub struct ScheduleTimeHolder {
21    pub(crate) expr: ScheduleExpr,
22    pub(crate) min_interval: Option<Duration>,
23    pub(crate) last_run: Option<ScheduleTime>,
24    pub(crate) next_run: Option<ScheduleTime>,
25}
26
27pub trait ScheduleTimeOp {
28    fn last_run(&self) -> Option<ScheduleTime>;
29
30    fn next_run(&self) -> Option<ScheduleTime>;
31
32    fn set_last_run(&mut self, t: &ScheduleTime) -> &mut Self;
33
34    fn set_next_run(&mut self, t: &ScheduleTime) -> &mut Self;
35}
36
37impl ScheduleTime {
38    pub fn new(dt: InternalDateTime) -> Self {
39        ScheduleTime { dt }
40    }
41    pub fn from_now() -> Self {
42        let local_time = Local::now();
43        let utc_time = DateTime::<Utc>::from_utc(local_time.naive_utc(), Utc);
44        ScheduleTime::new(utc_time)
45    }
46}
47
48impl From<InternalDateTime> for ScheduleTime {
49    fn from(dt: InternalDateTime) -> Self {
50        ScheduleTime::new(dt)
51    }
52}
53
54impl FromStr for ScheduleTime {
55    type Err = BronzeError;
56
57    fn from_str(s: &str) -> Result<Self, Self::Err> {
58        InternalDateTime::from_str(s)
59            .map(InternalDateTime::into)
60            .map_err(BronzeError::new)
61    }
62}
63
64impl PartialEq<Self> for ScheduleTime {
65    fn eq(&self, other: &Self) -> bool {
66        self.dt == other.dt
67    }
68}
69
70impl PartialOrd for ScheduleTime {
71    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
72        self.dt.partial_cmp(&other.dt)
73    }
74}
75
76impl ScheduleTimeHolder {
77    pub fn new(expr: ScheduleExpr) -> Self {
78        ScheduleTimeHolder {
79            expr,
80            min_interval: None,
81            last_run: None,
82            next_run: None,
83        }
84    }
85
86    pub fn init(&mut self) {
87        let now = ScheduleTime::from_now();
88        match self.expr.clone() {
89            ScheduleExpr::Cron(c) => {
90                let mut times = c.after(&now.dt).take(21);
91                debug!("Now: {}", now.dt);
92                let next_run = times.next().unwrap();
93
94                // Get the minimum time interval from the 20 schedule times
95                let min_interval: Duration = times
96                    .collect::<Vec<InternalDateTime>>()
97                    .windows(2)
98                    .map(|x| x[1] - x[0])
99                    .min()
100                    .unwrap();
101                debug!("min interval is: {}", min_interval.num_seconds());
102                self.min_interval = Some(min_interval);
103                self.next_run = Some(ScheduleTime::from(next_run));
104            },
105            _ => panic!(),
106        }
107    }
108
109    fn get_next_times(
110        &self,
111        n: usize,
112        from: &InternalDateTime,
113    ) -> Option<Take<ScheduleIterator<'_, Utc>>> {
114        match &self.expr {
115            ScheduleExpr::Cron(c) => Some(c.after(from).take(n)),
116            ScheduleExpr::Preset(_) => None,
117        }
118    }
119
120    pub fn cmp_and_to_next(&mut self, from: &ScheduleTime) -> bool {
121        if let Some(ref next_time) = self.next_run {
122            if from.dt >= next_time.dt {
123                debug!("from: {}, next_run: {}", from.dt, &next_time.dt);
124                let copy_next_run = next_time.clone();
125                self.last_run = Some(copy_next_run);
126                if let Some(mut it) = self.get_next_times(1, &next_time.dt) {
127                    let new_time = it.next().unwrap();
128                    debug!("set new time {}", new_time);
129                    self.next_run = Some(ScheduleTime::from(new_time));
130                    return true;
131                }
132            }
133        }
134        false
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use crate::prelude::ScheduleExpr;
141    use crate::schedule_time::ScheduleTimeHolder;
142    use std::str::FromStr;
143
144    #[test]
145    fn test_time_holder() {
146        let expr = ScheduleExpr::from_str("@daily").unwrap();
147
148        let mut s = ScheduleTimeHolder::new(expr);
149        s.init()
150    }
151}