Skip to main content

json_steroids/
value.rs

1//! Dynamic JSON value type
2//!
3//! Provides a flexible representation for JSON values when
4//! the structure is not known at compile time.
5
6use std::ops::Index;
7
8/// A dynamic JSON value
9#[derive(Debug, Clone, PartialEq, Default)]
10pub enum JsonValue {
11    /// Null value
12    #[default]
13    Null,
14    /// Boolean value
15    Bool(bool),
16    /// Integer value (fits in i64)
17    Integer(i64),
18    /// Floating point value
19    Float(f64),
20    /// String value
21    String(String),
22    /// Array of values
23    Array(Vec<JsonValue>),
24    /// Object (key-value pairs)
25    Object(Vec<(String, JsonValue)>),
26}
27
28impl JsonValue {
29    /// Check if value is null
30    #[inline]
31    pub fn is_null(&self) -> bool {
32        matches!(self, JsonValue::Null)
33    }
34
35    /// Check if value is a boolean
36    #[inline]
37    pub fn is_bool(&self) -> bool {
38        matches!(self, JsonValue::Bool(_))
39    }
40
41    /// Check if value is a number (integer or float)
42    #[inline]
43    pub fn is_number(&self) -> bool {
44        matches!(self, JsonValue::Integer(_) | JsonValue::Float(_))
45    }
46
47    /// Check if value is an integer
48    #[inline]
49    pub fn is_integer(&self) -> bool {
50        matches!(self, JsonValue::Integer(_))
51    }
52
53    /// Check if value is a float
54    #[inline]
55    pub fn is_float(&self) -> bool {
56        matches!(self, JsonValue::Float(_))
57    }
58
59    /// Check if value is a string
60    #[inline]
61    pub fn is_string(&self) -> bool {
62        matches!(self, JsonValue::String(_))
63    }
64
65    /// Check if value is an array
66    #[inline]
67    pub fn is_array(&self) -> bool {
68        matches!(self, JsonValue::Array(_))
69    }
70
71    /// Check if value is an object
72    #[inline]
73    pub fn is_object(&self) -> bool {
74        matches!(self, JsonValue::Object(_))
75    }
76
77    /// Get as boolean
78    #[inline]
79    pub fn as_bool(&self) -> Option<bool> {
80        match self {
81            JsonValue::Bool(b) => Some(*b),
82            _ => None,
83        }
84    }
85
86    /// Get as i64
87    #[inline]
88    pub fn as_i64(&self) -> Option<i64> {
89        match self {
90            JsonValue::Integer(n) => Some(*n),
91            JsonValue::Float(f) => Some(*f as i64),
92            _ => None,
93        }
94    }
95
96    /// Get as u64
97    #[inline]
98    pub fn as_u64(&self) -> Option<u64> {
99        match self {
100            JsonValue::Integer(n) if *n >= 0 => Some(*n as u64),
101            JsonValue::Float(f) if *f >= 0.0 => Some(*f as u64),
102            _ => None,
103        }
104    }
105
106    /// Get as f64
107    #[inline]
108    pub fn as_f64(&self) -> Option<f64> {
109        match self {
110            JsonValue::Float(f) => Some(*f),
111            JsonValue::Integer(n) => Some(*n as f64),
112            _ => None,
113        }
114    }
115
116    /// Get as string slice
117    #[inline]
118    pub fn as_str(&self) -> Option<&str> {
119        match self {
120            JsonValue::String(s) => Some(s),
121            _ => None,
122        }
123    }
124
125    /// Get as array slice
126    #[inline]
127    pub fn as_array(&self) -> Option<&[JsonValue]> {
128        match self {
129            JsonValue::Array(arr) => Some(arr),
130            _ => None,
131        }
132    }
133
134    /// Get as mutable array
135    #[inline]
136    pub fn as_array_mut(&mut self) -> Option<&mut Vec<JsonValue>> {
137        match self {
138            JsonValue::Array(arr) => Some(arr),
139            _ => None,
140        }
141    }
142
143    /// Get as object slice
144    #[inline]
145    pub fn as_object(&self) -> Option<&[(String, JsonValue)]> {
146        match self {
147            JsonValue::Object(obj) => Some(obj),
148            _ => None,
149        }
150    }
151
152    /// Get as mutable object
153    #[inline]
154    pub fn as_object_mut(&mut self) -> Option<&mut Vec<(String, JsonValue)>> {
155        match self {
156            JsonValue::Object(obj) => Some(obj),
157            _ => None,
158        }
159    }
160
161    /// Get a value from an object by key
162    #[inline]
163    pub fn get(&self, key: &str) -> Option<&JsonValue> {
164        match self {
165            JsonValue::Object(obj) => obj.iter().find(|(k, _)| k == key).map(|(_, v)| v),
166            _ => None,
167        }
168    }
169
170    /// Get a value from an array by index
171    #[inline]
172    pub fn get_index(&self, index: usize) -> Option<&JsonValue> {
173        match self {
174            JsonValue::Array(arr) => arr.get(index),
175            _ => None,
176        }
177    }
178
179    /// Get mutable reference to a value from an object by key
180    #[inline]
181    pub fn get_mut(&mut self, key: &str) -> Option<&mut JsonValue> {
182        match self {
183            JsonValue::Object(obj) => obj.iter_mut().find(|(k, _)| k == key).map(|(_, v)| v),
184            _ => None,
185        }
186    }
187
188    /// Take ownership of a string value
189    #[inline]
190    pub fn into_string(self) -> Option<String> {
191        match self {
192            JsonValue::String(s) => Some(s),
193            _ => None,
194        }
195    }
196
197    /// Take ownership of an array value
198    #[inline]
199    pub fn into_array(self) -> Option<Vec<JsonValue>> {
200        match self {
201            JsonValue::Array(arr) => Some(arr),
202            _ => None,
203        }
204    }
205
206    /// Take ownership of an object value
207    #[inline]
208    pub fn into_object(self) -> Option<Vec<(String, JsonValue)>> {
209        match self {
210            JsonValue::Object(obj) => Some(obj),
211            _ => None,
212        }
213    }
214}
215
216/// Static null value for index out of bounds
217static NULL: JsonValue = JsonValue::Null;
218
219impl Index<&str> for JsonValue {
220    type Output = JsonValue;
221
222    fn index(&self, key: &str) -> &Self::Output {
223        self.get(key).unwrap_or(&NULL)
224    }
225}
226
227impl Index<usize> for JsonValue {
228    type Output = JsonValue;
229
230    fn index(&self, index: usize) -> &Self::Output {
231        self.get_index(index).unwrap_or(&NULL)
232    }
233}
234
235impl From<bool> for JsonValue {
236    fn from(v: bool) -> Self {
237        JsonValue::Bool(v)
238    }
239}
240
241impl From<i32> for JsonValue {
242    fn from(v: i32) -> Self {
243        JsonValue::Integer(v as i64)
244    }
245}
246
247impl From<i64> for JsonValue {
248    fn from(v: i64) -> Self {
249        JsonValue::Integer(v)
250    }
251}
252
253impl From<u32> for JsonValue {
254    fn from(v: u32) -> Self {
255        JsonValue::Integer(v as i64)
256    }
257}
258
259impl From<u64> for JsonValue {
260    fn from(v: u64) -> Self {
261        JsonValue::Integer(v as i64)
262    }
263}
264
265impl From<f32> for JsonValue {
266    fn from(v: f32) -> Self {
267        JsonValue::Float(v as f64)
268    }
269}
270
271impl From<f64> for JsonValue {
272    fn from(v: f64) -> Self {
273        JsonValue::Float(v)
274    }
275}
276
277impl From<String> for JsonValue {
278    fn from(v: String) -> Self {
279        JsonValue::String(v)
280    }
281}
282
283impl From<&str> for JsonValue {
284    fn from(v: &str) -> Self {
285        JsonValue::String(v.to_string())
286    }
287}
288
289impl<T: Into<JsonValue>> From<Vec<T>> for JsonValue {
290    fn from(v: Vec<T>) -> Self {
291        JsonValue::Array(v.into_iter().map(Into::into).collect())
292    }
293}
294
295impl<T: Into<JsonValue>> From<Option<T>> for JsonValue {
296    fn from(v: Option<T>) -> Self {
297        match v {
298            Some(v) => v.into(),
299            None => JsonValue::Null,
300        }
301    }
302}
303
304#[cfg(test)]
305mod tests {
306    use super::*;
307
308    #[test]
309    fn test_is_methods() {
310        assert!(JsonValue::Null.is_null());
311        assert!(JsonValue::Bool(true).is_bool());
312        assert!(JsonValue::Integer(42).is_integer());
313        assert!(JsonValue::Float(3.14).is_float());
314        assert!(JsonValue::String("test".into()).is_string());
315        assert!(JsonValue::Array(vec![]).is_array());
316        assert!(JsonValue::Object(vec![]).is_object());
317    }
318
319    #[test]
320    fn test_as_methods() {
321        assert_eq!(JsonValue::Bool(true).as_bool(), Some(true));
322        assert_eq!(JsonValue::Integer(42).as_i64(), Some(42));
323        assert_eq!(JsonValue::Float(3.14).as_f64(), Some(3.14));
324        assert_eq!(JsonValue::String("test".into()).as_str(), Some("test"));
325    }
326
327    #[test]
328    fn test_index_object() {
329        let obj = JsonValue::Object(vec![
330            ("name".into(), JsonValue::String("test".into())),
331            ("value".into(), JsonValue::Integer(42)),
332        ]);
333
334        assert_eq!(obj["name"].as_str(), Some("test"));
335        assert_eq!(obj["value"].as_i64(), Some(42));
336        assert!(obj["missing"].is_null());
337    }
338
339    #[test]
340    fn test_index_array() {
341        let arr = JsonValue::Array(vec![
342            JsonValue::Integer(1),
343            JsonValue::Integer(2),
344            JsonValue::Integer(3),
345        ]);
346
347        assert_eq!(arr[0].as_i64(), Some(1));
348        assert_eq!(arr[1].as_i64(), Some(2));
349        assert!(arr[10].is_null());
350    }
351}