1pub const DATETIME_FORMAT: &str = "%FT%T%:z";
5
6pub const DATE_FORMAT: &str = "%F";
8
9pub mod mbta_datetime_format {
11 use chrono::{DateTime, FixedOffset};
12 use serde::{Deserialize, Deserializer, Serializer};
13
14 use super::DATETIME_FORMAT;
15
16 pub fn serialize<S: Serializer>(datetime: &DateTime<FixedOffset>, serializer: S) -> Result<S::Ok, S::Error> {
23 serializer.serialize_str(&format!("{}", datetime.format(DATETIME_FORMAT)))
24 }
25
26 pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<DateTime<FixedOffset>, D::Error> {
32 let s = String::deserialize(deserializer)?;
33 DateTime::parse_from_str(&s, DATETIME_FORMAT).map_err(serde::de::Error::custom)
34 }
35
36 #[cfg(test)]
37 mod tests {
38 use super::*;
39
40 use rstest::*;
41 use serde_json::{Deserializer, Serializer};
42
43 #[fixture]
44 fn serializer() -> Serializer<Vec<u8>> {
45 Serializer::new(Vec::new())
46 }
47
48 #[rstest]
49 #[case::simple_case(
50 DateTime::parse_from_str("2022-05-08T13:18:08-04:00", "%FT%T%:z").expect("invalid input"),
51 "\"2022-05-08T13:18:08-04:00\"",
52 )]
53 fn test_serialize(mut serializer: Serializer<Vec<u8>>, #[case] input: DateTime<FixedOffset>, #[case] expected: &str) {
54 serialize(&input, &mut serializer).expect("failed to serialize");
58 let inner = serializer.into_inner();
59 let actual = std::str::from_utf8(&inner).expect("failed to convert to string");
60
61 assert_eq!(actual, expected);
63 }
64
65 #[rstest]
66 #[case::valid_format(
67 "\"2022-05-08T13:18:08-04:00\"",
68 DateTime::parse_from_str("2022-05-08T13:18:08-04:00", "%FT%T%:z").expect("invalid input"),
69 )]
70 #[should_panic = "failed to deserialize"]
71 #[case::invalid_format(
72 "\"2022-05-08 13:18:08-04:00\"",
73 DateTime::parse_from_str("2022-05-08T13:18:08-04:00", "%FT%T%:z").expect("invalid input"),
74 )]
75 fn test_deserialize(#[case] input: &str, #[case] expected: DateTime<FixedOffset>) {
76 let mut deserializer = Deserializer::from_str(input);
78
79 let actual = deserialize(&mut deserializer).expect("failed to deserialize");
81
82 assert_eq!(actual, expected);
84 }
85 }
86}
87
88pub mod optional_mbta_datetime_format {
90 use chrono::{DateTime, FixedOffset};
91 use serde::{Deserialize, Deserializer, Serializer};
92
93 use super::{mbta_datetime_format::serialize as datetime_serialize, DATETIME_FORMAT};
94
95 pub fn serialize<S>(datetime: &Option<DateTime<FixedOffset>>, serializer: S) -> Result<S::Ok, S::Error>
102 where
103 S: Serializer,
104 {
105 match datetime {
106 Some(d) => datetime_serialize(d, serializer),
107 None => serializer.serialize_none(),
108 }
109 }
110
111 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<DateTime<FixedOffset>>, D::Error>
117 where
118 D: Deserializer<'de>,
119 {
120 let s = Option::<String>::deserialize(deserializer)?;
121 match s {
122 Some(s) => {
123 let date = DateTime::parse_from_str(&s, DATETIME_FORMAT).map_err(serde::de::Error::custom)?;
124 Ok(Some(date))
125 }
126 None => Ok(None),
127 }
128 }
129
130 #[cfg(test)]
131 mod tests {
132 use super::*;
133
134 use rstest::*;
135 use serde_json::{Deserializer, Serializer};
136
137 #[fixture]
138 fn serializer() -> Serializer<Vec<u8>> {
139 Serializer::new(Vec::new())
140 }
141
142 #[rstest]
143 #[case::some_dateime(
144 Some(DateTime::parse_from_str("2022-05-08T13:18:08-04:00", "%FT%T%:z").expect("invalid input")),
145 "\"2022-05-08T13:18:08-04:00\"",
146 )]
147 #[case::no_datetime(None, "null")]
148 fn test_serialize(mut serializer: Serializer<Vec<u8>>, #[case] input: Option<DateTime<FixedOffset>>, #[case] expected: &str) {
149 serialize(&input, &mut serializer).expect("failed to serialize");
153 let inner = serializer.into_inner();
154 let actual = std::str::from_utf8(&inner).expect("failed to convert to string");
155
156 assert_eq!(actual, expected);
158 }
159
160 #[rstest]
161 #[case::valid_format(
162 "\"2022-05-08T13:18:08-04:00\"",
163 Some(DateTime::parse_from_str("2022-05-08T13:18:08-04:00", "%FT%T%:z").expect("invalid input")),
164 )]
165 #[case::valid_format("null", None)]
166 #[should_panic = "failed to deserialize"]
167 #[case::invalid_format("\"2022-05-08 13:18:08-04:00\"", None)]
168 fn test_deserialize(#[case] input: &str, #[case] expected: Option<DateTime<FixedOffset>>) {
169 let mut deserializer = Deserializer::from_str(input);
171
172 let actual = deserialize(&mut deserializer).expect("failed to deserialize");
174
175 assert_eq!(actual, expected);
177 }
178 }
179}
180
181pub mod mbta_date_format {
183 use chrono::{Date, DateTime, FixedOffset};
184 use serde::{Deserialize, Deserializer, Serializer};
185
186 use super::{DATETIME_FORMAT, DATE_FORMAT};
187
188 pub fn serialize<S: Serializer>(date: &Date<FixedOffset>, serializer: S) -> Result<S::Ok, S::Error> {
195 serializer.serialize_str(&format!("{}", date.format(DATE_FORMAT)))
196 }
197
198 pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Date<FixedOffset>, D::Error> {
204 let s = format!("{}T00:00:00-04:00", String::deserialize(deserializer)?);
205 DateTime::parse_from_str(&s, DATETIME_FORMAT).map(|dt| dt.date()).map_err(serde::de::Error::custom)
206 }
207
208 #[cfg(test)]
209 mod tests {
210 use super::*;
211
212 use chrono::{Date, DateTime, FixedOffset};
213 use rstest::*;
214 use serde_json::{Deserializer, Serializer};
215
216 #[fixture]
217 fn serializer() -> Serializer<Vec<u8>> {
218 Serializer::new(Vec::new())
219 }
220
221 #[rstest]
222 #[case::simple_case(
223 DateTime::parse_from_str("2022-05-08T13:18:08-04:00", "%FT%T%:z").expect("invalid input").date(),
224 "\"2022-05-08\""
225 )]
226 fn test_serialize(mut serializer: Serializer<Vec<u8>>, #[case] input: Date<FixedOffset>, #[case] expected: &str) {
227 serialize(&input, &mut serializer).expect("failed to serialize");
231 let inner = serializer.into_inner();
232 let actual = std::str::from_utf8(&inner).expect("failed to convert to string");
233
234 assert_eq!(actual, expected);
236 }
237
238 #[rstest]
239 #[case::valid_format(
240 "\"2022-05-08\"",
241 DateTime::parse_from_str("2022-05-08T13:18:08-04:00", "%FT%T%:z").expect("invalid input").date(),
242 )]
243 #[should_panic = "failed to deserialize"]
244 #[case::invalid_format(
245 "\"2022 05 08\"",
246 DateTime::parse_from_str("2022-05-08T13:18:08-04:00", "%FT%T%:z").expect("invalid input").date(),
247 )]
248 fn test_deserialize(#[case] input: &str, #[case] expected: Date<FixedOffset>) {
249 let mut deserializer = Deserializer::from_str(input);
251
252 let actual = deserialize(&mut deserializer).expect("failed to deserialize");
254
255 assert_eq!(actual, expected);
257 }
258 }
259}
260
261pub mod optional_mbta_date_format {
263 use chrono::{Date, DateTime, FixedOffset};
264 use serde::{Deserialize, Deserializer, Serializer};
265
266 use super::{mbta_date_format::serialize as date_serialize, DATETIME_FORMAT};
267
268 pub fn serialize<S>(date: &Option<Date<FixedOffset>>, serializer: S) -> Result<S::Ok, S::Error>
275 where
276 S: Serializer,
277 {
278 match date {
279 Some(d) => date_serialize(d, serializer),
280 None => serializer.serialize_none(),
281 }
282 }
283
284 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Date<FixedOffset>>, D::Error>
290 where
291 D: Deserializer<'de>,
292 {
293 let s = Option::<String>::deserialize(deserializer)?;
294 match s {
295 Some(s) => {
296 let date = DateTime::parse_from_str(&format!("{}T00:00:00-04:00", s), DATETIME_FORMAT)
297 .map(|dt| dt.date())
298 .map_err(serde::de::Error::custom)?;
299 Ok(Some(date))
300 }
301 None => Ok(None),
302 }
303 }
304
305 #[cfg(test)]
306 mod tests {
307 use super::*;
308
309 use chrono::{Date, DateTime, FixedOffset};
310 use rstest::*;
311 use serde_json::{Deserializer, Serializer};
312
313 #[fixture]
314 fn serializer() -> Serializer<Vec<u8>> {
315 Serializer::new(Vec::new())
316 }
317
318 #[rstest]
319 #[case::some_date(
320 Some(DateTime::parse_from_str("2022-05-08T13:18:08-04:00", "%FT%T%:z").expect("invalid input").date()),
321 "\"2022-05-08\"",
322 )]
323 #[case::no_date(None, "null")]
324 fn test_serialize(mut serializer: Serializer<Vec<u8>>, #[case] input: Option<Date<FixedOffset>>, #[case] expected: &str) {
325 serialize(&input, &mut serializer).expect("failed to serialize");
329 let inner = serializer.into_inner();
330 let actual = std::str::from_utf8(&inner).expect("failed to convert to string");
331
332 assert_eq!(actual, expected);
334 }
335
336 #[rstest]
337 #[case::valid_format(
338 "\"2022-05-08\"",
339 Some(DateTime::parse_from_str("2022-05-08T13:18:08-04:00", "%FT%T%:z").expect("invalid input").date()),
340 )]
341 #[case::no_date("null", None)]
342 #[should_panic = "failed to deserialize"]
343 #[case::invalid_format("\"2022 05 08\"", None)]
344 fn test_deserialize(#[case] input: &str, #[case] expected: Option<Date<FixedOffset>>) {
345 let mut deserializer = Deserializer::from_str(input);
347
348 let actual = deserialize(&mut deserializer).expect("failed to deserialize");
350
351 assert_eq!(actual, expected);
353 }
354 }
355}
356
357pub mod vec_mbta_date_format {
359 use chrono::{Date, DateTime, FixedOffset};
360 use serde::{Deserialize, Deserializer, Serializer};
361
362 use super::{DATETIME_FORMAT, DATE_FORMAT};
363
364 pub fn serialize<S>(dates: &[Date<FixedOffset>], serializer: S) -> Result<S::Ok, S::Error>
371 where
372 S: Serializer,
373 {
374 serializer.collect_seq(dates.iter().map(|dt| format!("{}", dt.format(DATE_FORMAT))))
375 }
376
377 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Date<FixedOffset>>, D::Error>
383 where
384 D: Deserializer<'de>,
385 {
386 let v = Vec::<String>::deserialize(deserializer)?;
387 let mut dates = Vec::new();
388 for dt in v {
389 dates.push(
390 DateTime::parse_from_str(&format!("{}T00:00:00-04:00", dt), DATETIME_FORMAT)
391 .map(|dt| dt.date())
392 .map_err(serde::de::Error::custom)?,
393 )
394 }
395 Ok(dates)
396 }
397
398 #[cfg(test)]
399 mod tests {
400 use super::*;
401
402 use chrono::{Date, DateTime, FixedOffset};
403 use rstest::*;
404 use serde_json::{Deserializer, Serializer};
405
406 #[fixture]
407 fn serializer() -> Serializer<Vec<u8>> {
408 Serializer::new(Vec::new())
409 }
410
411 #[rstest]
412 #[case::some_dates(
413 vec![DateTime::parse_from_str("2022-05-08T13:18:08-04:00", "%FT%T%:z").expect("invalid input").date()],
414 "[\"2022-05-08\"]",
415 )]
416 #[case::no_dates(vec![], "[]")]
417 fn test_serialize(mut serializer: Serializer<Vec<u8>>, #[case] input: Vec<Date<FixedOffset>>, #[case] expected: &str) {
418 serialize(&input, &mut serializer).expect("failed to serialize");
422 let inner = serializer.into_inner();
423 let actual = std::str::from_utf8(&inner).expect("failed to convert to string");
424
425 assert_eq!(actual, expected);
427 }
428
429 #[rstest]
430 #[case::valid_format(
431 "[\"2022-05-08\"]",
432 vec![DateTime::parse_from_str("2022-05-08T13:18:08-04:00", "%FT%T%:z").expect("invalid input").date()],
433 )]
434 #[case::no_dates("[]", vec![])]
435 #[should_panic = "failed to deserialize"]
436 #[case::invalid_format("[\"2022 05 08\"]", vec![])]
437 fn test_deserialize(#[case] input: &str, #[case] expected: Vec<Date<FixedOffset>>) {
438 let mut deserializer = Deserializer::from_str(input);
440
441 let actual = deserialize(&mut deserializer).expect("failed to deserialize");
443
444 assert_eq!(actual, expected);
446 }
447 }
448}