1use crate::deserialize::{
4 DeserializeBoolLenient, DeserializeNumberLenient, SerializeDateRange, SerializeTimeRange,
5};
6use serde::{
7 de::{self, MapAccess, Visitor},
8 ser::SerializeStruct,
9 Deserialize, Deserializer, Serialize, Serializer,
10};
11use serde_inline_default::serde_inline_default;
12use serde_json::Value;
13use serde_repr::{Deserialize_repr, Serialize_repr};
14use serde_with::{serde_as, skip_serializing_none};
15use std::{collections::HashMap, fmt};
16use time::{PrimitiveDateTime, Time};
17
18include!(concat!(env!("OUT_DIR"), "/timezones.rs"));
19
20#[serde_inline_default]
21#[skip_serializing_none]
22#[serde_as]
23#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
24pub struct MaintenanceMonitor {
25 #[serde(rename = "id")]
26 pub id: Option<i32>,
27
28 #[serde(rename = "pathName")]
29 pub path_name: Option<String>,
30}
31
32#[serde_inline_default]
33#[skip_serializing_none]
34#[serde_as]
35#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
36pub struct MaintenanceStatusPage {
37 #[serde(rename = "id")]
38 pub id: Option<i32>,
39
40 #[serde(rename = "name")]
41 pub name: Option<String>,
42}
43
44#[derive(Clone, Serialize_repr, Deserialize_repr, PartialEq, Debug)]
45#[repr(u8)]
46pub enum Weekday {
47 Monday = 1,
48 Tuesday = 2,
49 Wednesday = 3,
50 Thursday = 4,
51 Friday = 5,
52 Saturday = 6,
53 Sunday = 0,
54}
55
56#[derive(PartialEq, Eq, Clone, Debug)]
57pub enum DayOfMonth {
58 Day(u8),
59 LastDay,
60}
61
62impl<'de> Deserialize<'de> for DayOfMonth {
63 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
64 where
65 D: Deserializer<'de>,
66 {
67 let value: Value = Deserialize::deserialize(deserializer)?;
68
69 match value {
70 Value::Number(n) if n.is_u64() => {
71 Ok(DayOfMonth::Day(n.as_u64().unwrap().try_into().unwrap()))
72 }
73 Value::String(s) if s == "lastDay1" => Ok(DayOfMonth::LastDay),
74 _ => Err(serde::de::Error::custom("Invalid DayOfMonth format")),
75 }
76 }
77}
78
79impl Serialize for DayOfMonth {
80 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
81 where
82 S: Serializer,
83 {
84 match self {
85 DayOfMonth::Day(day) => serializer.serialize_u8(*day),
86 DayOfMonth::LastDay => serializer.serialize_str("lastDay1"),
87 }
88 }
89}
90
91#[skip_serializing_none]
92#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
93pub struct TimeSlot {
94 #[serde(rename = "startDate")]
95 pub start_date: Option<String>,
96
97 #[serde(rename = "endDate")]
98 pub end_date: Option<String>,
99}
100
101#[derive(Clone, Debug, PartialEq, Eq)]
102pub enum TimeZoneOption {
103 SameAsServer(Option<TimeZone>),
104 UTC,
105 TimeZone(TimeZone),
106}
107
108impl Serialize for TimeZoneOption {
109 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
110 where
111 S: serde::Serializer,
112 {
113 let (timezone, timezone_option, timezone_offset) = match self {
114 TimeZoneOption::SameAsServer(tz) => (
115 tz.as_ref()
116 .map(|tz| tz.identifier().to_owned())
117 .unwrap_or("UTC".to_owned()),
118 "SAME_AS_SERVER".to_owned(),
119 tz.as_ref()
120 .map(|tz| tz.utc_offset().to_owned())
121 .unwrap_or("+00:00".to_owned()),
122 ),
123 TimeZoneOption::UTC => ("UTC".to_owned(), "UTC".to_owned(), "+00:00".to_owned()),
124 TimeZoneOption::TimeZone(timezone) => (
125 timezone.identifier().to_owned(),
126 timezone.identifier().to_owned(),
127 timezone.utc_offset().to_owned(),
128 ),
129 };
130
131 let mut ser_struct = serializer.serialize_struct("TimeZone", 3)?;
132 ser_struct.serialize_field("timezone", &timezone)?;
133 ser_struct.serialize_field("timezoneOption", &timezone_option)?;
134 ser_struct.serialize_field("timezoneOffset", &timezone_offset)?;
135 ser_struct.end()
136 }
137}
138
139impl<'de> Deserialize<'de> for TimeZoneOption {
140 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
141 where
142 D: Deserializer<'de>,
143 {
144 enum Field {
145 TimeZone,
146 TimeZoneOption,
147 TimeZoneOffset,
148 }
149
150 impl<'de> Deserialize<'de> for Field {
151 fn deserialize<D>(deserializer: D) -> Result<Field, D::Error>
152 where
153 D: Deserializer<'de>,
154 {
155 struct FieldVisitor;
156
157 impl<'de> Visitor<'de> for FieldVisitor {
158 type Value = Field;
159
160 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
161 formatter.write_str("`timezone`, `timezoneOption` or `timezoneOffset`")
162 }
163
164 fn visit_str<E>(self, value: &str) -> Result<Field, E>
165 where
166 E: de::Error,
167 {
168 match value {
169 "timezone" => Ok(Field::TimeZone),
170 "timezoneOption" => Ok(Field::TimeZoneOption),
171 "timezoneOffset" => Ok(Field::TimeZoneOffset),
172 _ => Err(de::Error::unknown_field(value, FIELDS)),
173 }
174 }
175 }
176
177 deserializer.deserialize_identifier(FieldVisitor)
178 }
179 }
180
181 struct TimeZoneOptionVisitor;
182
183 impl<'de> Visitor<'de> for TimeZoneOptionVisitor {
184 type Value = TimeZoneOption;
185
186 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
187 formatter.write_str("struct TimeZoneOption")
188 }
189
190 fn visit_map<V>(self, mut map: V) -> Result<TimeZoneOption, V::Error>
191 where
192 V: MapAccess<'de>,
193 {
194 let mut timezone_identifier: Option<String> = None;
195 let mut timezone_option: Option<String> = None;
196 let mut timezone_offset: Option<String> = None;
197 while let Some(key) = map.next_key()? {
198 match key {
199 Field::TimeZone => {
200 if timezone_identifier.is_some() {
201 return Err(de::Error::duplicate_field("timezone"));
202 }
203 timezone_identifier = Some(map.next_value()?);
204 }
205 Field::TimeZoneOption => {
206 if timezone_option.is_some() {
207 return Err(de::Error::duplicate_field("timezoneOption"));
208 }
209 timezone_option = Some(map.next_value()?);
210 }
211 Field::TimeZoneOffset => {
212 if timezone_offset.is_some() {
213 return Err(de::Error::duplicate_field("timezoneOffset"));
214 }
215 timezone_offset = Some(map.next_value()?);
216 }
217 }
218 }
219 let timezone_identifier =
220 timezone_identifier.ok_or_else(|| de::Error::missing_field("timezone"))?;
221 let timezone_option =
222 timezone_option.ok_or_else(|| de::Error::missing_field("timezoneOption"))?;
223 let _timezone_offset =
224 timezone_offset.ok_or_else(|| de::Error::missing_field("timezoneOffset"))?;
225
226 let timezone = TimeZone::from_str(&timezone_identifier).ok_or_else(|| {
227 de::Error::invalid_value(
228 de::Unexpected::Str(&timezone_identifier),
229 &"a valid timezone identifier",
230 )
231 })?;
232
233 Ok(match timezone_option.as_ref() {
234 "SAME_AS_SERVER" => TimeZoneOption::SameAsServer(Some(timezone)),
235 "UTC" => TimeZoneOption::UTC,
236 _ => TimeZoneOption::TimeZone(timezone),
237 })
238 }
239 }
240
241 const FIELDS: &'static [&'static str] = &["timezone", "timezoneOption", "timezoneOffset"];
242 deserializer.deserialize_struct("TimeZoneOption", FIELDS, TimeZoneOptionVisitor)
243 }
244}
245
246#[derive(Clone, Debug, PartialEq)]
247pub struct Range<T> {
248 pub start: T,
249 pub end: T,
250}
251
252#[serde_inline_default]
253#[skip_serializing_none]
254#[serde_as]
255#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
256pub struct MaintenanceCommon {
257 #[serde(rename = "id")]
258 #[serde_as(as = "Option<DeserializeNumberLenient>")]
259 pub id: Option<i32>,
260
261 #[serde(rename = "title")]
262 pub title: Option<String>,
263
264 #[serde(rename = "description")]
265 pub description: Option<String>,
266
267 #[serde(rename = "active")]
268 #[serde_inline_default(Some(true))]
269 #[serde_as(as = "Option<DeserializeBoolLenient>")]
270 pub active: Option<bool>,
271
272 #[serde(rename = "status")]
273 pub status: Option<String>,
274
275 #[serde(rename = "monitors")]
276 #[serde(default)]
277 pub monitors: Option<Vec<MaintenanceMonitor>>,
278
279 #[serde(rename = "statusPages")]
280 #[serde(default)]
281 pub status_pages: Option<Vec<MaintenanceStatusPage>>,
282}
283crate::default_from_serde!(MaintenanceCommon);
284
285#[serde_inline_default]
286#[skip_serializing_none]
287#[serde_as]
288#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
289pub struct MaintenanceSchedule {
290 #[serde(rename = "dateRange")]
291 #[serde_as(as = "SerializeDateRange")]
292 #[serialize_always]
293 pub date_range: Option<Range<PrimitiveDateTime>>,
294
295 #[serde(rename = "timeRange")]
296 #[serde_as(as = "Option<SerializeTimeRange>")]
297 pub time_range: Option<Range<Time>>,
298
299 #[serde(flatten)]
300 #[serde(rename = "timezone")]
301 pub timezone: Option<TimeZoneOption>,
302}
303crate::default_from_serde!(MaintenanceSchedule);
304
305#[serde_inline_default]
306#[skip_serializing_none]
307#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
308pub struct MaintenanceCron {
309 #[serde(rename = "cron")]
310 #[serde_inline_default(Some("30 3 * * *".to_owned()))]
311 pub cron: Option<String>,
312
313 #[serde(rename = "durationMinutes")]
314 #[serde_inline_default(Some(60.0))]
315 pub duration_minutes: Option<f64>,
316}
317crate::default_from_serde!(MaintenanceCron);
318
319#[serde_inline_default]
320#[skip_serializing_none]
321#[serde_as]
322#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
323pub struct MaintenanceRecurringInterval {
324 #[serde(rename = "intervalDay")]
325 pub interval: Option<u8>,
326
327 #[serde(rename = "timeslotList")]
328 #[serde(default)]
329 pub timeslots: Vec<TimeSlot>,
330}
331crate::default_from_serde!(MaintenanceRecurringInterval);
332
333#[serde_inline_default]
334#[skip_serializing_none]
335#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
336pub struct MaintenanceRecurringWeekday {
337 #[serde(rename = "timeslotList")]
338 #[serde(default)]
339 pub timeslots: Vec<TimeSlot>,
340
341 #[serde(rename = "weekdays")]
342 #[serde(default)]
343 pub weekdays: Vec<Weekday>,
344}
345crate::default_from_serde!(MaintenanceRecurringWeekday);
346
347#[serde_inline_default]
348#[skip_serializing_none]
349#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
350pub struct MaintenanceRecurringDayOfMonth {
351 #[serde(rename = "daysOfMonth")]
352 #[serde(default)]
353 pub days_of_month: Vec<DayOfMonth>,
354
355 #[serde(rename = "timeslotList")]
356 #[serde(default)]
357 pub timeslots: Vec<TimeSlot>,
358}
359crate::default_from_serde!(MaintenanceRecurringDayOfMonth);
360
361#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
362#[serde(tag = "strategy")]
363pub enum Maintenance {
364 #[serde(rename = "manual")]
365 Manual {
366 #[serde(flatten)]
367 common: MaintenanceCommon,
368 },
369
370 #[serde(rename = "single")]
371 Single {
372 #[serde(flatten)]
373 common: MaintenanceCommon,
374 #[serde(flatten)]
375 schedule: MaintenanceSchedule,
376 },
377
378 #[serde(rename = "cron")]
379 Cron {
380 #[serde(flatten)]
381 common: MaintenanceCommon,
382 #[serde(flatten)]
383 schedule: MaintenanceSchedule,
384 #[serde(flatten)]
385 cron: MaintenanceCron,
386 },
387
388 #[serde(rename = "recurring-interval")]
389 RecurringInterval {
390 #[serde(flatten)]
391 common: MaintenanceCommon,
392 #[serde(flatten)]
393 schedule: MaintenanceSchedule,
394 #[serde(flatten)]
395 recurring_interval: MaintenanceRecurringInterval,
396 },
397
398 #[serde(rename = "recurring-weekday")]
399 RecurringWeekday {
400 #[serde(flatten)]
401 common: MaintenanceCommon,
402 #[serde(flatten)]
403 schedule: MaintenanceSchedule,
404 #[serde(flatten)]
405 recurring_weekday: MaintenanceRecurringWeekday,
406 },
407
408 #[serde(rename = "recurring-day-of-month")]
409 RecurringDayOfMonth {
410 #[serde(flatten)]
411 common: MaintenanceCommon,
412 #[serde(flatten)]
413 schedule: MaintenanceSchedule,
414 #[serde(flatten)]
415 recurring_day_of_month: MaintenanceRecurringDayOfMonth,
416 },
417}
418
419impl Maintenance {
420 pub fn common(&self) -> &MaintenanceCommon {
421 match self {
422 Maintenance::Manual { common } => common,
423 Maintenance::Single { common, .. } => common,
424 Maintenance::Cron { common, .. } => common,
425 Maintenance::RecurringInterval { common, .. } => common,
426 Maintenance::RecurringWeekday { common, .. } => common,
427 Maintenance::RecurringDayOfMonth { common, .. } => common,
428 }
429 }
430 pub fn common_mut(&mut self) -> &mut MaintenanceCommon {
431 match self {
432 Maintenance::Manual { common } => common,
433 Maintenance::Single { common, .. } => common,
434 Maintenance::Cron { common, .. } => common,
435 Maintenance::RecurringInterval { common, .. } => common,
436 Maintenance::RecurringWeekday { common, .. } => common,
437 Maintenance::RecurringDayOfMonth { common, .. } => common,
438 }
439 }
440}
441
442pub type MaintenanceList = HashMap<String, Maintenance>;