Skip to main content

json_traits/
scalar.rs

1use std::fmt;
2
3use serde_json::Number;
4
5/// A leaf JSON value reached by a dotted path.
6///
7/// Objects and arrays are not leaves. [`crate::JsonPaths`] walks them and
8/// records only the scalars inside. [`Self::Undefined`] appears only in a
9/// diff, for a path that exists on one side.
10///
11/// Integer `1` and decimal `1.0` compare equal. Two integers compare exactly,
12/// so a pair of large integers that would collapse to the same float stay
13/// different.
14#[derive(Debug, Clone)]
15pub enum JsonScalar {
16    /// JSON `null`.
17    Null,
18    /// JSON `true` or `false`.
19    Bool(bool),
20    /// JSON number.
21    Number(Number),
22    /// JSON string.
23    String(String),
24    /// The path is missing on this side of a diff.
25    Undefined,
26}
27
28impl JsonScalar {
29    /// A JSON number from an integer.
30    #[must_use]
31    pub fn from_integer(value: i64) -> Self {
32        Self::Number(Number::from(value))
33    }
34
35    /// A JSON number from a finite float.
36    ///
37    /// Returns [`None`] for NaN and infinity, which are not JSON numbers.
38    #[must_use]
39    pub fn from_float(value: f64) -> Option<Self> {
40        Number::from_f64(value).map(Self::Number)
41    }
42}
43
44impl PartialEq for JsonScalar {
45    fn eq(&self, other: &Self) -> bool {
46        match (self, other) {
47            (Self::Null, Self::Null) | (Self::Undefined, Self::Undefined) => true,
48            (Self::Bool(left), Self::Bool(right)) => left == right,
49            (Self::String(left), Self::String(right)) => left == right,
50            (Self::Number(left), Self::Number(right)) => numbers_equal(left, right),
51            _ => false,
52        }
53    }
54}
55
56impl fmt::Display for JsonScalar {
57    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
58        match self {
59            Self::Null => formatter.write_str("null"),
60            Self::Bool(value) => write!(formatter, "{value}"),
61            Self::Number(value) => write!(formatter, "{value}"),
62            Self::String(value) => write!(formatter, "{value:?}"),
63            Self::Undefined => formatter.write_str("undefined"),
64        }
65    }
66}
67
68impl From<bool> for JsonScalar {
69    fn from(value: bool) -> Self {
70        Self::Bool(value)
71    }
72}
73
74impl From<&str> for JsonScalar {
75    fn from(value: &str) -> Self {
76        Self::String(value.to_owned())
77    }
78}
79
80impl From<String> for JsonScalar {
81    fn from(value: String) -> Self {
82        Self::String(value)
83    }
84}
85
86fn numbers_equal(left: &Number, right: &Number) -> bool {
87    if let (Some(left), Some(right)) = (left.as_i64(), right.as_i64()) {
88        return left == right;
89    }
90    if let (Some(left), Some(right)) = (left.as_u64(), right.as_u64()) {
91        return left == right;
92    }
93    match (left.as_f64(), right.as_f64()) {
94        (Some(left), Some(right)) => left == right,
95        _ => false,
96    }
97}