1use std::fmt::Display;
17use std::str::FromStr;
18
19use base64::engine::general_purpose::{STANDARD, STANDARD_NO_PAD, URL_SAFE, URL_SAFE_NO_PAD};
20use base64::Engine as _;
21use serde::de::{self, Unexpected, Visitor};
22use serde::{Deserialize, Deserializer, Serializer};
23
24#[derive(Debug, Clone, Deserialize)]
26#[serde(untagged)]
27pub enum EnumRepr {
28 Name(String),
30 Number(i32),
32}
33
34fn decode_base64<E: de::Error>(s: &str) -> Result<Vec<u8>, E> {
35 for engine in [&STANDARD, &STANDARD_NO_PAD, &URL_SAFE, &URL_SAFE_NO_PAD] {
36 if let Ok(bytes) = engine.decode(s) {
37 return Ok(bytes);
38 }
39 }
40 Err(E::invalid_value(
41 Unexpected::Str(s),
42 &"base64-encoded bytes",
43 ))
44}
45
46struct IntVisitor<T>(std::marker::PhantomData<T>);
47
48impl<T> Visitor<'_> for IntVisitor<T>
49where
50 T: FromStr + TryFrom<i64> + TryFrom<u64>,
51 <T as FromStr>::Err: Display,
52{
53 type Value = T;
54
55 fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 f.write_str("an integer, as a JSON number or a decimal string")
57 }
58
59 fn visit_str<E: de::Error>(self, v: &str) -> Result<T, E> {
60 v.parse().map_err(|e: <T as FromStr>::Err| E::custom(e))
61 }
62
63 fn visit_i64<E: de::Error>(self, v: i64) -> Result<T, E> {
64 T::try_from(v).map_err(|_| E::custom(format!("integer {v} out of range")))
65 }
66
67 fn visit_u64<E: de::Error>(self, v: u64) -> Result<T, E> {
68 T::try_from(v).map_err(|_| E::custom(format!("integer {v} out of range")))
69 }
70}
71
72fn deserialize_int<'de, T, D>(d: D) -> Result<T, D::Error>
73where
74 T: FromStr + TryFrom<i64> + TryFrom<u64>,
75 <T as FromStr>::Err: Display,
76 D: Deserializer<'de>,
77{
78 d.deserialize_any(IntVisitor(std::marker::PhantomData))
79}
80
81pub mod opt_int {
83 use super::*;
84
85 pub fn serialize<T: Display, S: Serializer>(v: &Option<T>, s: S) -> Result<S::Ok, S::Error> {
87 match v {
88 Some(v) => s.serialize_str(&v.to_string()),
89 None => s.serialize_none(),
90 }
91 }
92
93 pub fn deserialize<'de, T, D>(d: D) -> Result<Option<T>, D::Error>
95 where
96 T: FromStr + TryFrom<i64> + TryFrom<u64>,
97 <T as FromStr>::Err: Display,
98 D: Deserializer<'de>,
99 {
100 struct OptVisitor<T>(std::marker::PhantomData<T>);
101
102 impl<'de, T> Visitor<'de> for OptVisitor<T>
103 where
104 T: FromStr + TryFrom<i64> + TryFrom<u64>,
105 <T as FromStr>::Err: Display,
106 {
107 type Value = Option<T>;
108
109 fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 f.write_str("an optional integer")
111 }
112
113 fn visit_none<E: de::Error>(self) -> Result<Self::Value, E> {
114 Ok(None)
115 }
116
117 fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
118 Ok(None)
119 }
120
121 fn visit_some<D: Deserializer<'de>>(self, d: D) -> Result<Self::Value, D::Error> {
122 super::deserialize_int(d).map(Some)
123 }
124 }
125
126 d.deserialize_option(OptVisitor(std::marker::PhantomData))
127 }
128}
129
130pub mod vec_int {
132 use super::*;
133
134 pub fn serialize<T: Display, S: Serializer>(v: &[T], s: S) -> Result<S::Ok, S::Error> {
136 use serde::ser::SerializeSeq;
137 let mut seq = s.serialize_seq(Some(v.len()))?;
138 for item in v {
139 seq.serialize_element(&item.to_string())?;
140 }
141 seq.end()
142 }
143
144 pub fn deserialize<'de, T, D>(d: D) -> Result<Vec<T>, D::Error>
146 where
147 T: FromStr + TryFrom<i64> + TryFrom<u64>,
148 <T as FromStr>::Err: Display,
149 D: Deserializer<'de>,
150 {
151 struct SeqVisitor<T>(std::marker::PhantomData<T>);
152
153 impl<'de, T> Visitor<'de> for SeqVisitor<T>
154 where
155 T: FromStr + TryFrom<i64> + TryFrom<u64>,
156 <T as FromStr>::Err: Display,
157 {
158 type Value = Vec<T>;
159
160 fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 f.write_str("a sequence of integers")
162 }
163
164 fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
165 Ok(Vec::new())
166 }
167
168 fn visit_seq<A: de::SeqAccess<'de>>(self, mut a: A) -> Result<Self::Value, A::Error> {
169 let mut out = Vec::with_capacity(a.size_hint().unwrap_or(0));
170 while let Some(v) = a.next_element_seed(IntSeed(std::marker::PhantomData))? {
171 out.push(v);
172 }
173 Ok(out)
174 }
175 }
176
177 struct IntSeed<T>(std::marker::PhantomData<T>);
178
179 impl<'de, T> de::DeserializeSeed<'de> for IntSeed<T>
180 where
181 T: FromStr + TryFrom<i64> + TryFrom<u64>,
182 <T as FromStr>::Err: Display,
183 {
184 type Value = T;
185
186 fn deserialize<D: Deserializer<'de>>(self, d: D) -> Result<T, D::Error> {
187 super::deserialize_int(d)
188 }
189 }
190
191 d.deserialize_any(SeqVisitor(std::marker::PhantomData))
192 }
193}
194
195pub mod opt_bytes {
197 use super::*;
198
199 pub fn serialize<S: Serializer>(v: &Option<Vec<u8>>, s: S) -> Result<S::Ok, S::Error> {
201 match v {
202 Some(b) => s.serialize_str(&STANDARD.encode(b)),
203 None => s.serialize_none(),
204 }
205 }
206
207 pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Option<Vec<u8>>, D::Error> {
209 let raw = Option::<String>::deserialize(d)?;
210 raw.map(|s| decode_base64(&s)).transpose()
211 }
212}
213
214pub mod vec_bytes {
216 use super::*;
217
218 pub fn serialize<S: Serializer>(v: &[Vec<u8>], s: S) -> Result<S::Ok, S::Error> {
220 use serde::ser::SerializeSeq;
221 let mut seq = s.serialize_seq(Some(v.len()))?;
222 for item in v {
223 seq.serialize_element(&STANDARD.encode(item))?;
224 }
225 seq.end()
226 }
227
228 pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<Vec<u8>>, D::Error> {
230 let raw = Option::<Vec<String>>::deserialize(d)?.unwrap_or_default();
231 raw.iter().map(|s| decode_base64(s)).collect()
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use serde::{Deserialize, Serialize};
238
239 #[derive(Debug, PartialEq, Serialize, Deserialize)]
240 struct Sample {
241 #[serde(
242 default,
243 with = "super::opt_int",
244 skip_serializing_if = "Option::is_none"
245 )]
246 seq: Option<i64>,
247 #[serde(
248 default,
249 with = "super::opt_bytes",
250 skip_serializing_if = "Option::is_none"
251 )]
252 data: Option<Vec<u8>>,
253 #[serde(
254 default,
255 with = "super::vec_int",
256 skip_serializing_if = "Vec::is_empty"
257 )]
258 counts: Vec<u64>,
259 }
260
261 #[test]
262 fn accepts_string_and_numeric_integers() {
263 let from_string: Sample = serde_json::from_str(r#"{"seq":"17"}"#).unwrap();
264 let from_number: Sample = serde_json::from_str(r#"{"seq":17}"#).unwrap();
265 assert_eq!(from_string.seq, Some(17));
266 assert_eq!(from_number.seq, from_string.seq);
267 }
268
269 #[test]
270 fn emits_the_canonical_string_form() {
271 let s = Sample {
272 seq: Some(-3),
273 data: None,
274 counts: vec![1, 2],
275 };
276 assert_eq!(
277 serde_json::to_string(&s).unwrap(),
278 r#"{"seq":"-3","counts":["1","2"]}"#
279 );
280 }
281
282 #[test]
283 fn absent_fields_stay_absent() {
284 let s: Sample = serde_json::from_str("{}").unwrap();
285 assert_eq!(
286 s,
287 Sample {
288 seq: None,
289 data: None,
290 counts: vec![]
291 }
292 );
293 }
294
295 #[test]
296 fn base64_round_trips_across_alphabets() {
297 let padded: Sample = serde_json::from_str(r#"{"data":"//79"}"#).unwrap();
298 let url_safe: Sample = serde_json::from_str(r#"{"data":"__79"}"#).unwrap();
299 assert_eq!(padded.data, Some(vec![0xff, 0xfe, 0xfd]));
300 assert_eq!(url_safe.data, padded.data);
301 assert_eq!(
302 serde_json::to_string(&padded).unwrap(),
303 r#"{"data":"//79"}"#
304 );
305 }
306
307 #[test]
308 fn null_decodes_as_absent() {
309 let s: Sample = serde_json::from_str(r#"{"seq":null,"data":null}"#).unwrap();
310 assert_eq!(s.seq, None);
311 assert_eq!(s.data, None);
312 }
313}