1use std::fmt;
22
23use serde::de::{self, Deserializer, Unexpected, Visitor};
24
25pub(crate) fn parse_bool(raw: &str) -> Result<bool, String> {
31 match raw {
32 "1" | "t" | "T" | "TRUE" | "true" | "True" => Ok(true),
33 "0" | "f" | "F" | "FALSE" | "false" | "False" => Ok(false),
34 other => Err(format!("invalid boolean `{other}`")),
35 }
36}
37
38pub(crate) fn parse_i64(raw: &str) -> Result<i64, String> {
44 trim_zero_decimal(raw.trim())
45 .parse::<i64>()
46 .map_err(|_| format!("invalid integer `{raw}`"))
47}
48
49fn trim_zero_decimal(raw: &str) -> &str {
52 match raw.split_once('.') {
53 Some((integer, fraction)) if !fraction.is_empty() && fraction.bytes().all(|byte| byte == b'0') => integer,
54 _ => raw,
55 }
56}
57
58pub(crate) fn parse_f64(raw: &str) -> Result<f64, String> {
64 let parsed: f64 = raw.trim().parse().map_err(|_| format!("invalid number `{raw}`"))?;
65 if !parsed.is_finite() {
66 return Err(format!("non-finite number `{raw}`"));
67 }
68 Ok(parsed)
69}
70
71pub(crate) fn deserialize_bool<'de, D>(deserializer: D) -> Result<bool, D::Error>
78where
79 D: Deserializer<'de>,
80{
81 deserializer.deserialize_any(BoolVisitor)
82}
83
84pub(crate) fn deserialize_i64<'de, D>(deserializer: D) -> Result<i64, D::Error>
91where
92 D: Deserializer<'de>,
93{
94 deserializer.deserialize_any(I64Visitor)
95}
96
97pub(crate) fn deserialize_f64<'de, D>(deserializer: D) -> Result<f64, D::Error>
104where
105 D: Deserializer<'de>,
106{
107 deserializer.deserialize_any(F64Visitor)
108}
109
110pub(crate) fn deserialize_string<'de, D>(deserializer: D) -> Result<String, D::Error>
116where
117 D: Deserializer<'de>,
118{
119 deserializer.deserialize_any(StringVisitor)
120}
121
122pub fn cast_to_string(value: &::serde_json::Value) -> Result<String, String> {
132 value.deserialize_any(StringVisitor).map_err(|e| e.to_string())
133}
134
135pub(crate) fn deserialize_optional_string<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
141where
142 D: Deserializer<'de>,
143{
144 deserializer.deserialize_option(OptionalStringVisitor)
145}
146
147pub(crate) fn deserialize_optional_i64<'de, D>(deserializer: D) -> Result<Option<i64>, D::Error>
153where
154 D: Deserializer<'de>,
155{
156 deserializer.deserialize_option(OptionalI64Visitor)
157}
158
159struct BoolVisitor;
160
161impl Visitor<'_> for BoolVisitor {
162 type Value = bool;
163
164 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 f.write_str("a boolean, a boolean string, or a number")
166 }
167
168 fn visit_bool<E: de::Error>(self, value: bool) -> Result<bool, E> {
169 Ok(value)
170 }
171
172 fn visit_i64<E: de::Error>(self, value: i64) -> Result<bool, E> {
173 Ok(value != 0)
174 }
175
176 fn visit_u64<E: de::Error>(self, value: u64) -> Result<bool, E> {
177 Ok(value != 0)
178 }
179
180 fn visit_f64<E: de::Error>(self, value: f64) -> Result<bool, E> {
181 Ok(value != 0.0)
182 }
183
184 fn visit_str<E: de::Error>(self, value: &str) -> Result<bool, E> {
185 parse_bool(value).map_err(|_| E::invalid_value(Unexpected::Str(value), &self))
186 }
187
188 fn visit_unit<E: de::Error>(self) -> Result<bool, E> {
191 Ok(false)
192 }
193}
194
195struct I64Visitor;
196
197impl Visitor<'_> for I64Visitor {
198 type Value = i64;
199
200 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201 f.write_str("an integer, a numeric string, or a boolean")
202 }
203
204 fn visit_bool<E: de::Error>(self, value: bool) -> Result<i64, E> {
205 Ok(i64::from(value))
206 }
207
208 fn visit_i64<E: de::Error>(self, value: i64) -> Result<i64, E> {
209 Ok(value)
210 }
211
212 fn visit_u64<E: de::Error>(self, value: u64) -> Result<i64, E> {
213 i64::try_from(value).map_err(|_| E::invalid_value(Unexpected::Unsigned(value), &self))
214 }
215
216 fn visit_f64<E: de::Error>(self, value: f64) -> Result<i64, E> {
218 if !value.is_finite() || value < i64::MIN as f64 || value > i64::MAX as f64 {
219 return Err(E::invalid_value(Unexpected::Float(value), &self));
220 }
221 Ok(value.trunc() as i64)
222 }
223
224 fn visit_str<E: de::Error>(self, value: &str) -> Result<i64, E> {
225 parse_i64(value).map_err(|_| E::invalid_value(Unexpected::Str(value), &self))
226 }
227
228 fn visit_unit<E: de::Error>(self) -> Result<i64, E> {
229 Ok(0)
230 }
231}
232
233struct F64Visitor;
234
235impl Visitor<'_> for F64Visitor {
236 type Value = f64;
237
238 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239 f.write_str("a number, a numeric string, or a boolean")
240 }
241
242 fn visit_bool<E: de::Error>(self, value: bool) -> Result<f64, E> {
243 Ok(if value { 1.0 } else { 0.0 })
244 }
245
246 fn visit_i64<E: de::Error>(self, value: i64) -> Result<f64, E> {
247 Ok(value as f64)
248 }
249
250 fn visit_u64<E: de::Error>(self, value: u64) -> Result<f64, E> {
251 Ok(value as f64)
252 }
253
254 fn visit_f64<E: de::Error>(self, value: f64) -> Result<f64, E> {
255 Ok(value)
256 }
257
258 fn visit_str<E: de::Error>(self, value: &str) -> Result<f64, E> {
259 parse_f64(value).map_err(|_| E::invalid_value(Unexpected::Str(value), &self))
260 }
261
262 fn visit_unit<E: de::Error>(self) -> Result<f64, E> {
263 Ok(0.0)
264 }
265}
266
267struct StringVisitor;
268
269impl Visitor<'_> for StringVisitor {
270 type Value = String;
271
272 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
273 f.write_str("a string, a boolean, or a number")
274 }
275
276 fn visit_bool<E: de::Error>(self, value: bool) -> Result<String, E> {
277 Ok(value.to_string())
278 }
279
280 fn visit_i64<E: de::Error>(self, value: i64) -> Result<String, E> {
281 Ok(value.to_string())
282 }
283
284 fn visit_u64<E: de::Error>(self, value: u64) -> Result<String, E> {
285 Ok(value.to_string())
286 }
287
288 fn visit_f64<E: de::Error>(self, value: f64) -> Result<String, E> {
291 Ok(value.to_string())
292 }
293
294 fn visit_str<E: de::Error>(self, value: &str) -> Result<String, E> {
295 Ok(value.to_owned())
296 }
297
298 fn visit_string<E: de::Error>(self, value: String) -> Result<String, E> {
299 Ok(value)
300 }
301
302 fn visit_unit<E: de::Error>(self) -> Result<String, E> {
303 Ok(String::new())
304 }
305}
306
307struct OptionalStringVisitor;
308
309impl<'de> Visitor<'de> for OptionalStringVisitor {
310 type Value = Option<String>;
311
312 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
313 f.write_str("a string, a boolean, a number, or null")
314 }
315
316 fn visit_none<E: de::Error>(self) -> Result<Option<String>, E> {
317 Ok(None)
318 }
319
320 fn visit_unit<E: de::Error>(self) -> Result<Option<String>, E> {
321 Ok(None)
322 }
323
324 fn visit_some<D: Deserializer<'de>>(self, deserializer: D) -> Result<Option<String>, D::Error> {
325 deserializer.deserialize_any(StringVisitor).map(Some)
326 }
327}
328
329struct OptionalI64Visitor;
330
331impl<'de> Visitor<'de> for OptionalI64Visitor {
332 type Value = Option<i64>;
333
334 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
335 f.write_str("an integer, a numeric string, a boolean, or null")
336 }
337
338 fn visit_none<E: de::Error>(self) -> Result<Option<i64>, E> {
339 Ok(None)
340 }
341
342 fn visit_unit<E: de::Error>(self) -> Result<Option<i64>, E> {
343 Ok(None)
344 }
345
346 fn visit_some<D: Deserializer<'de>>(self, deserializer: D) -> Result<Option<i64>, D::Error> {
347 deserializer.deserialize_any(I64Visitor).map(Some)
348 }
349}
350
351#[cfg(test)]
352mod tests {
353 use serde::Deserialize;
354 use serde_json::{json, Value};
355
356 use super::*;
357
358 #[derive(Deserialize)]
359 struct Bool(#[serde(deserialize_with = "deserialize_bool")] bool);
360
361 #[derive(Deserialize)]
362 struct Int(#[serde(deserialize_with = "deserialize_i64")] i64);
363
364 #[derive(Deserialize)]
365 struct Float(#[serde(deserialize_with = "deserialize_f64")] f64);
366
367 #[derive(Deserialize)]
368 struct Str(#[serde(deserialize_with = "deserialize_string")] String);
369
370 #[derive(Deserialize)]
371 struct OptStr(#[serde(deserialize_with = "deserialize_optional_string")] Option<String>);
372
373 fn as_bool(value: Value) -> Result<bool, String> {
374 serde_json::from_value::<Bool>(value)
375 .map(|b| b.0)
376 .map_err(|e| e.to_string())
377 }
378
379 fn as_int(value: Value) -> Result<i64, String> {
380 serde_json::from_value::<Int>(value)
381 .map(|i| i.0)
382 .map_err(|e| e.to_string())
383 }
384
385 fn as_float(value: Value) -> Result<f64, String> {
386 serde_json::from_value::<Float>(value)
387 .map(|f| f.0)
388 .map_err(|e| e.to_string())
389 }
390
391 fn as_string(value: Value) -> Result<String, String> {
392 serde_json::from_value::<Str>(value)
393 .map(|s| s.0)
394 .map_err(|e| e.to_string())
395 }
396
397 #[test]
398 fn bool_accepts_every_spelling_go_accepts() {
399 for truthy in [
400 json!(true),
401 json!("true"),
402 json!("True"),
403 json!("TRUE"),
404 json!("t"),
405 json!("T"),
406 json!("1"),
407 ] {
408 assert_eq!(as_bool(truthy.clone()), Ok(true), "{truthy}");
409 }
410 for falsy in [
411 json!(false),
412 json!("false"),
413 json!("False"),
414 json!("FALSE"),
415 json!("f"),
416 json!("F"),
417 json!("0"),
418 ] {
419 assert_eq!(as_bool(falsy.clone()), Ok(false), "{falsy}");
420 }
421
422 assert_eq!(as_bool(json!(2)), Ok(true));
424 assert_eq!(as_bool(json!(-1)), Ok(true));
425 assert_eq!(as_bool(json!(1.0)), Ok(true));
426 assert_eq!(as_bool(json!(0.0)), Ok(false));
427 assert_eq!(as_bool(json!(null)), Ok(false));
428 }
429
430 #[test]
431 fn bool_rejects_what_go_rejects() {
432 for rejected in [json!("yes"), json!("on"), json!(""), json!([true]), json!({"a": true})] {
434 assert!(as_bool(rejected.clone()).is_err(), "{rejected}");
435 }
436 }
437
438 #[test]
439 fn integer_accepts_numeric_strings_floats_and_booleans() {
440 assert_eq!(as_int(json!(8125)), Ok(8125));
441 assert_eq!(as_int(json!("8125")), Ok(8125));
442 assert_eq!(as_int(json!(" -7 ")), Ok(-7));
443 assert_eq!(as_int(json!(true)), Ok(1));
444 assert_eq!(as_int(json!(null)), Ok(0));
445
446 assert_eq!(as_int(json!("8125.0")), Ok(8125));
448 assert_eq!(as_int(json!("8125.000")), Ok(8125));
449 assert_eq!(as_int(json!("-8125.0")), Ok(-8125));
450
451 assert_eq!(as_int(json!(10.9)), Ok(10));
453 assert_eq!(as_int(json!(-10.9)), Ok(-10));
454 }
455
456 #[test]
457 fn integer_rejects_unparseable_and_out_of_range_values() {
458 for rejected in [
459 json!("8125ms"),
460 json!(""),
461 json!("0x1f"),
462 json!("8125.5"),
463 json!("8125."),
464 json!(1e300),
465 json!(["8125"]),
466 ] {
467 assert!(as_int(rejected.clone()).is_err(), "{rejected}");
468 }
469 }
470
471 #[test]
472 fn number_accepts_numeric_strings_integers_and_booleans() {
473 assert_eq!(as_float(json!(1.5)), Ok(1.5));
474 assert_eq!(as_float(json!("1.5")), Ok(1.5));
475 assert_eq!(as_float(json!(2)), Ok(2.0));
476 assert_eq!(as_float(json!(true)), Ok(1.0));
477 assert_eq!(as_float(json!(null)), Ok(0.0));
478 assert!(as_float(json!("half")).is_err());
479 }
480
481 #[test]
482 fn string_accepts_every_scalar() {
483 assert_eq!(as_string(json!("datadog_only")), Ok("datadog_only".to_owned()));
484 assert_eq!(as_string(json!(true)), Ok("true".to_owned()));
485 assert_eq!(as_string(json!(false)), Ok("false".to_owned()));
486 assert_eq!(as_string(json!(10485760)), Ok("10485760".to_owned()));
487 assert_eq!(as_string(json!(-1)), Ok("-1".to_owned()));
488 assert_eq!(as_string(json!(10.5)), Ok("10.5".to_owned()));
489 assert_eq!(as_string(json!(null)), Ok(String::new()));
490
491 assert_eq!(as_string(json!(1.0)), Ok("1".to_owned()));
493 }
494
495 #[test]
496 fn string_rejects_compound_values() {
497 for rejected in [json!(["a"]), json!({"a": "b"})] {
498 assert!(as_string(rejected.clone()).is_err(), "{rejected}");
499 }
500 }
501
502 #[test]
503 fn optional_string_distinguishes_null_from_a_coerced_scalar() {
504 let absent = serde_json::from_value::<OptStr>(json!(null))
505 .expect("null deserializes")
506 .0;
507 assert_eq!(absent, None);
508
509 let coerced = serde_json::from_value::<OptStr>(json!(3))
510 .expect("scalar deserializes")
511 .0;
512 assert_eq!(coerced, Some("3".to_owned()));
513 }
514}