Skip to main content

jsonata_core/
value.rs

1// JValue: Rc-wrapped value type for O(1) cloning
2// Replaces serde_json::Value for internal use
3
4use std::fmt;
5use std::rc::Rc;
6
7use indexmap::IndexMap;
8use serde::de::{self, Deserializer, MapAccess, SeqAccess, Visitor};
9use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer};
10
11/// A JSON-like value with O(1) clone semantics via Rc-wrapping.
12///
13/// Standard JSON types (Array, Object, String) are wrapped in Rc for cheap cloning.
14/// Internal types (Undefined, Lambda, Builtin, Regex) are first-class variants
15/// instead of tagged objects with hash-map lookups.
16#[derive(Clone, Debug)]
17pub enum JValue {
18    // Standard JSON types
19    Null,
20    Bool(bool),
21    Number(f64),
22    String(Rc<str>),
23    Array(Rc<Vec<JValue>>),
24    Object(Rc<IndexMap<String, JValue>>),
25
26    // Internal types (previously tagged objects)
27    Undefined,
28    Lambda {
29        lambda_id: Rc<str>,
30        params: Rc<Vec<String>>,
31        name: Option<Rc<str>>,
32        signature: Option<Rc<str>>,
33    },
34    Builtin {
35        name: Rc<str>,
36    },
37    Regex {
38        pattern: Rc<str>,
39        flags: Rc<str>,
40    },
41}
42
43// ── Type checks ──────────────────────────────────────────────────────────────
44
45impl JValue {
46    #[inline]
47    pub fn is_null(&self) -> bool {
48        matches!(self, JValue::Null)
49    }
50
51    #[inline]
52    pub fn is_undefined(&self) -> bool {
53        matches!(self, JValue::Undefined)
54    }
55
56    #[inline]
57    pub fn is_bool(&self) -> bool {
58        matches!(self, JValue::Bool(_))
59    }
60
61    #[inline]
62    pub fn is_number(&self) -> bool {
63        matches!(self, JValue::Number(_))
64    }
65
66    #[inline]
67    pub fn is_string(&self) -> bool {
68        matches!(self, JValue::String(_))
69    }
70
71    #[inline]
72    pub fn is_array(&self) -> bool {
73        matches!(self, JValue::Array(_))
74    }
75
76    #[inline]
77    pub fn is_object(&self) -> bool {
78        matches!(self, JValue::Object(_))
79    }
80
81    #[inline]
82    pub fn is_lambda(&self) -> bool {
83        matches!(self, JValue::Lambda { .. })
84    }
85
86    #[inline]
87    pub fn is_builtin(&self) -> bool {
88        matches!(self, JValue::Builtin { .. })
89    }
90
91    #[inline]
92    pub fn is_function(&self) -> bool {
93        matches!(self, JValue::Lambda { .. } | JValue::Builtin { .. })
94    }
95
96    #[inline]
97    pub fn is_regex(&self) -> bool {
98        matches!(self, JValue::Regex { .. })
99    }
100}
101
102// ── Extraction ───────────────────────────────────────────────────────────────
103
104impl JValue {
105    #[inline]
106    pub fn as_f64(&self) -> Option<f64> {
107        match self {
108            JValue::Number(n) => Some(*n),
109            _ => None,
110        }
111    }
112
113    #[inline]
114    pub fn as_i64(&self) -> Option<i64> {
115        match self {
116            JValue::Number(n) => {
117                let f = *n;
118                if f.fract() == 0.0 && f >= i64::MIN as f64 && f <= i64::MAX as f64 {
119                    Some(f as i64)
120                } else {
121                    None
122                }
123            }
124            _ => None,
125        }
126    }
127
128    #[inline]
129    pub fn as_str(&self) -> Option<&str> {
130        match self {
131            JValue::String(s) => Some(s),
132            _ => None,
133        }
134    }
135
136    #[inline]
137    pub fn as_bool(&self) -> Option<bool> {
138        match self {
139            JValue::Bool(b) => Some(*b),
140            _ => None,
141        }
142    }
143
144    #[inline]
145    pub fn as_array(&self) -> Option<&Vec<JValue>> {
146        match self {
147            JValue::Array(arr) => Some(arr),
148            _ => None,
149        }
150    }
151
152    #[inline]
153    pub fn as_object(&self) -> Option<&IndexMap<String, JValue>> {
154        match self {
155            JValue::Object(map) => Some(map),
156            _ => None,
157        }
158    }
159
160    /// Get a mutable reference to the inner Vec, cloning if shared (Rc::make_mut).
161    #[inline]
162    pub fn as_array_mut(&mut self) -> Option<&mut Vec<JValue>> {
163        match self {
164            JValue::Array(arr) => Some(Rc::make_mut(arr)),
165            _ => None,
166        }
167    }
168
169    /// Get a mutable reference to the inner IndexMap, cloning if shared (Rc::make_mut).
170    #[inline]
171    pub fn as_object_mut(&mut self) -> Option<&mut IndexMap<String, JValue>> {
172        match self {
173            JValue::Object(map) => Some(Rc::make_mut(map)),
174            _ => None,
175        }
176    }
177
178    /// Get a reference to the Rc<str> for the string variant.
179    #[inline]
180    pub fn as_rc_str(&self) -> Option<&Rc<str>> {
181        match self {
182            JValue::String(s) => Some(s),
183            _ => None,
184        }
185    }
186
187    /// Index into an object by key.
188    #[inline]
189    pub fn get(&self, key: &str) -> Option<&JValue> {
190        match self {
191            JValue::Object(map) => map.get(key),
192            _ => None,
193        }
194    }
195
196    /// Index into an array by position.
197    #[inline]
198    pub fn get_index(&self, index: usize) -> Option<&JValue> {
199        match self {
200            JValue::Array(arr) => arr.get(index),
201            _ => None,
202        }
203    }
204}
205
206// ── Constructors ─────────────────────────────────────────────────────────────
207
208impl JValue {
209    #[inline]
210    pub fn from_i64(n: i64) -> Self {
211        JValue::Number(n as f64)
212    }
213
214    #[inline]
215    pub fn from_f64(n: f64) -> Self {
216        JValue::Number(n)
217    }
218
219    #[inline]
220    pub fn string(s: impl Into<Rc<str>>) -> Self {
221        JValue::String(s.into())
222    }
223
224    #[inline]
225    pub fn array(v: Vec<JValue>) -> Self {
226        JValue::Array(Rc::new(v))
227    }
228
229    #[inline]
230    pub fn object(m: IndexMap<String, JValue>) -> Self {
231        JValue::Object(Rc::new(m))
232    }
233
234    #[inline]
235    pub fn lambda(
236        lambda_id: impl Into<Rc<str>>,
237        params: Vec<String>,
238        name: Option<impl Into<Rc<str>>>,
239        signature: Option<impl Into<Rc<str>>>,
240    ) -> Self {
241        JValue::Lambda {
242            lambda_id: lambda_id.into(),
243            params: Rc::new(params),
244            name: name.map(|n| n.into()),
245            signature: signature.map(|s| s.into()),
246        }
247    }
248
249    #[inline]
250    pub fn builtin(name: impl Into<Rc<str>>) -> Self {
251        JValue::Builtin { name: name.into() }
252    }
253
254    #[inline]
255    pub fn regex(pattern: impl Into<Rc<str>>, flags: impl Into<Rc<str>>) -> Self {
256        JValue::Regex {
257            pattern: pattern.into(),
258            flags: flags.into(),
259        }
260    }
261}
262
263// ── From impls ───────────────────────────────────────────────────────────────
264
265impl From<bool> for JValue {
266    #[inline]
267    fn from(b: bool) -> Self {
268        JValue::Bool(b)
269    }
270}
271
272impl From<i64> for JValue {
273    #[inline]
274    fn from(n: i64) -> Self {
275        JValue::Number(n as f64)
276    }
277}
278
279impl From<i32> for JValue {
280    #[inline]
281    fn from(n: i32) -> Self {
282        JValue::Number(n as f64)
283    }
284}
285
286impl From<u64> for JValue {
287    #[inline]
288    fn from(n: u64) -> Self {
289        JValue::Number(n as f64)
290    }
291}
292
293impl From<usize> for JValue {
294    #[inline]
295    fn from(n: usize) -> Self {
296        JValue::Number(n as f64)
297    }
298}
299
300impl From<f64> for JValue {
301    #[inline]
302    fn from(n: f64) -> Self {
303        JValue::Number(n)
304    }
305}
306
307impl From<&str> for JValue {
308    #[inline]
309    fn from(s: &str) -> Self {
310        JValue::String(s.into())
311    }
312}
313
314impl From<String> for JValue {
315    #[inline]
316    fn from(s: String) -> Self {
317        JValue::String(s.into())
318    }
319}
320
321impl From<Rc<str>> for JValue {
322    #[inline]
323    fn from(s: Rc<str>) -> Self {
324        JValue::String(s)
325    }
326}
327
328impl From<Vec<JValue>> for JValue {
329    #[inline]
330    fn from(v: Vec<JValue>) -> Self {
331        JValue::Array(Rc::new(v))
332    }
333}
334
335impl From<IndexMap<String, JValue>> for JValue {
336    #[inline]
337    fn from(m: IndexMap<String, JValue>) -> Self {
338        JValue::Object(Rc::new(m))
339    }
340}
341
342// ── PartialEq ────────────────────────────────────────────────────────────────
343
344impl PartialEq for JValue {
345    fn eq(&self, other: &Self) -> bool {
346        match (self, other) {
347            (JValue::Null, JValue::Null) => true,
348            (JValue::Undefined, JValue::Undefined) => true,
349            (JValue::Bool(a), JValue::Bool(b)) => a == b,
350            (JValue::Number(a), JValue::Number(b)) => {
351                // Handle NaN: NaN != NaN
352                if a.is_nan() && b.is_nan() {
353                    return false;
354                }
355                a == b
356            }
357            (JValue::String(a), JValue::String(b)) => a == b,
358            (JValue::Array(a), JValue::Array(b)) => a == b,
359            (JValue::Object(a), JValue::Object(b)) => a == b,
360            (JValue::Lambda { lambda_id: a, .. }, JValue::Lambda { lambda_id: b, .. }) => a == b,
361            (JValue::Builtin { name: a }, JValue::Builtin { name: b }) => a == b,
362            (
363                JValue::Regex {
364                    pattern: ap,
365                    flags: af,
366                },
367                JValue::Regex {
368                    pattern: bp,
369                    flags: bf,
370                },
371            ) => ap == bp && af == bf,
372            _ => false,
373        }
374    }
375}
376
377// ── Display ──────────────────────────────────────────────────────────────────
378
379impl fmt::Display for JValue {
380    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
381        match self {
382            JValue::Null => write!(f, "null"),
383            JValue::Undefined => write!(f, "undefined"),
384            JValue::Bool(b) => write!(f, "{}", b),
385            JValue::Number(n) => format_number(*n, f),
386            JValue::String(s) => write!(f, "\"{}\"", escape_json_string(s)),
387            JValue::Array(arr) => {
388                write!(f, "[")?;
389                for (i, v) in arr.iter().enumerate() {
390                    if i > 0 {
391                        write!(f, ",")?;
392                    }
393                    write!(f, "{}", v)?;
394                }
395                write!(f, "]")
396            }
397            JValue::Object(map) => {
398                write!(f, "{{")?;
399                for (i, (k, v)) in map.iter().enumerate() {
400                    if i > 0 {
401                        write!(f, ",")?;
402                    }
403                    write!(f, "\"{}\":{}", escape_json_string(k), v)?;
404                }
405                write!(f, "}}")
406            }
407            JValue::Lambda { lambda_id, .. } => write!(f, "\"<lambda:{}>\"", lambda_id),
408            JValue::Builtin { name } => write!(f, "\"<builtin:{}>\"", name),
409            JValue::Regex { pattern, flags } => write!(f, "\"<regex:/{}/{}>\"", pattern, flags),
410        }
411    }
412}
413
414fn escape_json_string(s: &str) -> String {
415    let mut result = String::with_capacity(s.len());
416    for c in s.chars() {
417        match c {
418            '"' => result.push_str("\\\""),
419            '\\' => result.push_str("\\\\"),
420            '\n' => result.push_str("\\n"),
421            '\r' => result.push_str("\\r"),
422            '\t' => result.push_str("\\t"),
423            c if c < '\x20' => {
424                result.push_str(&format!("\\u{:04x}", c as u32));
425            }
426            c => result.push(c),
427        }
428    }
429    result
430}
431
432fn format_number(n: f64, f: &mut fmt::Formatter<'_>) -> fmt::Result {
433    if !n.is_finite() {
434        // NaN and +/-Infinity serialize as null (matching JSON spec)
435        write!(f, "null")
436    } else if n.fract() == 0.0 && n.abs() < 1e20 {
437        write!(f, "{}", n as i64)
438    } else {
439        // Use serde_json's number formatting for consistency
440        write!(f, "{}", n)
441    }
442}
443
444// ── Serialization (for JSON output via evaluate_json) ────────────────────────
445
446impl Serialize for JValue {
447    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
448    where
449        S: Serializer,
450    {
451        match self {
452            JValue::Null => serializer.serialize_none(),
453            JValue::Undefined => serializer.serialize_none(),
454            JValue::Bool(b) => serializer.serialize_bool(*b),
455            JValue::Number(n) => {
456                if n.is_nan() || n.is_infinite() {
457                    serializer.serialize_none()
458                } else if n.fract() == 0.0 && *n >= i64::MIN as f64 && *n <= i64::MAX as f64 {
459                    serializer.serialize_i64(*n as i64)
460                } else {
461                    serializer.serialize_f64(*n)
462                }
463            }
464            JValue::String(s) => serializer.serialize_str(s),
465            JValue::Array(arr) => {
466                let mut seq = serializer.serialize_seq(Some(arr.len()))?;
467                for v in arr.iter() {
468                    seq.serialize_element(v)?;
469                }
470                seq.end()
471            }
472            JValue::Object(map) => {
473                let mut m = serializer.serialize_map(Some(map.len()))?;
474                for (k, v) in map.iter() {
475                    m.serialize_entry(k, v)?;
476                }
477                m.end()
478            }
479            JValue::Lambda { .. } => serializer.serialize_str(""),
480            JValue::Builtin { .. } => serializer.serialize_str(""),
481            JValue::Regex { pattern, flags } => {
482                let mut m = serializer.serialize_map(Some(2))?;
483                m.serialize_entry("pattern", &**pattern)?;
484                m.serialize_entry("flags", &**flags)?;
485                m.end()
486            }
487        }
488    }
489}
490
491// ── Deserialization (single-pass JSON→JValue) ────────────────────────────────
492
493impl<'de> serde::Deserialize<'de> for JValue {
494    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
495    where
496        D: Deserializer<'de>,
497    {
498        deserializer.deserialize_any(JValueVisitor)
499    }
500}
501
502struct JValueVisitor;
503
504impl<'de> Visitor<'de> for JValueVisitor {
505    type Value = JValue;
506
507    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
508        write!(f, "any valid JSON value")
509    }
510
511    fn visit_bool<E: de::Error>(self, v: bool) -> Result<JValue, E> {
512        Ok(JValue::Bool(v))
513    }
514
515    fn visit_i64<E: de::Error>(self, v: i64) -> Result<JValue, E> {
516        Ok(JValue::Number(v as f64))
517    }
518
519    fn visit_u64<E: de::Error>(self, v: u64) -> Result<JValue, E> {
520        Ok(JValue::Number(v as f64))
521    }
522
523    fn visit_f64<E: de::Error>(self, v: f64) -> Result<JValue, E> {
524        Ok(JValue::Number(v))
525    }
526
527    fn visit_str<E: de::Error>(self, v: &str) -> Result<JValue, E> {
528        Ok(JValue::string(v))
529    }
530
531    fn visit_string<E: de::Error>(self, v: String) -> Result<JValue, E> {
532        Ok(JValue::String(v.into()))
533    }
534
535    fn visit_none<E: de::Error>(self) -> Result<JValue, E> {
536        Ok(JValue::Null)
537    }
538
539    fn visit_unit<E: de::Error>(self) -> Result<JValue, E> {
540        Ok(JValue::Null)
541    }
542
543    fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<JValue, A::Error> {
544        let mut vec = Vec::with_capacity(seq.size_hint().unwrap_or(0));
545        while let Some(elem) = seq.next_element()? {
546            vec.push(elem);
547        }
548        Ok(JValue::array(vec))
549    }
550
551    fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<JValue, A::Error> {
552        let mut m = IndexMap::with_capacity(map.size_hint().unwrap_or(0));
553        while let Some((k, v)) = map.next_entry()? {
554            m.insert(k, v);
555        }
556        Ok(JValue::object(m))
557    }
558}
559
560// ── JSON string I/O ──────────────────────────────────────────────────────────
561
562impl JValue {
563    /// Serialize to a JSON string.
564    pub fn to_json_string(&self) -> Result<String, serde_json::Error> {
565        serde_json::to_string(self)
566    }
567
568    /// Serialize to a pretty-printed JSON string.
569    pub fn to_json_string_pretty(&self) -> Result<String, serde_json::Error> {
570        serde_json::to_string_pretty(self)
571    }
572
573    /// Parse a JSON string into a JValue (single-pass, no intermediate serde_json::Value).
574    ///
575    /// When the `simd` feature is enabled, uses simd-json for 2-4x faster parsing
576    /// on CPUs with SIMD support (SSE4.2/AVX2/NEON). Falls back to serde_json otherwise.
577    pub fn from_json_str(s: &str) -> Result<JValue, serde_json::Error> {
578        #[cfg(feature = "simd")]
579        {
580            // simd-json requires a mutable byte slice with padding
581            let mut bytes = s.as_bytes().to_vec();
582            if let Ok(value) = simd_json::serde::from_slice::<JValue>(&mut bytes) {
583                return Ok(value);
584            }
585            // Fall back to serde_json if simd-json fails (e.g., unsupported CPU)
586        }
587        serde_json::from_str(s)
588    }
589}
590
591// ── Conversion from serde_json::Value ────────────────────────────────────────
592
593impl From<serde_json::Value> for JValue {
594    fn from(v: serde_json::Value) -> Self {
595        match v {
596            serde_json::Value::Null => JValue::Null,
597            serde_json::Value::Bool(b) => JValue::Bool(b),
598            serde_json::Value::Number(n) => JValue::Number(n.as_f64().unwrap_or(0.0)),
599            serde_json::Value::String(s) => JValue::String(s.into()),
600            serde_json::Value::Array(arr) => {
601                JValue::Array(Rc::new(arr.into_iter().map(JValue::from).collect()))
602            }
603            serde_json::Value::Object(map) => {
604                let m: IndexMap<String, JValue> =
605                    map.into_iter().map(|(k, v)| (k, JValue::from(v))).collect();
606                JValue::Object(Rc::new(m))
607            }
608        }
609    }
610}
611
612// ── Conversion to serde_json::Value (for Python boundary) ────────────────────
613
614impl From<&JValue> for serde_json::Value {
615    fn from(v: &JValue) -> Self {
616        match v {
617            JValue::Null | JValue::Undefined => serde_json::Value::Null,
618            JValue::Bool(b) => serde_json::Value::Bool(*b),
619            JValue::Number(n) => {
620                if n.is_nan() || n.is_infinite() {
621                    serde_json::Value::Null
622                } else {
623                    serde_json::json!(*n)
624                }
625            }
626            JValue::String(s) => serde_json::Value::String(s.to_string()),
627            JValue::Array(arr) => {
628                serde_json::Value::Array(arr.iter().map(serde_json::Value::from).collect())
629            }
630            JValue::Object(map) => {
631                let m: serde_json::Map<String, serde_json::Value> = map
632                    .iter()
633                    .map(|(k, v)| (k.clone(), serde_json::Value::from(v)))
634                    .collect();
635                serde_json::Value::Object(m)
636            }
637            JValue::Lambda { .. } | JValue::Builtin { .. } => serde_json::Value::Null,
638            JValue::Regex { pattern, flags } => {
639                let mut m = serde_json::Map::new();
640                m.insert(
641                    "pattern".to_string(),
642                    serde_json::Value::String(pattern.to_string()),
643                );
644                m.insert(
645                    "flags".to_string(),
646                    serde_json::Value::String(flags.to_string()),
647                );
648                serde_json::Value::Object(m)
649            }
650        }
651    }
652}
653
654// ── jvalue! macro ────────────────────────────────────────────────────────────
655
656/// Macro for constructing JValue literals, similar to serde_json::json!
657///
658/// Usage:
659///   jvalue!(null)           → JValue::Null
660///   jvalue!(true)           → JValue::Bool(true)
661///   jvalue!(false)          → JValue::Bool(false)
662///   jvalue!(42)             → JValue::Number(42.0)
663///   jvalue!(3.14)           → JValue::Number(3.14)
664///   jvalue!("hello")        → JValue::String(Rc::from("hello"))
665///   jvalue!([1, 2, 3])      → JValue::Array(Rc::new(vec![...]))
666///   jvalue!({"k": v, ...})  → JValue::Object(Rc::new(IndexMap from pairs))
667///   jvalue!(expr)           → JValue::from(expr)
668#[macro_export]
669macro_rules! jvalue {
670    // null
671    (null) => {
672        $crate::value::JValue::Null
673    };
674
675    // true
676    (true) => {
677        $crate::value::JValue::Bool(true)
678    };
679
680    // false
681    (false) => {
682        $crate::value::JValue::Bool(false)
683    };
684
685    // Array
686    ([ $($elem:tt),* $(,)? ]) => {
687        $crate::value::JValue::Array(std::rc::Rc::new(vec![ $( jvalue!($elem) ),* ]))
688    };
689
690    // Object
691    ({ $($key:tt : $val:tt),* $(,)? }) => {
692        {
693            let mut map = indexmap::IndexMap::new();
694            $(
695                map.insert(($key).to_string(), jvalue!($val));
696            )*
697            $crate::value::JValue::Object(std::rc::Rc::new(map))
698        }
699    };
700
701    // Expression (fallback — numbers, variables, function calls, etc.)
702    ($other:expr) => {
703        $crate::value::JValue::from($other)
704    };
705}
706
707// ── Tests ────────────────────────────────────────────────────────────────────
708
709#[cfg(test)]
710mod tests {
711    use super::*;
712
713    #[test]
714    fn test_clone_is_cheap() {
715        // Array clone should be O(1) — same Rc pointer
716        let arr = JValue::array(vec![
717            JValue::from(1i64),
718            JValue::from(2i64),
719            JValue::from(3i64),
720        ]);
721        let arr2 = arr.clone();
722        if let (JValue::Array(a), JValue::Array(b)) = (&arr, &arr2) {
723            assert!(Rc::ptr_eq(a, b));
724        } else {
725            panic!("expected arrays");
726        }
727
728        // Object clone should be O(1)
729        let mut map = IndexMap::new();
730        map.insert("x".to_string(), JValue::from(1i64));
731        let obj = JValue::object(map);
732        let obj2 = obj.clone();
733        if let (JValue::Object(a), JValue::Object(b)) = (&obj, &obj2) {
734            assert!(Rc::ptr_eq(a, b));
735        } else {
736            panic!("expected objects");
737        }
738
739        // String clone should be O(1)
740        let s = JValue::string("hello");
741        let s2 = s.clone();
742        if let (JValue::String(a), JValue::String(b)) = (&s, &s2) {
743            assert!(Rc::ptr_eq(a, b));
744        } else {
745            panic!("expected strings");
746        }
747    }
748
749    #[test]
750    fn test_type_checks() {
751        assert!(JValue::Null.is_null());
752        assert!(JValue::Undefined.is_undefined());
753        assert!(JValue::Bool(true).is_bool());
754        assert!(JValue::Number(42.0).is_number());
755        assert!(JValue::string("hello").is_string());
756        assert!(JValue::array(vec![]).is_array());
757        assert!(JValue::object(IndexMap::new()).is_object());
758        assert!(JValue::lambda("id", vec![], None::<&str>, None::<&str>).is_lambda());
759        assert!(JValue::lambda("id", vec![], None::<&str>, None::<&str>).is_function());
760        assert!(JValue::builtin("sum").is_builtin());
761        assert!(JValue::builtin("sum").is_function());
762        assert!(JValue::regex(".*", "i").is_regex());
763    }
764
765    #[test]
766    fn test_extraction() {
767        assert_eq!(JValue::Number(42.0).as_f64(), Some(42.0));
768        assert_eq!(JValue::Number(42.0).as_i64(), Some(42));
769        assert_eq!(JValue::Number(42.5).as_i64(), None);
770        assert_eq!(JValue::string("hello").as_str(), Some("hello"));
771        assert_eq!(JValue::Bool(true).as_bool(), Some(true));
772        assert_eq!(
773            JValue::array(vec![JValue::from(1i64)])
774                .as_array()
775                .map(|a| a.len()),
776            Some(1)
777        );
778    }
779
780    #[test]
781    fn test_jvalue_macro() {
782        let n = jvalue!(null);
783        assert!(n.is_null());
784
785        let b = jvalue!(true);
786        assert_eq!(b.as_bool(), Some(true));
787
788        let arr = jvalue!([1i64, 2i64, 3i64]);
789        assert_eq!(arr.as_array().map(|a| a.len()), Some(3));
790
791        let obj = jvalue!({"name": "Alice", "age": 30i64});
792        assert_eq!(obj.get("name").and_then(|v| v.as_str()), Some("Alice"));
793    }
794
795    #[test]
796    fn test_equality() {
797        assert_eq!(JValue::Null, JValue::Null);
798        assert_eq!(JValue::Bool(true), JValue::Bool(true));
799        assert_ne!(JValue::Bool(true), JValue::Bool(false));
800        assert_eq!(JValue::Number(42.0), JValue::Number(42.0));
801        assert_ne!(JValue::Number(f64::NAN), JValue::Number(f64::NAN));
802        assert_eq!(JValue::string("hello"), JValue::string("hello"));
803        assert_ne!(JValue::Null, JValue::Undefined);
804    }
805
806    #[test]
807    fn test_serde_roundtrip() {
808        let v = jvalue!({"name": "Alice", "scores": [1i64, 2i64, 3i64], "active": true});
809        let json_str = v.to_json_string().unwrap();
810        let parsed = JValue::from_json_str(&json_str).unwrap();
811        assert_eq!(v, parsed);
812    }
813
814    #[test]
815    fn test_from_serde_json() {
816        let sv = serde_json::json!({"name": "Alice", "age": 30, "scores": [1, 2, 3]});
817        let jv = JValue::from(sv);
818        assert_eq!(jv.get("name").and_then(|v| v.as_str()), Some("Alice"));
819        assert_eq!(jv.get("age").and_then(|v| v.as_f64()), Some(30.0));
820    }
821
822    #[test]
823    fn test_make_mut() {
824        let mut arr = JValue::array(vec![JValue::from(1i64), JValue::from(2i64)]);
825        let arr2 = arr.clone();
826
827        // Mutate arr — should CoW (clone-on-write)
828        arr.as_array_mut().unwrap().push(JValue::from(3i64));
829
830        // arr2 should be unchanged
831        assert_eq!(arr.as_array().unwrap().len(), 3);
832        assert_eq!(arr2.as_array().unwrap().len(), 2);
833    }
834}