septa_api/
deserialize.rs

1use chrono::{NaiveDateTime, NaiveTime};
2use serde::de;
3use std::{collections::HashMap, fmt, str::FromStr};
4
5struct CsvEncodedStringVisitor;
6
7impl<'a> de::Visitor<'a> for CsvEncodedStringVisitor {
8    type Value = Vec<i32>;
9
10    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
11        write!(formatter, "a string of comma-separated integers")
12    }
13
14    fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
15        let mut result = Vec::new();
16
17        for s in v.split(',') {
18            if !s.is_empty() {
19                result.push(s.parse::<i32>().map_err(|e| {
20                    de::Error::custom(format!("invalid integer {}, caused error {}", s, e))
21                })?);
22            }
23        }
24
25        Ok(result)
26    }
27}
28
29pub fn deserialize_option_csv_encoded_string<'a, D: de::Deserializer<'a>>(
30    deserializer: D,
31) -> Result<Option<Vec<i32>>, D::Error> {
32    deserializer.deserialize_option(OptionCsvEncodedStringVisitor)
33}
34
35struct OptionCsvEncodedStringVisitor;
36
37impl<'a> de::Visitor<'a> for OptionCsvEncodedStringVisitor {
38    type Value = Option<Vec<i32>>;
39
40    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
41        write!(formatter, "a string of comma-separated integers or null")
42    }
43
44    fn visit_none<E: de::Error>(self) -> Result<Self::Value, E> {
45        Ok(None)
46    }
47
48    fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
49        Ok(None)
50    }
51
52    fn visit_some<D: de::Deserializer<'a>>(self, d: D) -> Result<Self::Value, D::Error> {
53        Ok(Some(d.deserialize_str(CsvEncodedStringVisitor)?))
54    }
55}
56
57pub fn deserialize_optional_string_enum<'a, D: de::Deserializer<'a>, T: FromStr + 'a>(
58    deserializer: D,
59) -> Result<Option<T>, D::Error> {
60    deserializer.deserialize_option(OptionStringEnumVisitor {
61        _marker: Default::default(),
62    })
63}
64
65#[derive(Default)]
66struct OptionStringEnumVisitor<'a, T: FromStr> {
67    _marker: std::marker::PhantomData<&'a T>,
68}
69
70impl<'a, T: FromStr> de::Visitor<'a> for OptionStringEnumVisitor<'a, T> {
71    type Value = Option<T>;
72
73    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
74        write!(
75            formatter,
76            "a string representation an enum value that can be null"
77        )
78    }
79
80    // Can be called T can be a PhantomData Unit Struct
81    fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
82        Ok(None)
83    }
84
85    fn visit_none<E: de::Error>(self) -> Result<Self::Value, E> {
86        Ok(None)
87    }
88
89    fn visit_some<D: de::Deserializer<'a>>(self, d: D) -> Result<Self::Value, D::Error> {
90        Ok(Some(d.deserialize_str(StringEnumVisitor {
91            _marker: Default::default(),
92        })?))
93    }
94}
95
96pub fn deserialize_string_enum<'a, D: de::Deserializer<'a>, T: FromStr + 'a>(
97    deserializer: D,
98) -> Result<T, D::Error> {
99    deserializer.deserialize_str(StringEnumVisitor {
100        _marker: Default::default(),
101    })
102}
103
104#[derive(Default)]
105struct StringEnumVisitor<'a, T: FromStr> {
106    _marker: std::marker::PhantomData<&'a T>,
107}
108
109impl<'a, T: FromStr> de::Visitor<'a> for StringEnumVisitor<'a, T> {
110    type Value = T;
111
112    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
113        write!(formatter, "a string representation an enum value")
114    }
115
116    fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
117        let enum_value: T = v
118            .parse()
119            .map_err(|_| de::Error::custom(format!("invalid enum value {}", v)))?;
120
121        Ok(enum_value)
122    }
123}
124
125pub fn deserialize_api_error<'a, D: de::Deserializer<'a>>(
126    deserializer: D,
127) -> Result<String, D::Error> {
128    deserializer.deserialize_seq(ApiErrorVisitor)
129}
130
131struct ApiErrorVisitor;
132
133impl<'a> de::Visitor<'a> for ApiErrorVisitor {
134    type Value = String;
135
136    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
137        write!(formatter, "an SEPTA API error")
138    }
139
140    fn visit_seq<A: de::SeqAccess<'a>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
141        let mut error: Option<String> = None;
142
143        while let Some(element) = seq.next_element::<HashMap<String, String>>()? {
144            if error.is_some() {
145                return Err(de::Error::custom("expected only one error"));
146            } else if element.contains_key("error") {
147                error = element.get("error").map(|s| s.to_string());
148            }
149        }
150
151        error.ok_or_else(|| de::Error::custom("expected an error"))
152    }
153}
154
155pub fn deserialize_naive_date_time<'a, D: de::Deserializer<'a>>(
156    deserializer: D,
157) -> Result<NaiveDateTime, D::Error> {
158    deserializer.deserialize_str(NaiveDateTimeVisitor)
159}
160
161const DATE_TIME_FORMAT: &str = "%Y-%m-%d %H:%M:%S%.f";
162
163struct NaiveDateTimeVisitor;
164
165impl<'a> de::Visitor<'a> for NaiveDateTimeVisitor {
166    type Value = NaiveDateTime;
167
168    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
169        write!(formatter, "a trivially encoded date string")
170    }
171
172    fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
173        match NaiveDateTime::parse_from_str(value, DATE_TIME_FORMAT) {
174            Ok(date) => Ok(date),
175            Err(e) => Err(E::custom(format!(
176                "Error {} parsing timestamp {}",
177                e, value
178            ))),
179        }
180    }
181}
182
183pub fn deserialize_naive_time<'a, D: de::Deserializer<'a>>(
184    deserializer: D,
185) -> Result<NaiveTime, D::Error> {
186    deserializer.deserialize_str(NaiveTimeVisitor)
187}
188
189const TIME_FORMAT: &str = "%I:%M%p";
190
191struct NaiveTimeVisitor;
192
193impl<'a> de::Visitor<'a> for NaiveTimeVisitor {
194    type Value = NaiveTime;
195
196    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
197        write!(formatter, "a trivially encoded time string")
198    }
199
200    fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
201        match NaiveTime::parse_from_str(value, TIME_FORMAT) {
202            Ok(date) => Ok(date),
203            Err(e) => Err(E::custom(format!("Error {} parsing time {}", e, value))),
204        }
205    }
206}
207
208pub fn deserialize_naive_time_with_space<'a, D: de::Deserializer<'a>>(
209    deserializer: D,
210) -> Result<NaiveTime, D::Error> {
211    deserializer.deserialize_str(NaiveTimeWithSpaceVisitor)
212}
213
214const TIME_FORMAT_WITH_SPACE: &str = "%I:%M %p";
215
216struct NaiveTimeWithSpaceVisitor;
217
218impl<'a> de::Visitor<'a> for NaiveTimeWithSpaceVisitor {
219    type Value = NaiveTime;
220
221    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
222        write!(
223            formatter,
224            "an encoded time string with a space between the minutes and the AM/PM"
225        )
226    }
227
228    fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
229        match NaiveTime::parse_from_str(value, TIME_FORMAT_WITH_SPACE) {
230            Ok(date) => Ok(date),
231            Err(e) => Err(E::custom(format!("Error {} parsing time {}", e, value))),
232        }
233    }
234}
235
236pub fn deserialize_option_naive_time_with_space<'a, D: de::Deserializer<'a>>(
237    deserializer: D,
238) -> Result<Option<NaiveTime>, D::Error> {
239    deserializer.deserialize_str(OptionNaiveTimeWithSpaceVisitor)
240}
241
242struct OptionNaiveTimeWithSpaceVisitor;
243
244impl<'a> de::Visitor<'a> for OptionNaiveTimeWithSpaceVisitor {
245    type Value = Option<NaiveTime>;
246
247    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
248        write!(
249            formatter,
250            "an encoded time string with a space between the minutes and the AM/PM or 'na'"
251        )
252    }
253
254    fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
255        if value == "na" {
256            Ok(None)
257        } else {
258            match NaiveTime::parse_from_str(value, TIME_FORMAT_WITH_SPACE) {
259                Ok(date) => Ok(Some(date)),
260                Err(e) => Err(E::custom(format!("Error {} parsing time {}", e, value))),
261            }
262        }
263    }
264}
265
266pub fn deserialize_bool<'a, D: de::Deserializer<'a>>(deserializer: D) -> Result<bool, D::Error> {
267    deserializer.deserialize_str(BoolStringVisitor)
268}
269
270struct BoolStringVisitor;
271
272impl<'a> de::Visitor<'a> for BoolStringVisitor {
273    type Value = bool;
274
275    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
276        write!(formatter, "a trivially encoded bool")
277    }
278
279    fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
280        match value.to_ascii_lowercase().as_str() {
281            "true" => Ok(true),
282            "false" => Ok(false),
283            _ => Err(de::Error::unknown_variant(value, &["true", "false"])),
284        }
285    }
286}
287
288pub fn deserialize_f64<'a, D: de::Deserializer<'a>>(deserializer: D) -> Result<f64, D::Error> {
289    deserializer.deserialize_str(F64StringVisitor)
290}
291
292struct F64StringVisitor;
293
294impl<'a> de::Visitor<'a> for F64StringVisitor {
295    type Value = f64;
296
297    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
298        write!(formatter, "a string encoded f64")
299    }
300
301    fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
302        value
303            .parse::<f64>()
304            .map_err(|e| de::Error::custom(format!("Error {} parsing f64 {}", e, value)))
305    }
306}
307
308pub fn deserialize_optional_f64<'a, D: de::Deserializer<'a>>(
309    deserializer: D,
310) -> Result<Option<f64>, D::Error> {
311    deserializer.deserialize_option(OptionF64StringVisitor)
312}
313
314struct OptionF64StringVisitor;
315
316impl<'a> de::Visitor<'a> for OptionF64StringVisitor {
317    type Value = Option<f64>;
318
319    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
320        write!(formatter, "a string encoded f64 or null")
321    }
322
323    fn visit_none<E: de::Error>(self) -> Result<Self::Value, E> {
324        Ok(None)
325    }
326
327    fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
328        Ok(None)
329    }
330
331    fn visit_some<D: de::Deserializer<'a>>(self, d: D) -> Result<Self::Value, D::Error> {
332        Ok(d.deserialize_str(F64StringVisitor)
333            .map(Some)
334            .unwrap_or(None))
335    }
336}