Skip to main content

vexy_json_core/ast/
value.rs

1// this_file: src/ast/value.rs
2
3//! Value types and implementations for the vexy_json AST.
4//!
5//! This module defines the core value types that represent parsed JSON data
6//! in the vexy_json AST. It supports all standard JSON types plus the extensions
7//! provided by vexy_json's forgiving syntax.
8
9use rustc_hash::FxHashMap;
10use std::fmt;
11use std::ops::Index;
12
13/// Represents any valid JSON value.
14///
15/// This enum can hold all JSON data types: null, boolean, number, string,
16/// array, and object.
17#[cfg(feature = "serde")]
18use serde::{Deserialize, Serialize};
19
20#[derive(Debug, Clone, PartialEq)]
21#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22/// Represents any valid JSON value.
23pub enum Value {
24    /// JSON null value.
25    Null,
26    /// JSON boolean value (true or false).
27    Bool(bool),
28    /// JSON numeric value (integer or floating point).
29    Number(Number),
30    /// JSON string value.
31    String(String),
32    /// JSON array containing a sequence of values.
33    Array(Vec<Value>),
34    /// JSON object containing key-value pairs.
35    Object(FxHashMap<String, Value>),
36}
37
38/// Represents a JSON number, which can be either an integer or floating point.
39///
40/// This allows for more efficient representation and operations when the number
41/// is a whole number that fits in an i64.
42#[derive(Debug, Clone, PartialEq)]
43#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
44pub enum Number {
45    /// An integer value that fits in an i64.
46    Integer(i64),
47    /// A floating point value.
48    Float(f64),
49}
50
51impl Number {
52    /// Converts the number to an f64 value.
53    #[inline(always)]
54    pub fn as_f64(&self) -> f64 {
55        match self {
56            Number::Integer(i) => *i as f64,
57            Number::Float(f) => *f,
58        }
59    }
60}
61
62impl Value {
63    /// Returns true if the value is null.
64    #[inline(always)]
65    pub fn is_null(&self) -> bool {
66        matches!(self, Value::Null)
67    }
68
69    /// Returns true if the value is a boolean.
70    #[inline(always)]
71    pub fn is_bool(&self) -> bool {
72        matches!(self, Value::Bool(_))
73    }
74
75    /// Returns true if the value is a number.
76    #[inline(always)]
77    pub fn is_number(&self) -> bool {
78        matches!(self, Value::Number(_))
79    }
80
81    /// Returns true if the value is a string.
82    #[inline(always)]
83    pub fn is_string(&self) -> bool {
84        matches!(self, Value::String(_))
85    }
86
87    /// Returns true if the value is an array.
88    #[inline(always)]
89    pub fn is_array(&self) -> bool {
90        matches!(self, Value::Array(_))
91    }
92
93    /// Returns true if the value is an object.
94    #[inline(always)]
95    pub fn is_object(&self) -> bool {
96        matches!(self, Value::Object(_))
97    }
98
99    /// If the value is a boolean, returns the associated bool.
100    /// Returns None otherwise.
101    pub fn as_bool(&self) -> Option<bool> {
102        match self {
103            Value::Bool(b) => Some(*b),
104            _ => None,
105        }
106    }
107
108    /// If the value is a number, tries to return it as an i64.
109    /// Returns None if the value is not a number or cannot be represented as i64.
110    pub fn as_i64(&self) -> Option<i64> {
111        match self {
112            Value::Number(Number::Integer(i)) => Some(*i),
113            Value::Number(Number::Float(f)) => {
114                if f.fract() == 0.0 && *f >= i64::MIN as f64 && *f <= i64::MAX as f64 {
115                    Some(*f as i64)
116                } else {
117                    None
118                }
119            }
120            _ => None,
121        }
122    }
123
124    /// If the value is a number, returns it as an f64.
125    /// Returns None if the value is not a number.
126    pub fn as_f64(&self) -> Option<f64> {
127        match self {
128            Value::Number(Number::Integer(i)) => Some(*i as f64),
129            Value::Number(Number::Float(f)) => Some(*f),
130            _ => None,
131        }
132    }
133
134    /// If the value is a string, returns the associated str.
135    /// Returns None otherwise.
136    pub fn as_str(&self) -> Option<&str> {
137        match self {
138            Value::String(s) => Some(s),
139            _ => None,
140        }
141    }
142
143    /// If the value is an array, returns a reference to the associated vector.
144    /// Returns None otherwise.
145    pub fn as_array(&self) -> Option<&Vec<Value>> {
146        match self {
147            Value::Array(a) => Some(a),
148            _ => None,
149        }
150    }
151
152    /// If the value is an object, returns a reference to the associated map.
153    /// Returns None otherwise.
154    pub fn as_object(&self) -> Option<&FxHashMap<String, Value>> {
155        match self {
156            Value::Object(o) => Some(o),
157            _ => None,
158        }
159    }
160}
161
162impl fmt::Display for Value {
163    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164        match self {
165            Value::Null => write!(f, "null"),
166            Value::Bool(b) => write!(f, "{b}"),
167            Value::Number(n) => write!(f, "{n}"),
168            Value::String(s) => write!(f, "\"{s}\""),
169            Value::Array(arr) => {
170                write!(f, "[")?;
171                for (i, v) in arr.iter().enumerate() {
172                    if i > 0 {
173                        write!(f, ", ")?;
174                    }
175                    write!(f, "{v}")?;
176                }
177                write!(f, "]")
178            }
179            Value::Object(obj) => {
180                write!(f, "{{")?;
181                for (i, (k, v)) in obj.iter().enumerate() {
182                    if i > 0 {
183                        write!(f, ", ")?;
184                    }
185                    write!(f, "\"{k}\": {v}")?;
186                }
187                write!(f, "}}")
188            }
189        }
190    }
191}
192
193impl fmt::Display for Number {
194    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195        match self {
196            Number::Integer(i) => write!(f, "{i}"),
197            Number::Float(fl) => write!(f, "{fl}"),
198        }
199    }
200}
201
202impl Index<&str> for Value {
203    type Output = Value;
204
205    fn index(&self, index: &str) -> &Self::Output {
206        match self {
207            Value::Object(map) => map.get(index).unwrap_or(&Value::Null),
208            _ => &Value::Null,
209        }
210    }
211}
212
213impl Index<usize> for Value {
214    type Output = Value;
215
216    fn index(&self, index: usize) -> &Self::Output {
217        match self {
218            Value::Array(arr) => arr.get(index).unwrap_or(&Value::Null),
219            _ => &Value::Null,
220        }
221    }
222}
223
224#[cfg(test)]
225mod tests {
226    use super::*;
227
228    #[test]
229    fn test_value_is_type_checkers() {
230        let null_val = Value::Null;
231        let bool_val = Value::Bool(true);
232        let num_val = Value::Number(Number::Integer(42));
233        let str_val = Value::String("hello".to_string());
234        let arr_val = Value::Array(vec![Value::Null]);
235        let obj_val = Value::Object(FxHashMap::default());
236
237        // Test is_null
238        assert!(null_val.is_null());
239        assert!(!bool_val.is_null());
240        assert!(!num_val.is_null());
241        assert!(!str_val.is_null());
242        assert!(!arr_val.is_null());
243        assert!(!obj_val.is_null());
244
245        // Test is_bool
246        assert!(!null_val.is_bool());
247        assert!(bool_val.is_bool());
248        assert!(!num_val.is_bool());
249        assert!(!str_val.is_bool());
250        assert!(!arr_val.is_bool());
251        assert!(!obj_val.is_bool());
252
253        // Test is_number
254        assert!(!null_val.is_number());
255        assert!(!bool_val.is_number());
256        assert!(num_val.is_number());
257        assert!(!str_val.is_number());
258        assert!(!arr_val.is_number());
259        assert!(!obj_val.is_number());
260
261        // Test is_string
262        assert!(!null_val.is_string());
263        assert!(!bool_val.is_string());
264        assert!(!num_val.is_string());
265        assert!(str_val.is_string());
266        assert!(!arr_val.is_string());
267        assert!(!obj_val.is_string());
268
269        // Test is_array
270        assert!(!null_val.is_array());
271        assert!(!bool_val.is_array());
272        assert!(!num_val.is_array());
273        assert!(!str_val.is_array());
274        assert!(arr_val.is_array());
275        assert!(!obj_val.is_array());
276
277        // Test is_object
278        assert!(!null_val.is_object());
279        assert!(!bool_val.is_object());
280        assert!(!num_val.is_object());
281        assert!(!str_val.is_object());
282        assert!(!arr_val.is_object());
283        assert!(obj_val.is_object());
284    }
285
286    #[test]
287    fn test_value_as_bool() {
288        let true_val = Value::Bool(true);
289        let false_val = Value::Bool(false);
290        let null_val = Value::Null;
291        let num_val = Value::Number(Number::Integer(42));
292
293        assert_eq!(true_val.as_bool(), Some(true));
294        assert_eq!(false_val.as_bool(), Some(false));
295        assert_eq!(null_val.as_bool(), None);
296        assert_eq!(num_val.as_bool(), None);
297    }
298
299    #[test]
300    fn test_value_as_i64() {
301        let int_val = Value::Number(Number::Integer(42));
302        let float_val = Value::Number(Number::Float(3.14));
303        let float_int_val = Value::Number(Number::Float(5.0));
304        let large_float_val = Value::Number(Number::Float(1e20));
305        let null_val = Value::Null;
306
307        assert_eq!(int_val.as_i64(), Some(42));
308        assert_eq!(float_val.as_i64(), None); // Has fractional part
309        assert_eq!(float_int_val.as_i64(), Some(5)); // No fractional part
310        assert_eq!(large_float_val.as_i64(), None); // Too large for i64
311        assert_eq!(null_val.as_i64(), None);
312    }
313
314    #[test]
315    fn test_value_as_f64() {
316        let int_val = Value::Number(Number::Integer(42));
317        let float_val = Value::Number(Number::Float(3.14));
318        let null_val = Value::Null;
319        let str_val = Value::String("hello".to_string());
320
321        assert_eq!(int_val.as_f64(), Some(42.0));
322        assert_eq!(float_val.as_f64(), Some(3.14));
323        assert_eq!(null_val.as_f64(), None);
324        assert_eq!(str_val.as_f64(), None);
325    }
326
327    #[test]
328    fn test_value_as_str() {
329        let str_val = Value::String("hello".to_string());
330        let null_val = Value::Null;
331        let num_val = Value::Number(Number::Integer(42));
332
333        assert_eq!(str_val.as_str(), Some("hello"));
334        assert_eq!(null_val.as_str(), None);
335        assert_eq!(num_val.as_str(), None);
336    }
337
338    #[test]
339    fn test_value_as_array() {
340        let arr_val = Value::Array(vec![Value::Null, Value::Bool(true)]);
341        let null_val = Value::Null;
342        let str_val = Value::String("hello".to_string());
343
344        assert_eq!(arr_val.as_array(), Some(&vec![Value::Null, Value::Bool(true)]));
345        assert_eq!(null_val.as_array(), None);
346        assert_eq!(str_val.as_array(), None);
347    }
348
349    #[test]
350    fn test_value_as_object() {
351        let mut map = FxHashMap::default();
352        map.insert("key".to_string(), Value::String("value".to_string()));
353        let obj_val = Value::Object(map.clone());
354        let null_val = Value::Null;
355        let arr_val = Value::Array(vec![]);
356
357        assert_eq!(obj_val.as_object(), Some(&map));
358        assert_eq!(null_val.as_object(), None);
359        assert_eq!(arr_val.as_object(), None);
360    }
361
362    #[test]
363    fn test_value_index_str() {
364        let mut map = FxHashMap::default();
365        map.insert("name".to_string(), Value::String("John".to_string()));
366        map.insert("age".to_string(), Value::Number(Number::Integer(30)));
367        let obj_val = Value::Object(map);
368
369        assert_eq!(obj_val["name"], Value::String("John".to_string()));
370        assert_eq!(obj_val["age"], Value::Number(Number::Integer(30)));
371        assert_eq!(obj_val["missing"], Value::Null);
372
373        // Test indexing non-object returns null
374        let arr_val = Value::Array(vec![]);
375        assert_eq!(arr_val["any"], Value::Null);
376    }
377
378    #[test]
379    fn test_value_index_usize() {
380        let arr_val = Value::Array(vec![
381            Value::String("first".to_string()),
382            Value::Number(Number::Integer(42)),
383            Value::Bool(true),
384        ]);
385
386        assert_eq!(arr_val[0], Value::String("first".to_string()));
387        assert_eq!(arr_val[1], Value::Number(Number::Integer(42)));
388        assert_eq!(arr_val[2], Value::Bool(true));
389        assert_eq!(arr_val[99], Value::Null); // Out of bounds
390
391        // Test indexing non-array returns null
392        let obj_val = Value::Object(FxHashMap::default());
393        assert_eq!(obj_val[0], Value::Null);
394    }
395
396    #[test]
397    fn test_number_as_f64() {
398        let int_num = Number::Integer(42);
399        let float_num = Number::Float(3.14);
400
401        assert_eq!(int_num.as_f64(), 42.0);
402        assert_eq!(float_num.as_f64(), 3.14);
403    }
404
405    #[test]
406    fn test_value_display() {
407        let null_val = Value::Null;
408        let bool_val = Value::Bool(true);
409        let int_val = Value::Number(Number::Integer(42));
410        let float_val = Value::Number(Number::Float(3.14));
411        let str_val = Value::String("hello".to_string());
412
413        assert_eq!(null_val.to_string(), "null");
414        assert_eq!(bool_val.to_string(), "true");
415        assert_eq!(int_val.to_string(), "42");
416        assert_eq!(float_val.to_string(), "3.14");
417        assert_eq!(str_val.to_string(), "\"hello\"");
418    }
419
420    #[test]
421    fn test_value_display_array() {
422        let arr_val = Value::Array(vec![
423            Value::Number(Number::Integer(1)),
424            Value::Number(Number::Integer(2)),
425            Value::Number(Number::Integer(3)),
426        ]);
427
428        assert_eq!(arr_val.to_string(), "[1, 2, 3]");
429
430        let empty_arr = Value::Array(vec![]);
431        assert_eq!(empty_arr.to_string(), "[]");
432    }
433
434    #[test]
435    fn test_value_display_object() {
436        let mut map = FxHashMap::default();
437        map.insert("name".to_string(), Value::String("John".to_string()));
438        let obj_val = Value::Object(map);
439
440        assert_eq!(obj_val.to_string(), "{\"name\": \"John\"}");
441
442        let empty_obj = Value::Object(FxHashMap::default());
443        assert_eq!(empty_obj.to_string(), "{}");
444    }
445
446    #[test]
447    fn test_number_display() {
448        let int_num = Number::Integer(42);
449        let float_num = Number::Float(3.14);
450
451        assert_eq!(int_num.to_string(), "42");
452        assert_eq!(float_num.to_string(), "3.14");
453    }
454
455    #[test]
456    fn test_value_equality() {
457        let val1 = Value::Number(Number::Integer(42));
458        let val2 = Value::Number(Number::Integer(42));
459        let val3 = Value::Number(Number::Integer(43));
460
461        assert_eq!(val1, val2);
462        assert_ne!(val1, val3);
463    }
464
465    #[test]
466    fn test_number_equality() {
467        let int1 = Number::Integer(42);
468        let int2 = Number::Integer(42);
469        let int3 = Number::Integer(43);
470        let float1 = Number::Float(42.0);
471
472        assert_eq!(int1, int2);
473        assert_ne!(int1, int3);
474        assert_ne!(int1, float1); // Different variants are not equal
475    }
476
477    #[test]
478    fn test_as_i64_edge_cases() {
479        // Test max and min i64 values
480        let max_int = Value::Number(Number::Integer(i64::MAX));
481        let min_int = Value::Number(Number::Integer(i64::MIN));
482        
483        assert_eq!(max_int.as_i64(), Some(i64::MAX));
484        assert_eq!(min_int.as_i64(), Some(i64::MIN));
485
486        // Test float edge cases
487        let max_float = Value::Number(Number::Float(i64::MAX as f64));
488        let min_float = Value::Number(Number::Float(i64::MIN as f64));
489        let too_large = Value::Number(Number::Float(1e20));
490        let negative_zero = Value::Number(Number::Float(-0.0));
491
492        assert_eq!(max_float.as_i64(), Some(i64::MAX));
493        assert_eq!(min_float.as_i64(), Some(i64::MIN));
494        assert_eq!(too_large.as_i64(), None);
495        assert_eq!(negative_zero.as_i64(), Some(0));
496    }
497}