1use chrono::{DateTime, TimeZone, Utc};
2use mime::Mime;
3use serde::de::{DeserializeOwned, Unexpected};
4use serde::{Deserialize, Deserializer, Serializer};
5use serde_json::Value;
6use std::collections::BTreeMap;
7use std::fmt::Display;
8use std::result::Result;
9use std::str::FromStr;
10use url::Url;
11
12pub fn option_from_str<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
13where
14 T: FromStr,
15 T::Err: Display,
16 D: Deserializer<'de>,
17{
18 let result: Result<T, D::Error> = from_str(deserializer);
19 Ok(result.ok())
20}
21
22pub fn from_str<'de, T, D>(deserializer: D) -> Result<T, D::Error>
24where
25 T: FromStr,
26 T::Err: Display,
27 D: Deserializer<'de>,
28{
29 let s = String::deserialize(deserializer)?;
30 T::from_str(&s).map_err(serde::de::Error::custom)
31}
32
33pub fn optional_to_string<T, S>(x: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
34where
35 T: ToString,
36 S: Serializer,
37{
38 match x {
39 Some(ref value) => to_string(value, serializer),
40 None => serializer.serialize_none(),
41 }
42}
43
44pub fn to_string<T, S>(x: &T, serializer: S) -> Result<S::Ok, S::Error>
45where
46 T: ToString,
47 S: Serializer,
48{
49 serializer.serialize_str(&x.to_string())
50}
51
52pub fn to_comma_delimited_string<S>(x: &Option<&[&str]>, serializer: S) -> Result<S::Ok, S::Error>
53where
54 S: Serializer,
55{
56 match x {
57 Some(value) => serializer.serialize_str(&value.join(",")),
58 None => serializer.serialize_none(),
59 }
60}
61
62pub fn try_url_from_string<'de, D>(deserializer: D) -> Result<Option<Url>, D::Error>
63where
64 D: Deserializer<'de>,
65{
66 let o: Option<String> = Option::deserialize(deserializer)?;
67 Ok(o.and_then(|s| Url::parse(&s).ok()))
68}
69
70pub fn optional_vec_from_map<'de, T, D>(deserializer: D) -> Result<Option<Vec<T>>, D::Error>
71where
72 T: DeserializeOwned + Clone + std::fmt::Debug,
73 D: Deserializer<'de>,
74{
75 let o: Option<Value> = Option::deserialize(deserializer)?;
76 match o {
77 Some(v) => json_value_to_vec::<T, D>(v).map(Some),
78 None => Ok(None),
79 }
80}
81
82pub fn vec_from_map<'de, T, D>(deserializer: D) -> Result<Vec<T>, D::Error>
83where
84 T: DeserializeOwned + Clone + std::fmt::Debug,
85 D: Deserializer<'de>,
86{
87 let value = Value::deserialize(deserializer)?;
88 json_value_to_vec::<T, D>(value)
89}
90
91pub fn json_value_to_vec<'de, T, D>(value: Value) -> Result<Vec<T>, D::Error>
92where
93 T: DeserializeOwned + Clone + std::fmt::Debug,
94 D: Deserializer<'de>,
95{
96 match value {
97 a @ Value::Array(..) => {
98 serde_json::from_value::<Vec<T>>(a).map_err(serde::de::Error::custom)
99 }
100 o @ Value::Object(..) => serde_json::from_value::<BTreeMap<String, T>>(o)
101 .map(map_to_vec)
102 .map_err(serde::de::Error::custom),
103 other => Err(serde::de::Error::invalid_value(
104 Unexpected::Other(format!("{:?}", other).as_str()),
105 &"object or array",
106 )),
107 }
108}
109
110pub fn map_to_vec<T>(map: BTreeMap<String, T>) -> Vec<T> {
111 map.into_iter().map(|(_, v)| v).collect::<Vec<_>>()
112}
113
114pub fn bool_from_int<'de, D>(deserializer: D) -> Result<bool, D::Error>
116where
117 D: Deserializer<'de>,
118{
119 match u8::deserialize(deserializer)? {
120 0 => Ok(false),
121 1 => Ok(true),
122 other => Err(serde::de::Error::invalid_value(
123 Unexpected::Unsigned(other as u64),
124 &"zero or one",
125 )),
126 }
127}
128
129pub fn bool_from_int_string<'de, D>(deserializer: D) -> Result<bool, D::Error>
130where
131 D: Deserializer<'de>,
132{
133 match String::deserialize(deserializer)?.as_str() {
134 "0" => Ok(false),
135 "1" => Ok(true),
136 other => Err(serde::de::Error::invalid_value(
137 Unexpected::Str(other),
138 &"zero or one",
139 )),
140 }
141}
142
143#[allow(clippy::trivially_copy_pass_by_ref)]
144pub fn optional_bool_to_int<S>(x: &Option<bool>, serializer: S) -> Result<S::Ok, S::Error>
145where
146 S: Serializer,
147{
148 match x {
149 Some(ref value) => bool_to_int(value, serializer),
150 None => serializer.serialize_none(),
151 }
152}
153
154pub fn optional_datetime_to_int<S>(
155 x: &Option<DateTime<Utc>>,
156 serializer: S,
157) -> Result<S::Ok, S::Error>
158where
159 S: Serializer,
160{
161 match x {
162 Some(ref value) => string_date_unix_timestamp_format::serialize(value, serializer),
163 None => serializer.serialize_none(),
164 }
165}
166
167pub fn untagged_to_str<S>(serializer: S) -> Result<S::Ok, S::Error>
168where
169 S: Serializer,
170{
171 serializer.serialize_str("_untagged_")
172}
173
174pub fn option_mime_from_string<'de, D>(deserializer: D) -> Result<Option<Mime>, D::Error>
175where
176 D: Deserializer<'de>,
177{
178 Option::deserialize(deserializer).and_then(|o: Option<String>| match o.as_deref() {
179 Some("") | None => Ok(None),
180 Some(str) => str.parse::<Mime>().map(Some).map_err(|other| {
181 serde::de::Error::invalid_value(
182 Unexpected::Other(format!("{:?}", other).as_str()),
183 &"valid mime type",
184 )
185 }),
186 })
187}
188
189pub fn int_date_unix_timestamp_format<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
190where
191 D: Deserializer<'de>,
192{
193 let unix_timestamp = i64::deserialize(deserializer)?;
194 Ok(Utc.timestamp(unix_timestamp, 0))
195}
196
197pub fn option_string_date_unix_timestamp_format<'de, D>(
198 deserializer: D,
199) -> Result<Option<DateTime<Utc>>, D::Error>
200where
201 D: Deserializer<'de>,
202{
203 Option::deserialize(deserializer).and_then(|o: Option<String>| match o.as_deref() {
204 Some("0") | None => Ok(None),
205 Some(str) => str
206 .parse::<i64>()
207 .map(|i| Some(Utc.timestamp(i, 0)))
208 .map_err(serde::de::Error::custom),
209 })
210}
211
212pub const FORMAT: &str = "%Y-%m-%d %H:%M:%S";
213
214pub fn option_string_date_format<'de, D>(deserializer: D) -> Result<Option<DateTime<Utc>>, D::Error>
215where
216 D: Deserializer<'de>,
217{
218 match String::deserialize(deserializer)?.as_str() {
219 "0000-00-00 00:00:00" => Ok(None),
220 str => Utc
221 .datetime_from_str(str, FORMAT)
222 .map_err(serde::de::Error::custom)
223 .map(Option::Some),
224 }
225}
226
227pub mod string_date_unix_timestamp_format {
229 use chrono::{DateTime, TimeZone, Utc};
230 use serde::{self, Deserialize, Deserializer, Serializer};
231
232 pub fn serialize<S>(date: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
233 where
234 S: Serializer,
235 {
236 serializer.serialize_str(&date.timestamp().to_string())
237 }
238
239 pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
240 where
241 D: Deserializer<'de>,
242 {
243 let s = String::deserialize(deserializer)?;
244 s.parse::<i64>()
245 .map(|i| Utc.timestamp(i, 0))
246 .map_err(serde::de::Error::custom)
247 }
248}
249
250#[allow(clippy::trivially_copy_pass_by_ref)]
251pub fn bool_to_int<S>(x: &bool, serializer: S) -> Result<S::Ok, S::Error>
252where
253 S: Serializer,
254{
255 let output = match x {
256 true => "1",
257 false => "0",
258 };
259 serializer.serialize_str(output)
260}
261
262pub fn borrow_url<S>(x: &Url, serializer: S) -> Result<S::Ok, S::Error>
263where
264 S: Serializer,
265{
266 serializer.serialize_str(x.as_str())
267}
268
269pub fn true_to_unit_variant<'de, D>(deserializer: D) -> Result<(), D::Error>
270where
271 D: Deserializer<'de>,
272{
273 if bool::deserialize(deserializer)? {
274 Ok(())
275 } else {
276 Err(serde::de::Error::invalid_value(
277 Unexpected::Bool(false),
278 &r#"true"#,
279 ))
280 }
281}
282
283pub fn false_to_unit_variant<'de, D>(deserializer: D) -> Result<(), D::Error>
284where
285 D: Deserializer<'de>,
286{
287 if !bool::deserialize(deserializer)? {
288 Ok(())
289 } else {
290 Err(serde::de::Error::invalid_value(
291 Unexpected::Bool(false),
292 &r#"false"#,
293 ))
294 }
295}