celery/beat/schedule/
descriptor.rs

1use super::{CronSchedule, DeltaSchedule, Schedule};
2use crate::error::ScheduleError;
3use serde::{Deserialize, Serialize};
4use std::time::Duration;
5
6#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
7pub enum ScheduleDescriptor {
8    Cron(CronDescriptor),
9    Delta(DeltaDescriptor),
10}
11
12#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
13pub struct CronDescriptor {
14    pub minutes: Vec<u32>,
15    pub hours: Vec<u32>,
16    pub month_days: Vec<u32>,
17    pub months: Vec<u32>,
18    pub week_days: Vec<u32>,
19}
20
21#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
22pub struct DeltaDescriptor {
23    pub interval_millis: u64,
24}
25
26impl ScheduleDescriptor {
27    pub fn from_schedule(schedule: &dyn Schedule) -> Option<Self> {
28        schedule.describe()
29    }
30
31    pub fn delta(interval: Duration) -> Self {
32        ScheduleDescriptor::Delta(DeltaDescriptor {
33            interval_millis: interval.as_millis() as u64,
34        })
35    }
36
37    pub fn cron(descriptor: CronDescriptor) -> Self {
38        ScheduleDescriptor::Cron(descriptor)
39    }
40
41    pub fn to_schedule(&self) -> Result<Box<dyn Schedule>, ScheduleError> {
42        match self {
43            ScheduleDescriptor::Delta(desc) => Ok(Box::new(DeltaSchedule::new(
44                Duration::from_millis(desc.interval_millis),
45            ))),
46            ScheduleDescriptor::Cron(desc) => Ok(Box::new(desc.to_schedule()?)),
47        }
48    }
49}
50
51impl CronDescriptor {
52    pub fn to_schedule(&self) -> Result<CronSchedule<chrono::Utc>, ScheduleError> {
53        CronSchedule::new(
54            self.minutes.clone(),
55            self.hours.clone(),
56            self.month_days.clone(),
57            self.months.clone(),
58            self.week_days.clone(),
59        )
60    }
61}
62
63impl DeltaDescriptor {
64    pub fn interval(&self) -> Duration {
65        Duration::from_millis(self.interval_millis)
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn delta_descriptor_roundtrip() {
75        let schedule = DeltaSchedule::new(Duration::from_millis(250));
76        let descriptor = ScheduleDescriptor::from_schedule(&schedule).expect("descriptor");
77
78        let restored = descriptor.to_schedule().expect("restored");
79        let next_original = schedule.next_call_at(None).unwrap();
80        let next_restored = restored.next_call_at(None).unwrap();
81
82        assert!(
83            next_restored
84                .duration_since(next_original)
85                .unwrap_or_else(|_| Duration::from_secs(0))
86                < Duration::from_millis(2)
87        );
88    }
89
90    #[test]
91    fn cron_descriptor_roundtrip() {
92        let cron = CronSchedule::from_string("*/5 * * * *").expect("cron");
93        let descriptor = ScheduleDescriptor::from_schedule(&cron).expect("descriptor");
94
95        let restored = descriptor.to_schedule().expect("restored");
96        let next_original = cron.next_call_at(None).unwrap();
97        let next_restored = restored.next_call_at(None).unwrap();
98
99        assert!(
100            next_restored
101                .duration_since(next_original)
102                .unwrap_or_else(|_| Duration::from_secs(0))
103                < Duration::from_secs(60)
104        );
105    }
106}