Skip to main content

apalis_cron/
config.rs

1/// The config for the cron scheduler
2#[derive(Debug)]
3pub struct Config<Schedule, Tz> {
4    /// The schedule for the cron job
5    pub(super) schedule: Schedule,
6
7    pub(super) timezone: Tz,
8}
9
10impl<Schedule: Clone, Timezone: Clone> Clone for Config<Schedule, Timezone> {
11    fn clone(&self) -> Self {
12        Self {
13            schedule: self.schedule.clone(),
14            timezone: self.timezone.clone(),
15        }
16    }
17}
18
19impl<Schedule, Timezone> Config<Schedule, Timezone> {
20    /// Creates a new `CronContext` with the given schedule.
21    pub const fn new(schedule: Schedule, timezone: Timezone) -> Self {
22        Self { schedule, timezone }
23    }
24
25    /// Returns a reference to the schedule, if it exists.
26    pub fn schedule(&self) -> &Schedule {
27        &self.schedule
28    }
29
30    /// Returns the set timestamp
31    pub fn timezone(&self) -> &Timezone {
32        &self.timezone
33    }
34}
35
36#[cfg(feature = "serde")]
37mod encoding {
38    use serde::de::{self, Deserialize, Deserializer, MapAccess, Visitor};
39    use serde::ser::SerializeStruct;
40    use serde::{Serialize, Serializer};
41    use std::fmt::{self, Display};
42    use std::marker::PhantomData;
43    use std::str::FromStr;
44
45    use super::*;
46
47    impl<Schedule, Tz> Serialize for Config<Schedule, Tz>
48    where
49        Schedule: Display,
50        Tz: Display,
51    {
52        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
53        where
54            S: Serializer,
55        {
56            let mut state = serializer.serialize_struct("Config", 2)?;
57
58            state.serialize_field("schedule", &self.schedule.to_string())?;
59            state.serialize_field("timezone", &self.timezone.to_string())?;
60
61            state.end()
62        }
63    }
64
65    impl<'de, Schedule, Tz> Deserialize<'de> for Config<Schedule, Tz>
66    where
67        Schedule: FromStr,
68        Schedule::Err: fmt::Display,
69        Tz: FromStr,
70        Tz::Err: Display,
71    {
72        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
73        where
74            D: Deserializer<'de>,
75        {
76            #[derive(serde::Deserialize)]
77            #[serde(field_identifier, rename_all = "lowercase")]
78            enum Field {
79                Schedule,
80                Timezone,
81            }
82
83            struct ConfigVisitor<Schedule, Tz> {
84                marker: PhantomData<fn() -> Config<Schedule, Tz>>,
85            }
86
87            impl<'de, Schedule, Tz> Visitor<'de> for ConfigVisitor<Schedule, Tz>
88            where
89                Schedule: FromStr,
90                Schedule::Err: fmt::Display,
91                Tz: FromStr,
92                Tz::Err: fmt::Display,
93            {
94                type Value = Config<Schedule, Tz>;
95
96                fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
97                    formatter.write_str("struct Config")
98                }
99
100                fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
101                where
102                    V: MapAccess<'de>,
103                {
104                    let mut schedule: Option<String> = None;
105                    let mut timezone: Option<String> = None;
106
107                    while let Some(key) = map.next_key::<Field>()? {
108                        match key {
109                            Field::Schedule => {
110                                if schedule.is_some() {
111                                    return Err(de::Error::duplicate_field("schedule"));
112                                }
113                                schedule = Some(map.next_value()?);
114                            }
115                            Field::Timezone => {
116                                if timezone.is_some() {
117                                    return Err(de::Error::duplicate_field("timezone"));
118                                }
119                                timezone = Some(map.next_value()?);
120                            }
121                        }
122                    }
123
124                    let schedule = schedule.ok_or_else(|| de::Error::missing_field("schedule"))?;
125                    let timezone = timezone.ok_or_else(|| de::Error::missing_field("timezone"))?;
126
127                    let schedule = Schedule::from_str(&schedule).map_err(de::Error::custom)?;
128                    let timezone = Tz::from_str(&timezone).map_err(de::Error::custom)?;
129
130                    Ok(Config { schedule, timezone })
131                }
132            }
133
134            const FIELDS: &[&str] = &["schedule", "timezone"];
135            deserializer.deserialize_struct(
136                "Config",
137                FIELDS,
138                ConfigVisitor {
139                    marker: PhantomData,
140                },
141            )
142        }
143    }
144}