1#[derive(Debug)]
3pub struct Config<Schedule, Tz> {
4 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 pub const fn new(schedule: Schedule, timezone: Timezone) -> Self {
22 Self { schedule, timezone }
23 }
24
25 pub fn schedule(&self) -> &Schedule {
27 &self.schedule
28 }
29
30 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}