1use std::{collections::HashMap, fmt, marker::PhantomData};
21
22use serde::de::{self, DeserializeOwned, Deserializer, MapAccess, Unexpected, Visitor};
23use serde::Deserialize;
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(crate) fn deserialize_map_or_json_string<'de, D, V>(deserializer: D) -> Result<HashMap<String, V>, D::Error>
134where
135 D: Deserializer<'de>,
136 V: DeserializeOwned,
137{
138 struct MapOrJsonString<V>(PhantomData<V>);
139
140 impl<'de, V: DeserializeOwned> Visitor<'de> for MapOrJsonString<V> {
141 type Value = HashMap<String, V>;
142
143 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144 f.write_str("a map or a JSON-encoded map string")
145 }
146
147 fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
148 serde_json::from_str(value).map_err(|e| E::custom(format_args!("invalid JSON-encoded map: {e}")))
149 }
150
151 fn visit_map<A: MapAccess<'de>>(self, map: A) -> Result<Self::Value, A::Error> {
152 HashMap::deserialize(de::value::MapAccessDeserializer::new(map))
153 }
154
155 fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
156 Ok(HashMap::new())
157 }
158
159 fn visit_none<E: de::Error>(self) -> Result<Self::Value, E> {
160 Ok(HashMap::new())
161 }
162 }
163
164 deserializer.deserialize_any(MapOrJsonString(PhantomData))
165}
166
167pub(crate) fn deserialize_string_map<'de, D>(deserializer: D) -> Result<HashMap<String, String>, D::Error>
174where
175 D: Deserializer<'de>,
176{
177 let values = deserialize_map_or_json_string::<_, serde_json::Value>(deserializer)?;
178 values
179 .into_iter()
180 .map(|(key, value)| {
181 cast_to_string(&value)
182 .map(|value| (key, value))
183 .map_err(de::Error::custom)
184 })
185 .collect()
186}
187
188pub fn cast_to_string(value: &::serde_json::Value) -> Result<String, String> {
198 value.deserialize_any(StringVisitor).map_err(|e| e.to_string())
199}
200
201pub(crate) fn deserialize_optional_string<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
207where
208 D: Deserializer<'de>,
209{
210 deserializer.deserialize_option(OptionalStringVisitor)
211}
212
213pub(crate) fn deserialize_optional_i64<'de, D>(deserializer: D) -> Result<Option<i64>, D::Error>
219where
220 D: Deserializer<'de>,
221{
222 deserializer.deserialize_option(OptionalI64Visitor)
223}
224
225struct BoolVisitor;
226
227impl Visitor<'_> for BoolVisitor {
228 type Value = bool;
229
230 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231 f.write_str("a boolean, a boolean string, or a number")
232 }
233
234 fn visit_bool<E: de::Error>(self, value: bool) -> Result<bool, E> {
235 Ok(value)
236 }
237
238 fn visit_i64<E: de::Error>(self, value: i64) -> Result<bool, E> {
239 Ok(value != 0)
240 }
241
242 fn visit_u64<E: de::Error>(self, value: u64) -> Result<bool, E> {
243 Ok(value != 0)
244 }
245
246 fn visit_f64<E: de::Error>(self, value: f64) -> Result<bool, E> {
247 Ok(value != 0.0)
248 }
249
250 fn visit_str<E: de::Error>(self, value: &str) -> Result<bool, E> {
251 parse_bool(value).map_err(|_| E::invalid_value(Unexpected::Str(value), &self))
252 }
253
254 fn visit_unit<E: de::Error>(self) -> Result<bool, E> {
257 Ok(false)
258 }
259}
260
261struct I64Visitor;
262
263impl Visitor<'_> for I64Visitor {
264 type Value = i64;
265
266 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
267 f.write_str("an integer, a numeric string, or a boolean")
268 }
269
270 fn visit_bool<E: de::Error>(self, value: bool) -> Result<i64, E> {
271 Ok(i64::from(value))
272 }
273
274 fn visit_i64<E: de::Error>(self, value: i64) -> Result<i64, E> {
275 Ok(value)
276 }
277
278 fn visit_u64<E: de::Error>(self, value: u64) -> Result<i64, E> {
279 i64::try_from(value).map_err(|_| E::invalid_value(Unexpected::Unsigned(value), &self))
280 }
281
282 fn visit_f64<E: de::Error>(self, value: f64) -> Result<i64, E> {
284 if !value.is_finite() || value < i64::MIN as f64 || value > i64::MAX as f64 {
285 return Err(E::invalid_value(Unexpected::Float(value), &self));
286 }
287 Ok(value.trunc() as i64)
288 }
289
290 fn visit_str<E: de::Error>(self, value: &str) -> Result<i64, E> {
291 parse_i64(value).map_err(|_| E::invalid_value(Unexpected::Str(value), &self))
292 }
293
294 fn visit_unit<E: de::Error>(self) -> Result<i64, E> {
295 Ok(0)
296 }
297}
298
299struct F64Visitor;
300
301impl Visitor<'_> for F64Visitor {
302 type Value = f64;
303
304 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305 f.write_str("a number, a numeric string, or a boolean")
306 }
307
308 fn visit_bool<E: de::Error>(self, value: bool) -> Result<f64, E> {
309 Ok(if value { 1.0 } else { 0.0 })
310 }
311
312 fn visit_i64<E: de::Error>(self, value: i64) -> Result<f64, E> {
313 Ok(value as f64)
314 }
315
316 fn visit_u64<E: de::Error>(self, value: u64) -> Result<f64, E> {
317 Ok(value as f64)
318 }
319
320 fn visit_f64<E: de::Error>(self, value: f64) -> Result<f64, E> {
321 Ok(value)
322 }
323
324 fn visit_str<E: de::Error>(self, value: &str) -> Result<f64, E> {
325 parse_f64(value).map_err(|_| E::invalid_value(Unexpected::Str(value), &self))
326 }
327
328 fn visit_unit<E: de::Error>(self) -> Result<f64, E> {
329 Ok(0.0)
330 }
331}
332
333struct StringVisitor;
334
335impl Visitor<'_> for StringVisitor {
336 type Value = String;
337
338 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339 f.write_str("a string, a boolean, or a number")
340 }
341
342 fn visit_bool<E: de::Error>(self, value: bool) -> Result<String, E> {
343 Ok(value.to_string())
344 }
345
346 fn visit_i64<E: de::Error>(self, value: i64) -> Result<String, E> {
347 Ok(value.to_string())
348 }
349
350 fn visit_u64<E: de::Error>(self, value: u64) -> Result<String, E> {
351 Ok(value.to_string())
352 }
353
354 fn visit_f64<E: de::Error>(self, value: f64) -> Result<String, E> {
357 Ok(value.to_string())
358 }
359
360 fn visit_str<E: de::Error>(self, value: &str) -> Result<String, E> {
361 Ok(value.to_owned())
362 }
363
364 fn visit_string<E: de::Error>(self, value: String) -> Result<String, E> {
365 Ok(value)
366 }
367
368 fn visit_unit<E: de::Error>(self) -> Result<String, E> {
369 Ok(String::new())
370 }
371}
372
373struct OptionalStringVisitor;
374
375impl<'de> Visitor<'de> for OptionalStringVisitor {
376 type Value = Option<String>;
377
378 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
379 f.write_str("a string, a boolean, a number, or null")
380 }
381
382 fn visit_none<E: de::Error>(self) -> Result<Option<String>, E> {
383 Ok(None)
384 }
385
386 fn visit_unit<E: de::Error>(self) -> Result<Option<String>, E> {
387 Ok(None)
388 }
389
390 fn visit_some<D: Deserializer<'de>>(self, deserializer: D) -> Result<Option<String>, D::Error> {
391 deserializer.deserialize_any(StringVisitor).map(Some)
392 }
393}
394
395struct OptionalI64Visitor;
396
397impl<'de> Visitor<'de> for OptionalI64Visitor {
398 type Value = Option<i64>;
399
400 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
401 f.write_str("an integer, a numeric string, a boolean, or null")
402 }
403
404 fn visit_none<E: de::Error>(self) -> Result<Option<i64>, E> {
405 Ok(None)
406 }
407
408 fn visit_unit<E: de::Error>(self) -> Result<Option<i64>, E> {
409 Ok(None)
410 }
411
412 fn visit_some<D: Deserializer<'de>>(self, deserializer: D) -> Result<Option<i64>, D::Error> {
413 deserializer.deserialize_any(I64Visitor).map(Some)
414 }
415}
416
417#[cfg(test)]
418mod tests {
419 use serde::Deserialize;
420 use serde_json::{json, Value};
421
422 use super::*;
423
424 #[derive(Deserialize)]
425 struct Bool(#[serde(deserialize_with = "deserialize_bool")] bool);
426
427 #[derive(Deserialize)]
428 struct Int(#[serde(deserialize_with = "deserialize_i64")] i64);
429
430 #[derive(Deserialize)]
431 struct Float(#[serde(deserialize_with = "deserialize_f64")] f64);
432
433 #[derive(Deserialize)]
434 struct Str(#[serde(deserialize_with = "deserialize_string")] String);
435
436 #[derive(Deserialize)]
437 struct OptStr(#[serde(deserialize_with = "deserialize_optional_string")] Option<String>);
438
439 #[derive(Deserialize)]
440 struct StringMap(#[serde(deserialize_with = "deserialize_string_map")] HashMap<String, String>);
441
442 fn as_bool(value: Value) -> Result<bool, String> {
443 serde_json::from_value::<Bool>(value)
444 .map(|b| b.0)
445 .map_err(|e| e.to_string())
446 }
447
448 fn as_int(value: Value) -> Result<i64, String> {
449 serde_json::from_value::<Int>(value)
450 .map(|i| i.0)
451 .map_err(|e| e.to_string())
452 }
453
454 fn as_float(value: Value) -> Result<f64, String> {
455 serde_json::from_value::<Float>(value)
456 .map(|f| f.0)
457 .map_err(|e| e.to_string())
458 }
459
460 fn as_string(value: Value) -> Result<String, String> {
461 serde_json::from_value::<Str>(value)
462 .map(|s| s.0)
463 .map_err(|e| e.to_string())
464 }
465
466 #[test]
467 fn bool_accepts_every_spelling_go_accepts() {
468 for truthy in [
469 json!(true),
470 json!("true"),
471 json!("True"),
472 json!("TRUE"),
473 json!("t"),
474 json!("T"),
475 json!("1"),
476 ] {
477 assert_eq!(as_bool(truthy.clone()), Ok(true), "{truthy}");
478 }
479 for falsy in [
480 json!(false),
481 json!("false"),
482 json!("False"),
483 json!("FALSE"),
484 json!("f"),
485 json!("F"),
486 json!("0"),
487 ] {
488 assert_eq!(as_bool(falsy.clone()), Ok(false), "{falsy}");
489 }
490
491 assert_eq!(as_bool(json!(2)), Ok(true));
493 assert_eq!(as_bool(json!(-1)), Ok(true));
494 assert_eq!(as_bool(json!(1.0)), Ok(true));
495 assert_eq!(as_bool(json!(0.0)), Ok(false));
496 assert_eq!(as_bool(json!(null)), Ok(false));
497 }
498
499 #[test]
500 fn bool_rejects_what_go_rejects() {
501 for rejected in [json!("yes"), json!("on"), json!(""), json!([true]), json!({"a": true})] {
503 assert!(as_bool(rejected.clone()).is_err(), "{rejected}");
504 }
505 }
506
507 #[test]
508 fn integer_accepts_numeric_strings_floats_and_booleans() {
509 assert_eq!(as_int(json!(8125)), Ok(8125));
510 assert_eq!(as_int(json!("8125")), Ok(8125));
511 assert_eq!(as_int(json!(" -7 ")), Ok(-7));
512 assert_eq!(as_int(json!(true)), Ok(1));
513 assert_eq!(as_int(json!(null)), Ok(0));
514
515 assert_eq!(as_int(json!("8125.0")), Ok(8125));
517 assert_eq!(as_int(json!("8125.000")), Ok(8125));
518 assert_eq!(as_int(json!("-8125.0")), Ok(-8125));
519
520 assert_eq!(as_int(json!(10.9)), Ok(10));
522 assert_eq!(as_int(json!(-10.9)), Ok(-10));
523 }
524
525 #[test]
526 fn integer_rejects_unparseable_and_out_of_range_values() {
527 for rejected in [
528 json!("8125ms"),
529 json!(""),
530 json!("0x1f"),
531 json!("8125.5"),
532 json!("8125."),
533 json!(1e300),
534 json!(["8125"]),
535 ] {
536 assert!(as_int(rejected.clone()).is_err(), "{rejected}");
537 }
538 }
539
540 #[test]
541 fn number_accepts_numeric_strings_integers_and_booleans() {
542 assert_eq!(as_float(json!(1.5)), Ok(1.5));
543 assert_eq!(as_float(json!("1.5")), Ok(1.5));
544 assert_eq!(as_float(json!(2)), Ok(2.0));
545 assert_eq!(as_float(json!(true)), Ok(1.0));
546 assert_eq!(as_float(json!(null)), Ok(0.0));
547 assert!(as_float(json!("half")).is_err());
548 }
549
550 #[test]
551 fn string_accepts_every_scalar() {
552 assert_eq!(as_string(json!("datadog_only")), Ok("datadog_only".to_owned()));
553 assert_eq!(as_string(json!(true)), Ok("true".to_owned()));
554 assert_eq!(as_string(json!(false)), Ok("false".to_owned()));
555 assert_eq!(as_string(json!(10485760)), Ok("10485760".to_owned()));
556 assert_eq!(as_string(json!(-1)), Ok("-1".to_owned()));
557 assert_eq!(as_string(json!(10.5)), Ok("10.5".to_owned()));
558 assert_eq!(as_string(json!(null)), Ok(String::new()));
559
560 assert_eq!(as_string(json!(1.0)), Ok("1".to_owned()));
562 }
563
564 #[test]
565 fn string_rejects_compound_values() {
566 for rejected in [json!(["a"]), json!({"a": "b"})] {
567 assert!(as_string(rejected.clone()).is_err(), "{rejected}");
568 }
569 }
570
571 #[test]
572 fn string_map_coerces_scalar_values() {
573 let values = serde_json::from_value::<StringMap>(json!({
574 "bool": true,
575 "integer": 3,
576 "null": null,
577 "string": "datadog_only"
578 }))
579 .expect("scalar values deserialize")
580 .0;
581
582 assert_eq!(values["bool"], "true");
583 assert_eq!(values["integer"], "3");
584 assert_eq!(values["null"], "");
585 assert_eq!(values["string"], "datadog_only");
586 assert!(serde_json::from_value::<StringMap>(json!({ "compound": [] })).is_err());
587 }
588
589 #[test]
590 fn string_map_accepts_a_json_encoded_string() {
591 let values = serde_json::from_value::<StringMap>(json!(r#"{"bool": true, "string": "datadog_only"}"#))
592 .expect("JSON-encoded map deserializes")
593 .0;
594
595 assert_eq!(values["bool"], "true");
596 assert_eq!(values["string"], "datadog_only");
597 for rejected in [json!("not json"), json!(r#"["a"]"#), json!(r#"{"compound": []}"#)] {
598 assert!(
599 serde_json::from_value::<StringMap>(rejected.clone()).is_err(),
600 "{rejected}"
601 );
602 }
603 }
604
605 #[test]
606 fn string_map_reads_null_as_empty() {
607 for empty in [json!(null), json!({})] {
608 let values = serde_json::from_value::<StringMap>(empty.clone())
609 .expect("empty map deserializes")
610 .0;
611 assert!(values.is_empty(), "{empty}");
612 }
613 }
614
615 #[test]
616 fn optional_string_distinguishes_null_from_a_coerced_scalar() {
617 let absent = serde_json::from_value::<OptStr>(json!(null))
618 .expect("null deserializes")
619 .0;
620 assert_eq!(absent, None);
621
622 let coerced = serde_json::from_value::<OptStr>(json!(3))
623 .expect("scalar deserializes")
624 .0;
625 assert_eq!(coerced, Some("3".to_owned()));
626 }
627}