1use schemars::JsonSchema;
8use serde::Deserialize;
9
10use super::de::{Located, Loose, Map, UnknownKeys};
11
12type JsonMap = serde_json::Map<String, serde_json::Value>;
13type Raw = Option<Located<serde_json::Value>>;
15
16#[derive(Deserialize, JsonSchema)]
18#[serde(transparent)]
19#[schemars(title = "DRE Sets")]
20pub struct SetsFile(pub Map<Loose<Option<SetConfig>>>);
21
22#[derive(Deserialize, JsonSchema)]
24#[schemars(rename = "set", deny_unknown_fields)]
25pub struct SetConfig {
26 pub profile: Option<Loose<String>>,
28 #[serde(default)]
30 #[schemars(schema_with = "any_map")]
31 pub vars: Option<Loose<JsonMap>>,
32 pub locale: Option<Loose<String>>,
34}
35
36#[derive(Deserialize, JsonSchema)]
38#[serde(transparent)]
39#[schemars(title = "DRE timings", extend("propertyNames" = {"pattern": "^[A-Za-z_][A-Za-z0-9_]*$"}))]
40pub struct TimingsFile(pub Map<Loose<TimingConfig>>);
41
42#[derive(Deserialize, JsonSchema)]
44#[schemars(rename = "timing", deny_unknown_fields)]
45pub struct TimingConfig {
46 #[serde(default)]
48 #[schemars(with = "String")]
49 pub cron: Raw,
50 #[serde(default)]
52 #[schemars(with = "Every")]
53 pub every: Raw,
54 #[serde(default)]
56 #[schemars(with = "String")]
57 pub rrule: Raw,
58 #[serde(default)]
60 #[schemars(with = "String", regex(pattern = "^[0-9]{4}-[0-9]{2}-[0-9]{2}$"))]
61 pub starting: Raw,
62 #[serde(default)]
64 #[schemars(with = "String", regex(pattern = "^[0-9]{2}:[0-9]{2}$"))]
65 pub at: Raw,
66 #[serde(default)]
68 #[schemars(with = "Vec<Date>")]
69 pub except: Raw,
70 #[serde(default)]
72 #[schemars(with = "Vec<Date>")]
73 pub also: Raw,
74 pub timezone: Option<Loose<String>>,
76 #[serde(rename = "$unknown", default)]
77 #[schemars(skip)]
78 pub unknown: UnknownKeys,
79}
80
81impl TimingConfig {
82 pub fn block(&self) -> JsonMap {
84 block([
85 ("cron", &self.cron),
86 ("every", &self.every),
87 ("rrule", &self.rrule),
88 ("starting", &self.starting),
89 ("at", &self.at),
90 ("except", &self.except),
91 ("also", &self.also),
92 ])
93 }
94}
95
96#[derive(Deserialize, JsonSchema)]
98#[serde(transparent)]
99#[schemars(title = "DRE schedules")]
100pub struct SchedulesFile(pub Vec<Located<Loose<ScheduleConfig>>>);
101
102#[derive(Deserialize, JsonSchema)]
104#[schemars(rename = "schedule", deny_unknown_fields)]
105pub struct ScheduleConfig {
106 #[schemars(required, regex(pattern = "^[A-Za-z_][A-Za-z0-9_]*$"))]
108 pub name: Option<Loose<String>>,
109 pub report: Option<Loose<String>>,
111 pub set: Option<Loose<String>>,
113 pub select: Option<Loose<String>>,
115 #[serde(default)]
117 #[schemars(schema_with = "any_map")]
118 pub vars: Option<Loose<JsonMap>>,
119 #[schemars(regex(pattern = "^[A-Za-z_][A-Za-z0-9_]*$"))]
121 pub timing: Option<Loose<String>>,
122 pub timezone: Option<Located<Loose<String>>>,
124 pub enabled: Option<Loose<bool>>,
126 #[serde(default)]
128 #[schemars(with = "String")]
129 pub cron: Raw,
130 #[serde(default)]
132 #[schemars(with = "Every")]
133 pub every: Raw,
134 #[serde(default)]
136 #[schemars(with = "String")]
137 pub rrule: Raw,
138 #[serde(default)]
140 #[schemars(with = "String", regex(pattern = "^[0-9]{4}-[0-9]{2}-[0-9]{2}$"))]
141 pub starting: Raw,
142 #[serde(default)]
144 #[schemars(with = "String", regex(pattern = "^[0-9]{2}:[0-9]{2}$"))]
145 pub at: Raw,
146 #[serde(default)]
148 #[schemars(with = "Vec<Date>")]
149 pub except: Raw,
150 #[serde(default)]
152 #[schemars(with = "Vec<Date>")]
153 pub also: Raw,
154 #[serde(rename = "$unknown", default)]
155 #[schemars(skip)]
156 pub unknown: UnknownKeys,
157}
158
159impl ScheduleConfig {
160 pub fn timing_keys(&self) -> Vec<&'static str> {
162 let mut keys: Vec<(&'static str, (usize, usize))> = [
163 ("cron", &self.cron),
164 ("every", &self.every),
165 ("rrule", &self.rrule),
166 ("starting", &self.starting),
167 ("at", &self.at),
168 ("except", &self.except),
169 ("also", &self.also),
170 ]
171 .into_iter()
172 .filter_map(|(k, v)| v.as_ref().map(|v| (k, (v.line, v.column))))
173 .collect();
174 if let Some(tz) = &self.timezone {
175 keys.push(("timezone", (tz.line, tz.column)));
176 }
177 keys.sort_by_key(|(_, at)| *at);
178 keys.into_iter().map(|(k, _)| k).collect()
179 }
180
181 pub fn block(&self) -> JsonMap {
183 block([
184 ("cron", &self.cron),
185 ("every", &self.every),
186 ("rrule", &self.rrule),
187 ("starting", &self.starting),
188 ("at", &self.at),
189 ("except", &self.except),
190 ("also", &self.also),
191 ])
192 }
193}
194
195fn block<const N: usize>(keys: [(&str, &Raw); N]) -> JsonMap {
197 let mut written: Vec<(&str, &Located<serde_json::Value>)> = keys
198 .into_iter()
199 .filter_map(|(k, v)| Some((k, v.as_ref()?)))
200 .collect();
201 written.sort_by_key(|(_, v)| (v.line, v.column));
202 written
203 .into_iter()
204 .map(|(k, v)| (k.to_string(), v.value.clone()))
205 .collect()
206}
207
208#[derive(JsonSchema)]
210#[schemars(deny_unknown_fields, inline, extend("minProperties" = 1, "maxProperties" = 1))]
211#[allow(dead_code)]
212struct Every {
213 #[schemars(range(min = 1))]
215 days: Option<u32>,
216 #[schemars(range(min = 1))]
218 weeks: Option<u32>,
219 #[schemars(range(min = 1))]
221 months: Option<u32>,
222}
223
224#[derive(JsonSchema)]
226#[schemars(inline)]
227#[allow(dead_code)]
228struct Date(#[schemars(regex(pattern = "^[0-9]{4}-[0-9]{2}-[0-9]{2}$"))] String);
229
230fn any_map(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
231 schemars::json_schema!({"type": "object", "additionalProperties": true})
232}