1use serde::{de, Deserialize, Deserializer};
7use std::fmt::Display;
8use std::str::FromStr;
9
10fn is_invalid_query_token(s: &str) -> bool {
12 let t = s.trim();
13 t.is_empty() || t.eq_ignore_ascii_case("nan") || t.eq_ignore_ascii_case("undefined")
14}
15
16fn empty_as_none<'de, T, D>(deserializer: D, _field: &str) -> Result<Option<T>, D::Error>
17where
18 D: Deserializer<'de>,
19 T: FromStr,
20 T::Err: Display,
21{
22 let raw = Option::<String>::deserialize(deserializer)?;
23 match raw {
24 None => Ok(None),
25 Some(s) if is_invalid_query_token(&s) => Ok(None),
26 Some(s) => s.parse().map(Some).map_err(de::Error::custom),
27 }
28}
29
30fn empty_as_i32_default<'de, D>(
31 deserializer: D,
32 default: i32,
33 _field: &str,
34) -> Result<i32, D::Error>
35where
36 D: Deserializer<'de>,
37{
38 let raw = Option::<String>::deserialize(deserializer)?;
39 match raw {
40 None => Ok(default),
41 Some(s) if s.trim().is_empty() => Ok(default),
42 Some(s) => s.parse().map_err(de::Error::custom),
43 }
44}
45
46fn required_i32<'de, D>(deserializer: D, field: &str) -> Result<i32, D::Error>
47where
48 D: Deserializer<'de>,
49{
50 let raw = Option::<String>::deserialize(deserializer)?;
51 match raw {
52 None => Err(de::Error::custom(format!("missing query parameter `{field}`"))),
53 Some(s) if s.trim().is_empty() => {
54 Err(de::Error::custom(format!("empty query parameter `{field}`")))
55 }
56 Some(s) => s.parse().map_err(de::Error::custom),
57 }
58}
59
60fn required_i16<'de, D>(deserializer: D, field: &str) -> Result<i16, D::Error>
61where
62 D: Deserializer<'de>,
63{
64 let raw = Option::<String>::deserialize(deserializer)?;
65 match raw {
66 None => Err(de::Error::custom(format!("missing query parameter `{field}`"))),
67 Some(s) if s.trim().is_empty() => {
68 Err(de::Error::custom(format!("empty query parameter `{field}`")))
69 }
70 Some(s) => s.parse().map_err(de::Error::custom),
71 }
72}
73
74pub fn de_opt_string<'de, D>(d: D) -> Result<Option<String>, D::Error>
76where
77 D: Deserializer<'de>,
78{
79 let raw = Option::<String>::deserialize(d)?;
80 Ok(match raw {
81 None => None,
82 Some(s) if s.trim().is_empty() => None,
83 Some(s) => Some(s),
84 })
85}
86
87pub fn de_opt_i16<'de, D>(d: D) -> Result<Option<i16>, D::Error>
89where
90 D: Deserializer<'de>,
91{
92 empty_as_none(d, "i16")
93}
94
95pub fn de_opt_i32<'de, D>(d: D) -> Result<Option<i32>, D::Error>
97where
98 D: Deserializer<'de>,
99{
100 empty_as_none(d, "i32")
101}
102
103pub fn de_page<'de, D>(d: D) -> Result<i32, D::Error>
105where
106 D: Deserializer<'de>,
107{
108 empty_as_i32_default(d, 1, "page")
109}
110
111pub fn de_page_size<'de, D>(d: D) -> Result<i32, D::Error>
113where
114 D: Deserializer<'de>,
115{
116 empty_as_i32_default(d, 20, "pageSize")
117}
118
119pub fn de_station_page_size<'de, D>(d: D) -> Result<i32, D::Error>
121where
122 D: Deserializer<'de>,
123{
124 empty_as_i32_default(d, 5, "pageSize")
125}
126
127pub fn de_i32<'de, D>(d: D) -> Result<i32, D::Error>
129where
130 D: Deserializer<'de>,
131{
132 required_i32(d, "i32")
133}
134
135pub fn de_i16<'de, D>(d: D) -> Result<i16, D::Error>
137where
138 D: Deserializer<'de>,
139{
140 required_i16(d, "i16")
141}
142
143pub fn de_opt_i32_loose<'de, D>(d: D) -> Result<Option<i32>, D::Error>
145where
146 D: Deserializer<'de>,
147{
148 #[derive(Deserialize)]
149 #[serde(untagged)]
150 enum NumOrStr {
151 Num(i32),
152 Str(String),
153 }
154 match Option::<NumOrStr>::deserialize(d)? {
155 None => Ok(None),
156 Some(NumOrStr::Num(v)) => Ok(Some(v)),
157 Some(NumOrStr::Str(s)) if s.trim().is_empty() => Ok(None),
158 Some(NumOrStr::Str(s)) => s.parse().map(Some).map_err(de::Error::custom),
159 }
160}
161
162pub fn de_opt_i16_loose<'de, D>(d: D) -> Result<Option<i16>, D::Error>
164where
165 D: Deserializer<'de>,
166{
167 #[derive(Deserialize)]
168 #[serde(untagged)]
169 enum NumOrStr {
170 Num(i16),
171 Str(String),
172 }
173 match Option::<NumOrStr>::deserialize(d)? {
174 None => Ok(None),
175 Some(NumOrStr::Num(v)) => Ok(Some(v)),
176 Some(NumOrStr::Str(s)) if s.trim().is_empty() => Ok(None),
177 Some(NumOrStr::Str(s)) => s.parse().map(Some).map_err(de::Error::custom),
178 }
179}
180
181pub fn de_i32_loose<'de, D>(d: D) -> Result<i32, D::Error>
183where
184 D: Deserializer<'de>,
185{
186 #[derive(Deserialize)]
187 #[serde(untagged)]
188 enum NumOrStr {
189 Num(i32),
190 Str(String),
191 }
192 match NumOrStr::deserialize(d)? {
193 NumOrStr::Num(v) => Ok(v),
194 NumOrStr::Str(s) if s.trim().is_empty() => {
195 Err(de::Error::custom("required i32 field is empty"))
196 }
197 NumOrStr::Str(s) => s.parse().map_err(de::Error::custom),
198 }
199}