Skip to main content

kernel/records/
json_value.rs

1//! A dynamic JSON value used for model parameters, tool arguments, and payloads.
2//!
3//! Unlike `serde_json::Value` this keeps integers and floats distinct yet treats
4//! them as equal when they represent the same number (`Int(2) == Double(2.0)`),
5//! matching the semantics the rest of the kernel relies on when comparing
6//! parameter values. Objects use a `BTreeMap` so serialization is key-sorted and
7//! deterministic.
8
9use std::collections::BTreeMap;
10use std::fmt;
11
12use serde::de::{Deserialize, Deserializer, MapAccess, SeqAccess, Visitor};
13use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer};
14
15const MIN_I64_AS_F64: f64 = i64::MIN as f64;
16const MAX_I64_AS_F64: f64 = i64::MAX as f64;
17
18/// A JSON value with integers and floats kept distinct.
19#[derive(Debug, Clone, Default)]
20pub enum JsonValue {
21    /// JSON `null`.
22    #[default]
23    Null,
24    /// A boolean.
25    Bool(bool),
26    /// An integer.
27    Int(i64),
28    /// A floating-point number. Non-finite values (NaN, infinities) are not valid
29    /// JSON and serialize to `null`; avoid constructing them.
30    Double(f64),
31    /// A string.
32    String(String),
33    /// An array of values.
34    Array(Vec<JsonValue>),
35    /// An object with string keys, kept sorted.
36    Object(BTreeMap<String, JsonValue>),
37}
38
39impl JsonValue {
40    /// Build an object from a fixed set of `(key, value)` pairs.
41    pub fn object<const N: usize>(pairs: [(&str, JsonValue); N]) -> Self {
42        JsonValue::Object(
43            pairs
44                .into_iter()
45                .map(|(key, value)| (key.to_owned(), value))
46                .collect(),
47        )
48    }
49
50    /// The object's fields, if this is an object.
51    pub fn as_object(&self) -> Option<&BTreeMap<String, JsonValue>> {
52        match self {
53            JsonValue::Object(fields) => Some(fields),
54            _ => None,
55        }
56    }
57
58    /// The array's elements, if this is an array.
59    pub fn as_array(&self) -> Option<&[JsonValue]> {
60        match self {
61            JsonValue::Array(items) => Some(items),
62            _ => None,
63        }
64    }
65
66    /// The string, if this is a string.
67    pub fn as_str(&self) -> Option<&str> {
68        match self {
69            JsonValue::String(value) => Some(value),
70            _ => None,
71        }
72    }
73
74    /// The boolean, if this is a boolean.
75    pub fn as_bool(&self) -> Option<bool> {
76        match self {
77            JsonValue::Bool(value) => Some(*value),
78            _ => None,
79        }
80    }
81
82    /// The value as an integer, accepting a finite in-range float (truncated
83    /// toward zero) as well as an integer. A non-finite or out-of-range float
84    /// yields `None` rather than a saturated, misleading value.
85    pub fn as_i64(&self) -> Option<i64> {
86        match self {
87            JsonValue::Int(value) => Some(*value),
88            JsonValue::Double(value)
89                if value.is_finite() && *value >= MIN_I64_AS_F64 && *value < MAX_I64_AS_F64 =>
90            {
91                Some(*value as i64)
92            }
93            _ => None,
94        }
95    }
96
97    /// The value as a float, accepting an integer as well as a float.
98    pub fn as_f64(&self) -> Option<f64> {
99        match self {
100            JsonValue::Double(value) => Some(*value),
101            JsonValue::Int(value) => Some(*value as f64),
102            _ => None,
103        }
104    }
105}
106
107impl From<bool> for JsonValue {
108    fn from(value: bool) -> Self {
109        JsonValue::Bool(value)
110    }
111}
112
113impl From<i64> for JsonValue {
114    fn from(value: i64) -> Self {
115        JsonValue::Int(value)
116    }
117}
118
119impl From<f64> for JsonValue {
120    fn from(value: f64) -> Self {
121        JsonValue::Double(value)
122    }
123}
124
125impl From<&str> for JsonValue {
126    fn from(value: &str) -> Self {
127        JsonValue::String(value.to_owned())
128    }
129}
130
131impl From<String> for JsonValue {
132    fn from(value: String) -> Self {
133        JsonValue::String(value)
134    }
135}
136
137impl From<Vec<JsonValue>> for JsonValue {
138    fn from(value: Vec<JsonValue>) -> Self {
139        JsonValue::Array(value)
140    }
141}
142
143impl From<BTreeMap<String, JsonValue>> for JsonValue {
144    fn from(value: BTreeMap<String, JsonValue>) -> Self {
145        JsonValue::Object(value)
146    }
147}
148
149impl PartialEq for JsonValue {
150    fn eq(&self, other: &Self) -> bool {
151        use JsonValue::{Array, Bool, Double, Int, Null, Object, String};
152        match (self, other) {
153            (Null, Null) => true,
154            (Bool(a), Bool(b)) => a == b,
155            (String(a), String(b)) => a == b,
156            (Array(a), Array(b)) => a == b,
157            (Object(a), Object(b)) => a == b,
158            (Int(a), Int(b)) => a == b,
159            (Double(a), Double(b)) => a == b,
160            (Int(a), Double(b)) => (*a as f64) == *b,
161            (Double(a), Int(b)) => *a == (*b as f64),
162            _ => false,
163        }
164    }
165}
166
167impl Serialize for JsonValue {
168    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
169        match self {
170            JsonValue::Null => serializer.serialize_unit(),
171            JsonValue::Bool(value) => serializer.serialize_bool(*value),
172            JsonValue::Int(value) => serializer.serialize_i64(*value),
173            JsonValue::Double(value) => serializer.serialize_f64(*value),
174            JsonValue::String(value) => serializer.serialize_str(value),
175            JsonValue::Array(items) => {
176                let mut seq = serializer.serialize_seq(Some(items.len()))?;
177                for item in items {
178                    seq.serialize_element(item)?;
179                }
180                seq.end()
181            }
182            JsonValue::Object(fields) => {
183                let mut map = serializer.serialize_map(Some(fields.len()))?;
184                for (key, value) in fields {
185                    map.serialize_entry(key, value)?;
186                }
187                map.end()
188            }
189        }
190    }
191}
192
193impl<'de> Deserialize<'de> for JsonValue {
194    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
195        deserializer.deserialize_any(JsonValueVisitor)
196    }
197}
198
199struct JsonValueVisitor;
200
201impl<'de> Visitor<'de> for JsonValueVisitor {
202    type Value = JsonValue;
203
204    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
205        formatter.write_str("any JSON value")
206    }
207
208    fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E> {
209        Ok(JsonValue::Bool(value))
210    }
211
212    fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> {
213        Ok(JsonValue::Int(value))
214    }
215
216    fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> {
217        if value <= i64::MAX as u64 {
218            Ok(JsonValue::Int(value as i64))
219        } else {
220            Ok(JsonValue::Double(value as f64))
221        }
222    }
223
224    fn visit_i128<E>(self, value: i128) -> Result<Self::Value, E> {
225        Ok(i64::try_from(value).map_or_else(|_| JsonValue::Double(value as f64), JsonValue::Int))
226    }
227
228    fn visit_u128<E>(self, value: u128) -> Result<Self::Value, E> {
229        Ok(i64::try_from(value).map_or_else(|_| JsonValue::Double(value as f64), JsonValue::Int))
230    }
231
232    fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E> {
233        Ok(JsonValue::Double(value))
234    }
235
236    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> {
237        Ok(JsonValue::String(value.to_owned()))
238    }
239
240    fn visit_string<E>(self, value: String) -> Result<Self::Value, E> {
241        Ok(JsonValue::String(value))
242    }
243
244    fn visit_none<E>(self) -> Result<Self::Value, E> {
245        Ok(JsonValue::Null)
246    }
247
248    fn visit_unit<E>(self) -> Result<Self::Value, E> {
249        Ok(JsonValue::Null)
250    }
251
252    fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
253        let mut items = Vec::new();
254        while let Some(item) = seq.next_element()? {
255            items.push(item);
256        }
257        Ok(JsonValue::Array(items))
258    }
259
260    fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
261        let mut fields = BTreeMap::new();
262        while let Some((key, value)) = map.next_entry::<String, JsonValue>()? {
263            fields.insert(key, value);
264        }
265        Ok(JsonValue::Object(fields))
266    }
267}