Skip to main content

azul_core/
json.rs

1//! JSON value types for C API (data definitions only, no serde_json dependency)
2//!
3//! The actual parsing/serialization lives in `azul_layout::json` which adds
4//! serde_json-based implementations on top of these types.
5
6use alloc::string::String;
7use alloc::vec::Vec;
8use core::fmt;
9use azul_css::{
10    AzString, OptionString, OptionF64, OptionBool,
11    impl_vec, impl_vec_clone, impl_vec_debug, impl_vec_partialeq, impl_vec_mut,
12    impl_result, impl_result_inner,
13    impl_option, impl_option_inner,
14};
15
16// ============================================================================
17// JSON Value Type
18// ============================================================================
19
20/// A generic JSON value that can hold any JSON type
21#[derive(Debug, Clone, PartialEq)]
22#[repr(C)]
23pub struct Json {
24    /// The type of this JSON value
25    pub value_type: JsonType,
26    /// Internal storage - interpretation depends on `value_type`
27    /// For objects/arrays, this contains serialized data
28    pub internal: JsonInternal,
29}
30
31/// Internal storage for JSON values.
32///
33/// This is a C-FFI-compatible tagged-union-via-struct: all fields always exist,
34/// but only the field(s) corresponding to `JsonType` in the parent `Json` are
35/// meaningful.  For compound types (`Array`, `Object`) the serialized JSON is
36/// stored in `string_value` and re-parsed on each access β€” this trades repeated
37/// parsing cost for a flat, FFI-safe layout with no interior pointers.
38#[derive(Debug, Clone, PartialEq)]
39#[repr(C)]
40pub struct JsonInternal {
41    /// For strings and serialized objects/arrays
42    pub string_value: AzString,
43    /// For numbers
44    pub number_value: f64,
45    /// For booleans
46    pub bool_value: bool,
47}
48
49/// `Json::null()`. A default that is JSON null, rather than an empty string
50/// masquerading as a value.
51impl Default for Json {
52    fn default() -> Self {
53        Self::null()
54    }
55}
56
57impl Default for JsonInternal {
58    fn default() -> Self {
59        Self {
60            string_value: AzString::from(String::new()),
61            number_value: 0.0,
62            bool_value: false,
63        }
64    }
65}
66
67/// Type of a JSON value
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69#[repr(C)]
70pub enum JsonType {
71    /// JSON null
72    Null,
73    /// JSON boolean (true/false)
74    Bool,
75    /// JSON number (stored as f64)
76    Number,
77    /// JSON string
78    String,
79    /// JSON array
80    Array,
81    /// JSON object
82    Object,
83}
84
85/// Error when parsing JSON
86#[derive(Debug, Clone, PartialEq, Eq)]
87#[repr(C)]
88pub struct JsonParseError {
89    /// Error message
90    pub message: AzString,
91    /// Line number (if available)
92    pub line: u32,
93    /// Column number (if available)
94    pub column: u32,
95}
96
97impl fmt::Display for JsonParseError {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        if self.line > 0 {
100            write!(f, "{}:{}: {}", self.line, self.column, self.message.as_str())
101        } else {
102            write!(f, "{}", self.message.as_str())
103        }
104    }
105}
106
107#[cfg(feature = "std")]
108impl std::error::Error for JsonParseError {}
109
110/// A key-value pair in a JSON object
111#[derive(Debug, Clone, PartialEq)]
112#[repr(C)]
113pub struct JsonKeyValue {
114    /// The key
115    pub key: AzString,
116    /// The value
117    pub value: Json,
118}
119
120impl JsonKeyValue {
121    /// Create a new key-value pair
122    #[must_use] pub const fn create(key: AzString, value: Json) -> Self {
123        Self { key, value }
124    }
125}
126
127// ============================================================================
128// FFI-safe collection types
129// ============================================================================
130
131/// Option type for JsonKeyValue
132impl_option!(JsonKeyValue, OptionJsonKeyValue, copy = false, [Debug, Clone, PartialEq]);
133
134/// Vec of JsonKeyValue (FFI-safe)
135impl_vec!(JsonKeyValue, JsonKeyValueVec, JsonKeyValueVecDestructor, JsonKeyValueVecDestructorType, JsonKeyValueVecSlice, OptionJsonKeyValue);
136impl_vec_clone!(JsonKeyValue, JsonKeyValueVec, JsonKeyValueVecDestructor);
137impl_vec_debug!(JsonKeyValue, JsonKeyValueVec);
138
139impl JsonKeyValueVec {
140    /// Creates a new, heap-allocated `JsonKeyValueVec` by copying elements from a C array
141    #[inline]
142    #[allow(clippy::not_unsafe_ptr_arg_deref)] // SAFETY/FFI: `*const T` is the C-ABI signature; the fn null-checks then derefs under the documented caller contract (C guarantees a valid ptr/len). Marking it `unsafe fn` would force unsafe blocks into the generated dll bindings.
143    #[must_use] pub fn copy_from_array(ptr: *const JsonKeyValue, len: usize) -> Self {
144        if ptr.is_null() || len == 0 {
145            return Self::new();
146        }
147        let slice = unsafe { core::slice::from_raw_parts(ptr, len) };
148        Self::from_vec(slice.to_vec())
149    }
150}
151
152// FFI-safe JsonVec using impl_vec! macro
153impl_vec!(Json, JsonVec, JsonVecDestructor, JsonVecDestructorType, JsonVecSlice, OptionJson);
154impl_vec_clone!(Json, JsonVec, JsonVecDestructor);
155impl_vec_debug!(Json, JsonVec);
156impl_vec_partialeq!(Json, JsonVec);
157impl_vec_mut!(Json, JsonVec);
158
159impl JsonVec {
160    /// Creates a new, heap-allocated `JsonVec` by copying elements from a C array
161    #[inline]
162    #[allow(clippy::not_unsafe_ptr_arg_deref)] // SAFETY/FFI: `*const T` is the C-ABI signature; the fn null-checks then derefs under the documented caller contract (C guarantees a valid ptr/len). Marking it `unsafe fn` would force unsafe blocks into the generated dll bindings.
163    #[must_use] pub fn copy_from_array(ptr: *const Json, len: usize) -> Self {
164        if ptr.is_null() || len == 0 {
165            return Self::new();
166        }
167        let slice = unsafe { core::slice::from_raw_parts(ptr, len) };
168        Self::from_vec(slice.to_vec())
169    }
170}
171
172// FFI-safe Result type for JSON parsing
173impl_result!(
174    Json,
175    JsonParseError,
176    ResultJsonJsonParseError,
177    copy = false,
178    [Debug, Clone, PartialEq]
179);
180
181// FFI-safe Option types for JSON
182impl_option!(Json, OptionJson, copy = false, [Clone, Debug, PartialEq]);
183impl_option!(JsonVec, OptionJsonVec, copy = false, [Clone, Debug]);
184impl_option!(JsonKeyValueVec, OptionJsonKeyValueVec, copy = false, [Clone, Debug]);
185
186// FFI-safe Option types for JSON value extraction
187// Note: OptionBool and OptionF64 are already exported from azul_css
188impl_option!(i64, OptionI64, [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]);
189
190// ============================================================================
191// Helpers
192// ============================================================================
193
194/// Try to losslessly convert an `f64` to `i64`.
195///
196/// Returns `Some` only when `n` is an integer that fits in `i64` without
197/// overflow.  The upper bound uses `< 2^63` (not `<= i64::MAX as f64`)
198/// because `i64::MAX` cannot be represented exactly in `f64` β€” the cast
199/// rounds up to `2^63`, which would cause overflow on `n as i64`.
200#[allow(clippy::cast_possible_truncation)] // bounded DPI/dimension/number conversion
201fn f64_as_i64(n: f64) -> Option<i64> {
202    if n.fract() == 0.0 && n >= -(2_f64.powi(63)) && n < 2_f64.powi(63) {
203        Some(n as i64)
204    } else {
205        None
206    }
207}
208
209// ============================================================================
210// Non-serde methods on Json (pure data, no parsing)
211// ============================================================================
212
213impl Json {
214    /// Create a null JSON value
215    #[must_use] pub fn null() -> Self {
216        Self {
217            value_type: JsonType::Null,
218            internal: JsonInternal::default(),
219        }
220    }
221
222    /// Create a boolean JSON value
223    #[must_use] pub fn bool(value: bool) -> Self {
224        Self {
225            value_type: JsonType::Bool,
226            internal: JsonInternal {
227                string_value: AzString::from(String::new()),
228                number_value: 0.0,
229                bool_value: value,
230            },
231        }
232    }
233
234    /// Create a number JSON value (floating-point)
235    #[must_use] pub fn number(value: f64) -> Self {
236        Self {
237            value_type: JsonType::Number,
238            internal: JsonInternal {
239                string_value: AzString::from(String::new()),
240                number_value: value,
241                bool_value: false,
242            },
243        }
244    }
245
246    /// Create an integer JSON value.
247    ///
248    /// **Note:** the value is stored as `f64` internally, so `i64` values with
249    /// magnitude greater than 2^53 will lose precision silently.
250    #[allow(clippy::cast_precision_loss)] // bounded DPI/dimension/number conversion
251    #[must_use] pub fn integer(value: i64) -> Self {
252        Self {
253            value_type: JsonType::Number,
254            internal: JsonInternal {
255                string_value: AzString::from(String::new()),
256                number_value: value as f64,
257                bool_value: false,
258            },
259        }
260    }
261
262    /// Create a string JSON value
263    pub fn string(value: impl Into<String>) -> Self {
264        Self {
265            value_type: JsonType::String,
266            internal: JsonInternal {
267                string_value: AzString::from(value.into()),
268                number_value: 0.0,
269                bool_value: false,
270            },
271        }
272    }
273
274    /// Check if this is null
275    #[must_use] pub fn is_null(&self) -> bool {
276        self.value_type == JsonType::Null
277    }
278
279    /// Check if this is a boolean
280    #[must_use] pub fn is_bool(&self) -> bool {
281        self.value_type == JsonType::Bool
282    }
283
284    /// Check if this is a number
285    #[must_use] pub fn is_number(&self) -> bool {
286        self.value_type == JsonType::Number
287    }
288
289    /// Check if this is a string
290    #[must_use] pub fn is_string(&self) -> bool {
291        self.value_type == JsonType::String
292    }
293
294    /// Check if this is an array
295    #[must_use] pub fn is_array(&self) -> bool {
296        self.value_type == JsonType::Array
297    }
298
299    /// Check if this is an object
300    #[must_use] pub fn is_object(&self) -> bool {
301        self.value_type == JsonType::Object
302    }
303
304    /// Get as boolean (returns None if not a bool)
305    #[must_use] pub fn as_bool(&self) -> OptionBool {
306        if self.value_type == JsonType::Bool {
307            OptionBool::Some(self.internal.bool_value)
308        } else {
309            OptionBool::None
310        }
311    }
312
313    /// Get as number (returns None if not a number)
314    #[must_use] pub fn as_number(&self) -> OptionF64 {
315        if self.value_type == JsonType::Number {
316            OptionF64::Some(self.internal.number_value)
317        } else {
318            OptionF64::None
319        }
320    }
321
322    /// Get as integer (returns None if not a number or not an integer)
323    #[must_use] pub fn as_i64(&self) -> OptionI64 {
324        if self.value_type == JsonType::Number {
325            f64_as_i64(self.internal.number_value).map_or(OptionI64::None, OptionI64::Some)
326        } else {
327            OptionI64::None
328        }
329    }
330
331    /// Get as string (returns None if not a string)
332    #[must_use] pub fn as_string(&self) -> OptionString {
333        if self.value_type == JsonType::String {
334            OptionString::Some(self.internal.string_value.clone())
335        } else {
336            OptionString::None
337        }
338    }
339
340    /// Get the raw internal string value (for arrays/objects this is the serialized JSON)
341    #[must_use] pub fn raw_string(&self) -> &str {
342        self.internal.string_value.as_str()
343    }
344}
345
346/// Note: the `Display` output is meant for human-readable / debug display.
347/// String values are quoted but **not** JSON-escaped (no backslash escaping
348/// of embedded quotes, newlines, etc.).  Use `to_json_string()` (requires
349/// the `serde-json` feature) when valid JSON output is needed.
350impl fmt::Display for Json {
351    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352        match self.value_type {
353            JsonType::Null => write!(f, "null"),
354            JsonType::Bool => write!(f, "{}", self.internal.bool_value),
355            JsonType::Number => {
356                let num = self.internal.number_value;
357                if let Some(i) = f64_as_i64(num) {
358                    write!(f, "{i}")
359                } else {
360                    write!(f, "{num}")
361                }
362            }
363            JsonType::String => write!(f, "\"{}\"", self.internal.string_value.as_str()),
364            JsonType::Array | JsonType::Object => {
365                write!(f, "{}", self.internal.string_value.as_str())
366            }
367        }
368    }
369}
370
371// ============================================================================
372// serde_json-dependent methods (gated behind "serde-json" feature)
373// ============================================================================
374
375#[cfg(feature = "serde-json")]
376impl serde::Serialize for Json {
377    /// Serialize as the JSON value this represents, not as its repr(C) fields.
378    ///
379    /// Without this, a struct holding a `Json` field cannot derive
380    /// `Serialize` at all β€” and the obvious workaround, storing the payload
381    /// as a `String` of JSON, produces escaped JSON-inside-JSON that every
382    /// consumer has to parse twice and nothing validates.
383    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
384        self.to_serde_value().serialize(s)
385    }
386}
387
388#[cfg(feature = "serde-json")]
389impl<'de> serde::Deserialize<'de> for Json {
390    fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
391        Ok(Self::from_serde_value(serde_json::Value::deserialize(d)?))
392    }
393}
394
395#[cfg(feature = "serde-json")]
396impl Json {
397    /// Parse JSON from a string.
398    ///
399    /// # Errors
400    ///
401    /// Returns [`JsonParseError`] (message plus line/column) if `s` is not
402    /// well-formed JSON.
403    pub fn parse(s: &str) -> Result<Self, JsonParseError> {
404        let value: serde_json::Value = serde_json::from_str(s).map_err(|e| {
405            JsonParseError {
406                message: AzString::from(alloc::format!("{e}")),
407                line: u32::try_from(e.line()).unwrap_or(u32::MAX),
408                column: u32::try_from(e.column()).unwrap_or(u32::MAX),
409            }
410        })?;
411        Ok(Self::from_serde_value(value))
412    }
413
414    /// Parse JSON from bytes (UTF-8).
415    ///
416    /// # Errors
417    ///
418    /// Returns [`JsonParseError`] (message plus line/column) if `bytes` is not
419    /// well-formed UTF-8 JSON.
420    pub fn parse_bytes(bytes: &[u8]) -> Result<Self, JsonParseError> {
421        let value: serde_json::Value = serde_json::from_slice(bytes).map_err(|e| {
422            JsonParseError {
423                message: AzString::from(alloc::format!("{e}")),
424                line: u32::try_from(e.line()).unwrap_or(u32::MAX),
425                column: u32::try_from(e.column()).unwrap_or(u32::MAX),
426            }
427        })?;
428        Ok(Self::from_serde_value(value))
429    }
430
431    /// Convert from `serde_json::Value`
432    #[must_use] pub fn from_serde_value(value: serde_json::Value) -> Self {
433        match value {
434            serde_json::Value::Null => Self::null(),
435            serde_json::Value::Bool(b) => Self::bool(b),
436            serde_json::Value::Number(n) => Self::number(n.as_f64().unwrap_or(0.0)),
437            serde_json::Value::String(s) => Self::string(s),
438            serde_json::Value::Array(arr) => {
439                let json_str = serde_json::to_string(&serde_json::Value::Array(arr)).unwrap_or_default();
440                Self {
441                    value_type: JsonType::Array,
442                    internal: JsonInternal {
443                        string_value: AzString::from(json_str),
444                        number_value: 0.0,
445                        bool_value: false,
446                    },
447                }
448            }
449            serde_json::Value::Object(obj) => {
450                let json_str = serde_json::to_string(&serde_json::Value::Object(obj)).unwrap_or_default();
451                Self {
452                    value_type: JsonType::Object,
453                    internal: JsonInternal {
454                        string_value: AzString::from(json_str),
455                        number_value: 0.0,
456                        bool_value: false,
457                    },
458                }
459            }
460        }
461    }
462
463    /// Convert this Json to a `serde_json::Value`
464    #[must_use]
465    pub fn to_serde_value(&self) -> serde_json::Value {
466        match self.value_type {
467            JsonType::Null => serde_json::Value::Null,
468            JsonType::Bool => serde_json::Value::Bool(self.internal.bool_value),
469            JsonType::Number => {
470                let num = self.internal.number_value;
471                f64_as_i64(num).map_or_else(
472                    || {
473                        serde_json::Number::from_f64(num)
474                            .map_or(serde_json::Value::Null, serde_json::Value::Number)
475                    },
476                    |i| serde_json::Value::Number(serde_json::Number::from(i)),
477                )
478            }
479            JsonType::String => serde_json::Value::String(self.internal.string_value.as_str().to_string()),
480            JsonType::Array | JsonType::Object => {
481                serde_json::from_str(self.internal.string_value.as_str())
482                    .unwrap_or(serde_json::Value::Null)
483            }
484        }
485    }
486
487    /// Create a JSON array from a vector of JSON values
488    // By-value is the C-ABI shape: the generated bindings hand ownership in.
489    #[allow(clippy::needless_pass_by_value)]
490    #[must_use] pub fn array(values: JsonVec) -> Self {
491        let serde_array: Vec<serde_json::Value> = values
492            .as_slice()
493            .iter()
494            .map(Self::to_serde_value)
495            .collect();
496        let json_str = serde_json::to_string(&serde_json::Value::Array(serde_array))
497            .unwrap_or_else(|_| "[]".to_string());
498        Self {
499            value_type: JsonType::Array,
500            internal: JsonInternal {
501                string_value: AzString::from(json_str),
502                number_value: 0.0,
503                bool_value: false,
504            },
505        }
506    }
507
508    /// Create a JSON object from key-value pairs
509    // By-value is the C-ABI shape: the generated bindings hand ownership in.
510    #[allow(clippy::needless_pass_by_value)]
511    #[must_use] pub fn object(entries: JsonKeyValueVec) -> Self {
512        let mut map = serde_json::Map::new();
513        for kv in entries.as_slice() {
514            map.insert(kv.key.as_str().to_string(), kv.value.to_serde_value());
515        }
516        let json_str = serde_json::to_string(&serde_json::Value::Object(map))
517            .unwrap_or_else(|_| "{}".to_string());
518        Self {
519            value_type: JsonType::Object,
520            internal: JsonInternal {
521                string_value: AzString::from(json_str),
522                number_value: 0.0,
523                bool_value: false,
524            },
525        }
526    }
527
528    /// Get the number of elements (for arrays) or keys (for objects)
529    #[must_use] pub fn len(&self) -> usize {
530        match self.value_type {
531            JsonType::Array => {
532                if let Ok(serde_json::Value::Array(arr)) = serde_json::from_str(self.internal.string_value.as_str()) {
533                    arr.len()
534                } else {
535                    0
536                }
537            }
538            JsonType::Object => {
539                if let Ok(serde_json::Value::Object(obj)) = serde_json::from_str(self.internal.string_value.as_str()) {
540                    obj.len()
541                } else {
542                    0
543                }
544            }
545            _ => 0,
546        }
547    }
548
549    /// Check if empty (for arrays/objects)
550    #[must_use] pub fn is_empty(&self) -> bool {
551        self.len() == 0
552    }
553
554    /// Get array element by index
555    #[must_use] pub fn get_index(&self, index: usize) -> Option<Self> {
556        if self.value_type != JsonType::Array { return None; }
557        let value: serde_json::Value = serde_json::from_str(self.internal.string_value.as_str()).ok()?;
558        if let serde_json::Value::Array(arr) = value {
559            arr.get(index).map(|v| Self::from_serde_value(v.clone()))
560        } else {
561            None
562        }
563    }
564
565    /// Get object value by key
566    #[must_use] pub fn get_key(&self, key: &str) -> Option<Self> {
567        if self.value_type != JsonType::Object { return None; }
568        let value: serde_json::Value = serde_json::from_str(self.internal.string_value.as_str()).ok()?;
569        if let serde_json::Value::Object(obj) = value {
570            obj.get(key).map(|v| Self::from_serde_value(v.clone()))
571        } else {
572            None
573        }
574    }
575
576    /// Get all keys of an object
577    #[must_use] pub fn keys(&self) -> Vec<AzString> {
578        if self.value_type != JsonType::Object { return Vec::new(); }
579        let value: serde_json::Value = match serde_json::from_str(self.internal.string_value.as_str()) {
580            Ok(v) => v,
581            Err(_) => return Vec::new(),
582        };
583        if let serde_json::Value::Object(obj) = value {
584            obj.keys().map(|k| AzString::from(k.clone())).collect()
585        } else {
586            Vec::new()
587        }
588    }
589
590    /// Convert array to Vec<Json>
591    pub fn to_array(&self) -> Option<JsonVec> {
592        if self.value_type != JsonType::Array { return None; }
593        let value: serde_json::Value = serde_json::from_str(self.internal.string_value.as_str()).ok()?;
594        if let serde_json::Value::Array(arr) = value {
595            Some(arr.into_iter().map(Self::from_serde_value).collect())
596        } else {
597            None
598        }
599    }
600
601    /// Convert object to Vec<JsonKeyValue>
602    #[must_use] pub fn to_object(&self) -> Option<JsonKeyValueVec> {
603        if self.value_type != JsonType::Object { return None; }
604        let value: serde_json::Value = serde_json::from_str(self.internal.string_value.as_str()).ok()?;
605        if let serde_json::Value::Object(obj) = value {
606            Some(obj.into_iter().map(|(k, v)| JsonKeyValue {
607                key: AzString::from(k),
608                value: Self::from_serde_value(v),
609            }).collect())
610        } else {
611            None
612        }
613    }
614
615    /// Serialize to JSON string (returns `AzString`)
616    #[must_use] pub fn to_json_string(&self) -> AzString {
617        match self.value_type {
618            JsonType::Null => AzString::from(alloc::string::String::from("null")),
619            JsonType::Bool => AzString::from(if self.internal.bool_value { alloc::string::String::from("true") } else { alloc::string::String::from("false") }),
620            JsonType::Number => {
621                let num = self.internal.number_value;
622                f64_as_i64(num).map_or_else(
623                    || AzString::from(alloc::format!("{num}")),
624                    |i| AzString::from(alloc::format!("{i}")),
625                )
626            }
627            JsonType::String => {
628                let escaped = serde_json::to_string(self.internal.string_value.as_str()).unwrap_or_default();
629                AzString::from(escaped)
630            }
631            JsonType::Array | JsonType::Object => {
632                self.internal.string_value.clone()
633            }
634        }
635    }
636
637    /// Serialize to pretty-printed JSON string
638    #[must_use] pub fn to_string_pretty(&self) -> AzString {
639        match self.value_type {
640            JsonType::Null | JsonType::Bool | JsonType::Number | JsonType::String => {
641                self.to_json_string()
642            }
643            JsonType::Array | JsonType::Object => {
644                serde_json::from_str::<serde_json::Value>(self.internal.string_value.as_str())
645                    .map_or_else(
646                        |_| self.internal.string_value.clone(),
647                        |value| {
648                            AzString::from(
649                                serde_json::to_string_pretty(&value).unwrap_or_default(),
650                            )
651                        },
652                    )
653            }
654        }
655    }
656
657    /// Access a nested value using a JSON Pointer (RFC 6901).
658    #[must_use] pub fn jq(&self, path: &str) -> Self {
659        match self.value_type {
660            JsonType::Null | JsonType::Bool | JsonType::Number | JsonType::String => {
661                if path.is_empty() { self.clone() } else { Self::null() }
662            }
663            JsonType::Array | JsonType::Object => {
664                let value: serde_json::Value = match serde_json::from_str(self.internal.string_value.as_str()) {
665                    Ok(v) => v,
666                    Err(_) => return Self::null(),
667                };
668                value
669                    .pointer(path)
670                    .map_or_else(Self::null, |v| Self::from_serde_value(v.clone()))
671            }
672        }
673    }
674
675    /// Access nested values using a JSON Pointer with wildcard support.
676    #[must_use] pub fn jq_all(&self, path: &str) -> JsonVec {
677        let result = match self.value_type {
678            JsonType::Null | JsonType::Bool | JsonType::Number | JsonType::String => {
679                if path.is_empty() { vec![self.clone()] } else { vec![] }
680            }
681            JsonType::Array | JsonType::Object => {
682                let value: serde_json::Value = match serde_json::from_str(self.internal.string_value.as_str()) {
683                    Ok(v) => v,
684                    Err(_) => return JsonVec::from_vec(vec![]),
685                };
686                Self::jq_all_recursive(&value, path)
687            }
688        };
689        JsonVec::from_vec(result)
690    }
691
692    /// Maximum JSON-Pointer component depth for [`jq_all`](Self::jq_all).
693    ///
694    /// AUDIT 2026-07-08: `jq_all_recursive` recursed once per pointer component,
695    /// so an attacker-supplied pointer with tens of thousands of `/` segments
696    /// (e.g. `"/a".repeat(100_000)`) overflowed the stack. The single-child
697    /// descent is now iterative (unbounded, allocation-free); only the wildcard
698    /// (`*`) fan-out still recurses, and that recursion is capped here. 512 is far
699    /// deeper than any real document nesting while staying well inside the stack.
700    const JQ_MAX_WILDCARD_DEPTH: usize = 512;
701
702    /// Recursive helper for `jq_all` that handles wildcards.
703    ///
704    /// Non-wildcard components are walked in a loop so a long linear pointer can
705    /// never overflow the stack; only `*` fan-out recurses, bounded by
706    /// [`JQ_MAX_WILDCARD_DEPTH`](Self::JQ_MAX_WILDCARD_DEPTH).
707    fn jq_all_recursive(value: &serde_json::Value, path: &str) -> Vec<Self> {
708        Self::jq_all_recursive_depth(value, path, 0)
709    }
710
711    fn jq_all_recursive_depth(
712        value: &serde_json::Value,
713        path: &str,
714        depth: usize,
715    ) -> Vec<Self> {
716        // Guard the wildcard recursion; exceeding the cap yields no match rather
717        // than crashing.
718        if depth > Self::JQ_MAX_WILDCARD_DEPTH {
719            return vec![];
720        }
721
722        // Walk non-wildcard components iteratively.
723        let mut value = value;
724        let mut path = path;
725        loop {
726            if path.is_empty() {
727                return vec![Self::from_serde_value(value.clone())];
728            }
729            if !path.starts_with('/') {
730                return vec![];
731            }
732            let rest = &path[1..];
733            let (component, remaining) =
734                rest.find('/').map_or((rest, ""), |idx| (&rest[..idx], &rest[idx..]));
735
736            if component == "*" {
737                let mut results = Vec::new();
738                match value {
739                    serde_json::Value::Array(arr) => {
740                        for item in arr {
741                            results.extend(Self::jq_all_recursive_depth(
742                                item,
743                                remaining,
744                                depth + 1,
745                            ));
746                        }
747                    }
748                    serde_json::Value::Object(obj) => {
749                        for (_key, val) in obj {
750                            results.extend(Self::jq_all_recursive_depth(
751                                val,
752                                remaining,
753                                depth + 1,
754                            ));
755                        }
756                    }
757                    _ => {}
758                }
759                return results;
760            }
761
762            // Single-child descent: advance the cursor instead of recursing.
763            let next = match value {
764                serde_json::Value::Array(arr) => {
765                    component.parse::<usize>().ok().and_then(|idx| arr.get(idx))
766                }
767                serde_json::Value::Object(obj) => obj.get(component),
768                _ => None,
769            };
770            match next {
771                Some(v) => {
772                    value = v;
773                    path = remaining;
774                }
775                None => return vec![],
776            }
777        }
778    }
779}
780
781#[cfg(test)]
782mod jq_recursion_tests {
783    use super::*;
784
785    /// AUDIT 2026-07-08: a pointer with a very large number of components used to
786    /// overflow the stack via per-component recursion. Linear (non-wildcard)
787    /// descent is now iterative, so an over-long pointer against a shallow
788    /// document returns empty promptly with zero recursion instead of a deep call
789    /// chain. `serde_json`'s own 128-level parse cap keeps documents shallow, but
790    /// this guarantees the jq walk itself never blows the stack on a huge pointer.
791    #[test]
792    #[cfg(feature = "serde-json")]
793    fn huge_pointer_on_shallow_doc_returns_empty() {
794        let json = Json::parse("{\"a\":{\"b\":1}}").expect("parse");
795        let pointer = "/a".repeat(200_000);
796        assert_eq!(json.jq_all(&pointer).as_ref().len(), 0);
797    }
798
799    /// A moderately deep linear pointer (within serde's parse limit) resolves to
800    /// its single leaf via the iterative descent.
801    #[test]
802    #[cfg(feature = "serde-json")]
803    fn deep_linear_pointer_resolves_leaf() {
804        const DEPTH: usize = 100; // below serde_json's 128-level parse cap
805
806        let mut doc = String::new();
807        for _ in 0..DEPTH {
808            doc.push_str("{\"a\":");
809        }
810        doc.push_str("42");
811        for _ in 0..DEPTH {
812            doc.push('}');
813        }
814
815        let json = Json::parse(&doc).expect("deep doc should parse");
816        let pointer = "/a".repeat(DEPTH);
817        let out = json.jq_all(&pointer);
818        assert_eq!(out.as_ref().len(), 1, "the single leaf should be found");
819    }
820
821    /// Ordinary wildcard + index access still works after the iterative rewrite.
822    #[test]
823    #[cfg(feature = "serde-json")]
824    fn wildcard_and_index_still_work() {
825        let json = Json::parse("{\"items\":[{\"v\":1},{\"v\":2},{\"v\":3}]}").expect("parse");
826        let all = json.jq_all("/items/*/v");
827        assert_eq!(all.as_ref().len(), 3);
828        let one = json.jq_all("/items/1/v");
829        assert_eq!(one.as_ref().len(), 1);
830    }
831}
832
833#[cfg(test)]
834#[allow(clippy::float_cmp, clippy::unreadable_literal)]
835mod autotest_generated {
836    use super::*;
837
838    // ------------------------------------------------------------------
839    // helpers
840    // ------------------------------------------------------------------
841
842    fn az(s: &str) -> AzString {
843        AzString::from(String::from(s))
844    }
845
846    /// Build a `Json` by hand, bypassing the constructors. Used to feed the
847    /// accessors a *corrupt* value (e.g. `value_type: Array` whose internal
848    /// string is not parseable JSON) β€” reachable over FFI because every field
849    /// of `Json` / `JsonInternal` is `pub`.
850    fn raw(value_type: JsonType, string_value: &str) -> Json {
851        Json {
852            value_type,
853            internal: JsonInternal {
854                string_value: az(string_value),
855                number_value: 0.0,
856                bool_value: false,
857            },
858        }
859    }
860
861    fn two_pow_63() -> f64 {
862        2_f64.powi(63)
863    }
864
865    // ==================================================================
866    // f64_as_i64  (numeric: zero / min_max / negative / overflow / nan_inf)
867    // ==================================================================
868
869    #[test]
870    fn f64_as_i64_zero_and_negative_zero() {
871        assert_eq!(f64_as_i64(0.0), Some(0));
872        // -0.0 is an integer and in range: the sign is silently dropped.
873        assert_eq!(f64_as_i64(-0.0), Some(0));
874    }
875
876    #[test]
877    fn f64_as_i64_min_max_boundaries() {
878        // -2^63 is exactly representable and is exactly i64::MIN.
879        assert_eq!(f64_as_i64(-two_pow_63()), Some(i64::MIN));
880        // +2^63 is NOT a valid i64 β€” must be rejected rather than wrapping.
881        assert_eq!(f64_as_i64(two_pow_63()), None);
882        // i64::MAX rounds *up* to 2^63 when cast to f64, so it is rejected too.
883        // This is the documented reason the bound is `< 2^63` and not `<= MAX`.
884        #[allow(clippy::cast_precision_loss)]
885        let max_as_f64 = i64::MAX as f64;
886        assert_eq!(max_as_f64, two_pow_63());
887        assert_eq!(f64_as_i64(max_as_f64), None);
888        // The largest f64 that is a valid i64: 2^63 - 1024.
889        let just_below = two_pow_63() - 1024.0;
890        assert_eq!(f64_as_i64(just_below), Some(9_223_372_036_854_774_784));
891    }
892
893    #[test]
894    fn f64_as_i64_negatives_are_deterministic() {
895        assert_eq!(f64_as_i64(-1.0), Some(-1));
896        assert_eq!(f64_as_i64(-42.0), Some(-42));
897        assert_eq!(f64_as_i64(-0.5), None);
898        assert_eq!(f64_as_i64(-1.0 - f64::EPSILON), None);
899        // One ULP below -2^63 is out of range.
900        assert_eq!(f64_as_i64(-two_pow_63() * (1.0 + f64::EPSILON)), None);
901    }
902
903    #[test]
904    fn f64_as_i64_overflow_inputs_return_none_not_a_wrapped_cast() {
905        for n in [
906            1e19_f64,
907            1e300_f64,
908            -1e300_f64,
909            f64::MAX,
910            f64::MIN,
911            two_pow_63() * 2.0,
912        ] {
913            assert_eq!(f64_as_i64(n), None, "{n} must not be cast to i64");
914        }
915    }
916
917    #[test]
918    fn f64_as_i64_nan_and_infinity_do_not_panic() {
919        // NaN.fract() is NaN, and NaN == 0.0 is false, so all three fall through
920        // to `None` without ever reaching the (UB-adjacent) `as i64` cast.
921        assert_eq!(f64_as_i64(f64::NAN), None);
922        assert_eq!(f64_as_i64(f64::INFINITY), None);
923        assert_eq!(f64_as_i64(f64::NEG_INFINITY), None);
924    }
925
926    #[test]
927    fn f64_as_i64_fractional_and_subnormal_return_none() {
928        assert_eq!(f64_as_i64(0.5), None);
929        assert_eq!(f64_as_i64(f64::EPSILON), None);
930        assert_eq!(f64_as_i64(f64::MIN_POSITIVE), None);
931        // Smallest subnormal.
932        assert_eq!(f64_as_i64(f64::from_bits(1)), None);
933    }
934
935    // ==================================================================
936    // Json::number / Json::integer  (numeric)
937    // ==================================================================
938
939    #[test]
940    fn number_stores_nan_and_infinity_without_panicking() {
941        for n in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
942            let j = Json::number(n);
943            assert!(j.is_number());
944            assert_eq!(j.as_i64(), OptionI64::None);
945            match j.as_number() {
946                OptionF64::Some(v) => assert_eq!(v.is_nan(), n.is_nan()),
947                OptionF64::None => panic!("as_number() must be Some for a Number"),
948            }
949            // Display must not panic on non-finite floats.
950            let s = alloc::format!("{j}");
951            assert!(!s.is_empty());
952        }
953    }
954
955    #[test]
956    fn number_min_max_and_zero() {
957        assert_eq!(Json::number(0.0).as_i64(), OptionI64::Some(0));
958        assert_eq!(Json::number(-0.0).as_i64(), OptionI64::Some(0));
959        assert_eq!(Json::number(f64::MAX).as_number(), OptionF64::Some(f64::MAX));
960        assert_eq!(Json::number(f64::MIN).as_number(), OptionF64::Some(f64::MIN));
961        // Huge-but-finite values are numbers, but not integers.
962        assert_eq!(Json::number(f64::MAX).as_i64(), OptionI64::None);
963    }
964
965    #[test]
966    fn integer_min_round_trips_but_max_does_not() {
967        // i64::MIN == -2^63 is exactly representable in f64.
968        assert_eq!(Json::integer(i64::MIN).as_i64(), OptionI64::Some(i64::MIN));
969        // i64::MAX is NOT: `value as f64` rounds it up to 2^63, which is out of
970        // i64 range, so the value cannot be read back. Documented on the fn as
971        // "silent precision loss" for |value| > 2^53.
972        assert_eq!(Json::integer(i64::MAX).as_i64(), OptionI64::None);
973        assert_eq!(
974            Json::integer(i64::MAX).as_number(),
975            OptionF64::Some(two_pow_63())
976        );
977    }
978
979    #[test]
980    fn integer_silently_loses_precision_above_2_pow_53() {
981        let boundary = 1_i64 << 53; // 9_007_199_254_740_992
982        assert_eq!(Json::integer(boundary).as_i64(), OptionI64::Some(boundary));
983        // 2^53 + 1 is not representable: it rounds *down* to 2^53.
984        assert_eq!(
985            Json::integer(boundary + 1).as_i64(),
986            OptionI64::Some(boundary)
987        );
988        assert_eq!(Json::integer(0).as_i64(), OptionI64::Some(0));
989        assert_eq!(Json::integer(-1).as_i64(), OptionI64::Some(-1));
990    }
991
992    // ==================================================================
993    // constructors + predicates  (predicate: basic_true_false / edge_inputs)
994    // ==================================================================
995
996    #[test]
997    fn predicates_are_mutually_exclusive_for_every_type() {
998        let cases = [
999            (Json::null(), JsonType::Null),
1000            (Json::bool(false), JsonType::Bool),
1001            (Json::number(f64::NAN), JsonType::Number),
1002            (Json::integer(0), JsonType::Number),
1003            (Json::string(""), JsonType::String),
1004            (raw(JsonType::Array, "[]"), JsonType::Array),
1005            (raw(JsonType::Object, "{}"), JsonType::Object),
1006        ];
1007        for (j, ty) in &cases {
1008            let flags = [
1009                j.is_null(),
1010                j.is_bool(),
1011                j.is_number(),
1012                j.is_string(),
1013                j.is_array(),
1014                j.is_object(),
1015            ];
1016            assert_eq!(
1017                flags.iter().filter(|b| **b).count(),
1018                1,
1019                "exactly one predicate must hold for {ty:?}"
1020            );
1021            assert_eq!(j.value_type, *ty);
1022        }
1023    }
1024
1025    #[test]
1026    fn predicates_on_a_default_internal_do_not_panic() {
1027        // A hand-rolled Json with a default (empty) payload β€” the FFI worst case.
1028        for ty in [
1029            JsonType::Null,
1030            JsonType::Bool,
1031            JsonType::Number,
1032            JsonType::String,
1033            JsonType::Array,
1034            JsonType::Object,
1035        ] {
1036            let j = Json {
1037                value_type: ty,
1038                internal: JsonInternal::default(),
1039            };
1040            assert_eq!(j.is_null(), ty == JsonType::Null);
1041            assert_eq!(j.is_object(), ty == JsonType::Object);
1042            assert_eq!(j.raw_string(), "");
1043        }
1044    }
1045
1046    #[test]
1047    fn bool_constructor_keeps_both_values() {
1048        assert_eq!(Json::bool(true).as_bool(), OptionBool::Some(true));
1049        assert_eq!(Json::bool(false).as_bool(), OptionBool::Some(false));
1050        // The unused payload fields are zeroed, which the derived PartialEq relies on.
1051        assert_eq!(Json::bool(true).raw_string(), "");
1052        assert_eq!(Json::bool(true).as_number(), OptionF64::None);
1053    }
1054
1055    #[test]
1056    fn string_constructor_handles_empty_unicode_and_huge_inputs() {
1057        assert_eq!(Json::string("").as_string(), OptionString::Some(az("")));
1058
1059        let unicode = "πŸ˜€ nÑïve \u{0301}\u{202e}\0 δΈ­ζ–‡";
1060        assert_eq!(
1061            Json::string(unicode).as_string(),
1062            OptionString::Some(az(unicode))
1063        );
1064        assert_eq!(Json::string(unicode).raw_string(), unicode);
1065
1066        let huge = "a".repeat(1_000_000);
1067        let j = Json::string(huge.clone());
1068        assert_eq!(j.raw_string().len(), 1_000_000);
1069        assert_eq!(j.as_string(), OptionString::Some(AzString::from(huge)));
1070    }
1071
1072    // ==================================================================
1073    // getters  (getter: basic_access / edge_access)
1074    // ==================================================================
1075
1076    #[test]
1077    fn getters_return_none_on_type_mismatch() {
1078        let null = Json::null();
1079        assert_eq!(null.as_bool(), OptionBool::None);
1080        assert_eq!(null.as_number(), OptionF64::None);
1081        assert_eq!(null.as_i64(), OptionI64::None);
1082        assert_eq!(null.as_string(), OptionString::None);
1083
1084        // A Bool whose *number* payload happens to be set must still not be
1085        // readable as a number (the tag, not the payload, decides).
1086        let mut liar = Json::bool(true);
1087        liar.internal.number_value = 7.0;
1088        liar.internal.string_value = az("7");
1089        assert_eq!(liar.as_number(), OptionF64::None);
1090        assert_eq!(liar.as_i64(), OptionI64::None);
1091        assert_eq!(liar.as_string(), OptionString::None);
1092        assert_eq!(liar.as_bool(), OptionBool::Some(true));
1093        // ...but raw_string() is the *unchecked* accessor and does hand it back.
1094        assert_eq!(liar.raw_string(), "7");
1095    }
1096
1097    #[test]
1098    fn raw_string_is_empty_for_scalars_and_serialized_for_containers() {
1099        assert_eq!(Json::null().raw_string(), "");
1100        assert_eq!(Json::bool(true).raw_string(), "");
1101        assert_eq!(Json::number(1.5).raw_string(), "");
1102        assert_eq!(Json::string("hi").raw_string(), "hi");
1103        assert_eq!(raw(JsonType::Array, "[1,2]").raw_string(), "[1,2]");
1104        // Corrupt payloads are handed back verbatim, never panic.
1105        assert_eq!(raw(JsonType::Object, "{not json").raw_string(), "{not json");
1106    }
1107
1108    // ==================================================================
1109    // Display for Json  (round_trip / serializer)
1110    // ==================================================================
1111
1112    #[test]
1113    fn display_scalar_values() {
1114        assert_eq!(alloc::format!("{}", Json::null()), "null");
1115        assert_eq!(alloc::format!("{}", Json::bool(true)), "true");
1116        assert_eq!(alloc::format!("{}", Json::bool(false)), "false");
1117        // Integral floats print without a fractional part (via f64_as_i64).
1118        assert_eq!(alloc::format!("{}", Json::number(3.0)), "3");
1119        assert_eq!(alloc::format!("{}", Json::integer(-42)), "-42");
1120        assert_eq!(alloc::format!("{}", Json::number(1.5)), "1.5");
1121        assert_eq!(alloc::format!("{}", Json::string("x")), "\"x\"");
1122    }
1123
1124    #[test]
1125    fn display_of_non_finite_numbers_is_not_json() {
1126        // Characterization: Display is documented as human-readable, NOT JSON.
1127        assert_eq!(alloc::format!("{}", Json::number(f64::NAN)), "NaN");
1128        assert_eq!(alloc::format!("{}", Json::number(f64::INFINITY)), "inf");
1129        assert_eq!(alloc::format!("{}", Json::number(f64::NEG_INFINITY)), "-inf");
1130        // -0.0 loses its sign because f64_as_i64(-0.0) == Some(0).
1131        assert_eq!(alloc::format!("{}", Json::number(-0.0)), "0");
1132    }
1133
1134    #[test]
1135    fn display_does_not_escape_strings() {
1136        // Documented caveat on the Display impl: embedded quotes/newlines are
1137        // NOT escaped, so the output is deliberately not valid JSON.
1138        let j = Json::string("a\"b\nc");
1139        assert_eq!(alloc::format!("{j}"), "\"a\"b\nc\"");
1140    }
1141
1142    #[test]
1143    fn display_of_container_emits_the_raw_payload_even_when_corrupt() {
1144        assert_eq!(
1145            alloc::format!("{}", raw(JsonType::Array, "[1, 2]")),
1146            "[1, 2]"
1147        );
1148        assert_eq!(
1149            alloc::format!("{}", raw(JsonType::Object, "<<garbage>>")),
1150            "<<garbage>>"
1151        );
1152        assert_eq!(alloc::format!("{}", raw(JsonType::Array, "")), "");
1153    }
1154
1155    // ==================================================================
1156    // JsonParseError::fmt  (serializer)
1157    // ==================================================================
1158
1159    #[test]
1160    fn parse_error_display_with_and_without_position() {
1161        let with_pos = JsonParseError {
1162            message: az("expected value"),
1163            line: 3,
1164            column: 7,
1165        };
1166        assert_eq!(alloc::format!("{with_pos}"), "3:7: expected value");
1167
1168        let no_pos = JsonParseError {
1169            message: az("expected value"),
1170            line: 0,
1171            column: 99,
1172        };
1173        assert_eq!(alloc::format!("{no_pos}"), "expected value");
1174    }
1175
1176    #[test]
1177    fn parse_error_display_edge_values_do_not_panic() {
1178        let empty = JsonParseError {
1179            message: az(""),
1180            line: 0,
1181            column: 0,
1182        };
1183        assert_eq!(alloc::format!("{empty}"), "");
1184
1185        let maxed = JsonParseError {
1186            message: az("πŸ˜€"),
1187            line: u32::MAX,
1188            column: u32::MAX,
1189        };
1190        assert_eq!(
1191            alloc::format!("{maxed}"),
1192            alloc::format!("{}:{}: πŸ˜€", u32::MAX, u32::MAX)
1193        );
1194    }
1195
1196    // ==================================================================
1197    // JsonKeyValue::create  (other: no_panic_smoke)
1198    // ==================================================================
1199
1200    #[test]
1201    fn key_value_create_preserves_key_and_value() {
1202        let kv = JsonKeyValue::create(az(""), Json::null());
1203        assert_eq!(kv.key.as_str(), "");
1204        assert!(kv.value.is_null());
1205
1206        let big_key = "k".repeat(100_000);
1207        let kv = JsonKeyValue::create(
1208            AzString::from(big_key.clone()),
1209            Json::number(f64::NEG_INFINITY),
1210        );
1211        assert_eq!(kv.key.as_str().len(), big_key.len());
1212        assert!(kv.value.is_number());
1213
1214        let kv = JsonKeyValue::create(az("πŸ˜€/\u{0}"), Json::string("v"));
1215        assert_eq!(kv.key.as_str(), "πŸ˜€/\u{0}");
1216        assert_eq!(kv.value.as_string(), OptionString::Some(az("v")));
1217    }
1218
1219    // ==================================================================
1220    // copy_from_array  (numeric: zero / min_max / overflow)
1221    // ==================================================================
1222
1223    #[test]
1224    fn json_vec_copy_from_array_null_ptr_is_empty_even_at_usize_max_len() {
1225        // The null check runs first, so a bogus (null, huge) pair from C must
1226        // yield an empty vec rather than constructing a wild slice.
1227        let v = JsonVec::copy_from_array(core::ptr::null(), 0);
1228        assert!(v.is_empty());
1229        let v = JsonVec::copy_from_array(core::ptr::null(), usize::MAX);
1230        assert!(v.is_empty());
1231        assert_eq!(v.len(), 0);
1232    }
1233
1234    #[test]
1235    fn json_vec_copy_from_array_zero_len_with_valid_ptr_is_empty() {
1236        let items = [Json::null(), Json::bool(true)];
1237        let v = JsonVec::copy_from_array(items.as_ptr(), 0);
1238        assert!(v.is_empty());
1239    }
1240
1241    #[test]
1242    fn json_vec_copy_from_array_deep_copies_the_elements() {
1243        let items = vec![
1244            Json::null(),
1245            Json::bool(true),
1246            Json::number(f64::NAN),
1247            Json::string("πŸ˜€"),
1248        ];
1249        let v = JsonVec::copy_from_array(items.as_ptr(), items.len());
1250        assert_eq!(v.len(), 4);
1251        assert_eq!(v.as_slice()[3].as_string(), OptionString::Some(az("πŸ˜€")));
1252        // The copy is independent: dropping the source must not invalidate it.
1253        drop(items);
1254        assert!(v.as_slice()[0].is_null());
1255        assert_eq!(v.as_slice()[1].as_bool(), OptionBool::Some(true));
1256        assert_eq!(v.as_slice()[3].raw_string(), "πŸ˜€");
1257    }
1258
1259    #[test]
1260    fn key_value_vec_copy_from_array_null_ptr_is_empty_even_at_usize_max_len() {
1261        let v = JsonKeyValueVec::copy_from_array(core::ptr::null(), 0);
1262        assert!(v.is_empty());
1263        let v = JsonKeyValueVec::copy_from_array(core::ptr::null(), usize::MAX);
1264        assert!(v.is_empty());
1265        assert_eq!(v.len(), 0);
1266    }
1267
1268    #[test]
1269    fn key_value_vec_copy_from_array_deep_copies_the_elements() {
1270        let items = vec![
1271            JsonKeyValue::create(az("a"), Json::integer(1)),
1272            JsonKeyValue::create(az(""), Json::null()),
1273        ];
1274        let v = JsonKeyValueVec::copy_from_array(items.as_ptr(), items.len());
1275        assert_eq!(v.len(), 2);
1276        let zero_len = JsonKeyValueVec::copy_from_array(items.as_ptr(), 0);
1277        assert!(zero_len.is_empty());
1278        drop(items);
1279        assert_eq!(v.as_slice()[0].key.as_str(), "a");
1280        assert_eq!(v.as_slice()[0].value.as_i64(), OptionI64::Some(1));
1281        assert_eq!(v.as_slice()[1].key.as_str(), "");
1282    }
1283
1284    // ==================================================================
1285    // Json::parse / parse_bytes β€” malformed input
1286    // ==================================================================
1287
1288    #[test]
1289    #[cfg(feature = "serde-json")]
1290    fn parse_empty_and_whitespace_only_input_is_an_error() {
1291        for s in ["", " ", "   ", "\t\n\r", "\u{feff}"] {
1292            let err = Json::parse(s).expect_err("empty/blank input must not parse");
1293            assert!(!err.message.as_str().is_empty());
1294            assert!(Json::parse_bytes(s.as_bytes()).is_err());
1295        }
1296        assert!(Json::parse_bytes(b"").is_err());
1297    }
1298
1299    #[test]
1300    #[cfg(feature = "serde-json")]
1301    fn parse_garbage_returns_err_and_never_panics() {
1302        for s in [
1303            "{", "}", "[", "]", ",", ":", "nul", "tru", "'a'", "{,}", "[,]", "{\"a\"}", "{\"a\":}",
1304            "[1,]", "{\"a\":1,}", "\"unterminated", "\\", "\u{0}", "01", "+1", ".5", "1.", "-",
1305            "0x10", "--1", "1e", "{'a':1}", "undefined",
1306        ] {
1307            assert!(Json::parse(s).is_err(), "{s:?} must be rejected");
1308            assert!(Json::parse_bytes(s.as_bytes()).is_err(), "{s:?} (bytes)");
1309        }
1310    }
1311
1312    #[test]
1313    #[cfg(feature = "serde-json")]
1314    fn parse_leading_and_trailing_junk() {
1315        // Surrounding whitespace is allowed and trimmed...
1316        assert_eq!(Json::parse("  1  ").expect("padded"), Json::integer(1));
1317        assert_eq!(Json::parse("\n\t{}\r\n").expect("padded"), Json::parse("{}").expect("{}"));
1318        // ...but trailing non-whitespace is not.
1319        for s in ["1;garbage", "{} {}", "null null", "1 2", "[1] x"] {
1320            assert!(Json::parse(s).is_err(), "{s:?} must be rejected");
1321        }
1322    }
1323
1324    #[test]
1325    #[cfg(feature = "serde-json")]
1326    fn parse_bytes_rejects_invalid_utf8_without_panicking() {
1327        assert!(Json::parse_bytes(&[0xFF, 0xFE, 0x00]).is_err());
1328        // Structurally valid JSON, but the string body is not UTF-8.
1329        assert!(Json::parse_bytes(&[b'"', 0xFF, b'"']).is_err());
1330        // Lone continuation byte inside an otherwise fine document.
1331        assert!(Json::parse_bytes(&[b'[', 0x80, b']']).is_err());
1332    }
1333
1334    #[test]
1335    #[cfg(feature = "serde-json")]
1336    fn parse_deeply_nested_input_errors_instead_of_overflowing_the_stack() {
1337        // serde_json's 128-level recursion cap turns this into an Err.
1338        let deep = alloc::format!("{}{}", "[".repeat(10_000), "]".repeat(10_000));
1339        assert!(Json::parse(&deep).is_err());
1340        assert!(Json::parse_bytes(deep.as_bytes()).is_err());
1341
1342        let deep_obj = alloc::format!("{}1{}", "{\"a\":".repeat(10_000), "}".repeat(10_000));
1343        assert!(Json::parse(&deep_obj).is_err());
1344
1345        // Unbalanced (never-closing) nesting must also terminate.
1346        let unbalanced = "[".repeat(100_000);
1347        assert!(Json::parse(&unbalanced).is_err());
1348    }
1349
1350    #[test]
1351    #[cfg(feature = "serde-json")]
1352    fn parse_extremely_long_input_terminates() {
1353        const N: usize = 200_000;
1354        let mut doc = String::with_capacity(N * 2 + 2);
1355        doc.push('[');
1356        for i in 0..N {
1357            if i > 0 {
1358                doc.push(',');
1359            }
1360            doc.push('1');
1361        }
1362        doc.push(']');
1363        let j = Json::parse(&doc).expect("a long flat array must parse");
1364        assert!(j.is_array());
1365        assert_eq!(j.len(), N);
1366
1367        // A ~1 MB string payload.
1368        let payload = "a".repeat(1_000_000);
1369        let j = Json::parse(&alloc::format!("\"{payload}\"")).expect("long string");
1370        assert_eq!(j.as_string(), OptionString::Some(AzString::from(payload)));
1371    }
1372
1373    #[test]
1374    #[cfg(feature = "serde-json")]
1375    fn parse_unicode_input() {
1376        let j = Json::parse("\"\u{1F600}\"").expect("emoji");
1377        assert_eq!(j.as_string(), OptionString::Some(az("\u{1F600}")));
1378
1379        // Combining marks + RTL override + escaped NUL survive the round trip.
1380        let j = Json::parse("\"e\\u0301\\u202e\\u0000\"").expect("escapes");
1381        assert_eq!(
1382            j.as_string(),
1383            OptionString::Some(az("e\u{0301}\u{202e}\u{0}"))
1384        );
1385
1386        // Non-ASCII keys.
1387        let j = Json::parse("{\"ΠΊΠ»ΡŽΡ‡\":\"Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅\"}").expect("cyrillic keys");
1388        assert_eq!(
1389            j.get_key("ΠΊΠ»ΡŽΡ‡").expect("key").as_string(),
1390            OptionString::Some(az("Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅"))
1391        );
1392
1393        // A lone surrogate is not encodable as UTF-8 and must be rejected.
1394        assert!(Json::parse("\"\\ud800\"").is_err());
1395    }
1396
1397    #[test]
1398    #[cfg(feature = "serde-json")]
1399    fn parse_boundary_numbers() {
1400        assert_eq!(Json::parse("0").expect("0").as_i64(), OptionI64::Some(0));
1401        // "-0" is accepted; the sign is not observable through as_number().
1402        match Json::parse("-0").expect("-0").as_number() {
1403            OptionF64::Some(v) => assert_eq!(v, 0.0),
1404            OptionF64::None => panic!("-0 must be a number"),
1405        }
1406
1407        // i64::MIN is exactly representable in f64 and reads back exactly.
1408        assert_eq!(
1409            Json::parse("-9223372036854775808").expect("i64::MIN").as_i64(),
1410            OptionI64::Some(i64::MIN)
1411        );
1412        // i64::MAX is NOT: it rounds up to 2^63 on the f64 hop through
1413        // `Number::as_f64()`, so as_i64() reports None (silent precision loss).
1414        let max = Json::parse("9223372036854775807").expect("i64::MAX");
1415        assert_eq!(max.as_number(), OptionF64::Some(two_pow_63()));
1416        assert_eq!(max.as_i64(), OptionI64::None);
1417        // Same for u64::MAX.
1418        assert_eq!(
1419            Json::parse("18446744073709551615").expect("u64::MAX").as_i64(),
1420            OptionI64::None
1421        );
1422
1423        assert!(Json::parse("1e308").expect("1e308").is_number());
1424        assert!(Json::parse("1e-308").expect("1e-308").is_number());
1425
1426        // JSON has no NaN/Infinity literals.
1427        for s in ["NaN", "nan", "Infinity", "-Infinity", "inf"] {
1428            assert!(Json::parse(s).is_err(), "{s:?} is not valid JSON");
1429        }
1430
1431        // Overflowing exponents must not panic; whatever serde decides, the
1432        // result is a deterministic Ok(number) or Err.
1433        if let Ok(j) = Json::parse("1e400") {
1434            assert!(j.is_number());
1435        }
1436        if let Ok(j) = Json::parse("1e-400") {
1437            assert!(j.is_number());
1438        }
1439    }
1440
1441    #[test]
1442    #[cfg(feature = "serde-json")]
1443    fn parse_valid_minimal_inputs() {
1444        assert_eq!(Json::parse("null").expect("null"), Json::null());
1445        assert_eq!(Json::parse("true").expect("true"), Json::bool(true));
1446        assert_eq!(Json::parse("false").expect("false"), Json::bool(false));
1447        assert_eq!(Json::parse("1").expect("1"), Json::integer(1));
1448        assert_eq!(Json::parse("1.5").expect("1.5"), Json::number(1.5));
1449        assert_eq!(Json::parse("\"s\"").expect("str"), Json::string("s"));
1450        assert!(Json::parse("[]").expect("[]").is_array());
1451        assert!(Json::parse("{}").expect("{}").is_object());
1452        // parse_bytes agrees with parse.
1453        assert_eq!(
1454            Json::parse_bytes(b"{\"a\":[1,2]}").expect("bytes"),
1455            Json::parse("{\"a\":[1,2]}").expect("str")
1456        );
1457    }
1458
1459    // ==================================================================
1460    // round trips  (round_trip: representative / edge / stable)
1461    // ==================================================================
1462
1463    #[cfg(feature = "serde-json")]
1464    fn round_trip_corpus() -> Vec<Json> {
1465        vec![
1466            Json::null(),
1467            Json::bool(true),
1468            Json::bool(false),
1469            Json::integer(0),
1470            Json::integer(-1),
1471            Json::integer(i64::MIN),
1472            Json::number(1.5),
1473            Json::number(-0.25),
1474            Json::number(1e21),
1475            Json::string(""),
1476            Json::string("πŸ˜€ \"quoted\" \\slash\\ \n\t \u{0}"),
1477            Json::parse("[]").expect("[]"),
1478            Json::parse("{}").expect("{}"),
1479            Json::parse("[1,[2,[3]],{\"k\":null}]").expect("nested array"),
1480            Json::parse("{\"a\":{\"b\":[1,2,3]},\"ΓΌnΓ―\":\"πŸ˜€\"}").expect("nested object"),
1481        ]
1482    }
1483
1484    #[test]
1485    #[cfg(feature = "serde-json")]
1486    fn parse_of_to_json_string_reproduces_the_value() {
1487        for j in round_trip_corpus() {
1488            let encoded = j.to_json_string();
1489            let decoded = Json::parse(encoded.as_str())
1490                .unwrap_or_else(|e| panic!("{encoded:?} must re-parse: {e}"));
1491            assert_eq!(decoded, j, "round trip failed for {encoded:?}");
1492        }
1493    }
1494
1495    #[test]
1496    #[cfg(feature = "serde-json")]
1497    fn serialize_parse_serialize_is_idempotent() {
1498        for j in round_trip_corpus() {
1499            let once = j.to_json_string();
1500            let twice = Json::parse(once.as_str()).expect("re-parse").to_json_string();
1501            assert_eq!(once.as_str(), twice.as_str());
1502        }
1503    }
1504
1505    #[test]
1506    #[cfg(feature = "serde-json")]
1507    fn round_trip_extreme_floats() {
1508        for n in [f64::MAX, f64::MIN, f64::MIN_POSITIVE, -f64::MIN_POSITIVE] {
1509            let j = Json::number(n);
1510            let encoded = j.to_json_string();
1511            let decoded = Json::parse(encoded.as_str())
1512                .unwrap_or_else(|e| panic!("{encoded:?} must re-parse: {e}"));
1513            assert_eq!(decoded.as_number(), OptionF64::Some(n));
1514        }
1515    }
1516
1517    #[test]
1518    #[cfg(feature = "serde-json")]
1519    fn non_finite_numbers_do_not_survive_to_json_string() {
1520        // BUG (characterized, not fixed here): `to_json_string()` promises valid
1521        // JSON, but for a non-finite `number_value` it falls back to Rust's float
1522        // Display and emits the bare tokens `NaN` / `inf` / `-inf`, which no JSON
1523        // parser accepts. `to_serde_value()` handles the same input correctly by
1524        // mapping it to `null`. Callers must therefore not assume
1525        // `parse(to_json_string(x))` succeeds for a hand-built non-finite number.
1526        for (n, token) in [
1527            (f64::NAN, "NaN"),
1528            (f64::INFINITY, "inf"),
1529            (f64::NEG_INFINITY, "-inf"),
1530        ] {
1531            let j = Json::number(n);
1532            let encoded = j.to_json_string();
1533            assert_eq!(encoded.as_str(), token);
1534            assert!(
1535                Json::parse(encoded.as_str()).is_err(),
1536                "{token} is not valid JSON"
1537            );
1538            // The serde path degrades safely instead.
1539            assert_eq!(j.to_serde_value(), serde_json::Value::Null);
1540        }
1541    }
1542
1543    // ==================================================================
1544    // from_serde_value / to_serde_value
1545    // ==================================================================
1546
1547    #[test]
1548    #[cfg(feature = "serde-json")]
1549    fn from_serde_value_maps_every_variant() {
1550        use serde_json::json;
1551
1552        assert!(Json::from_serde_value(json!(null)).is_null());
1553        assert_eq!(
1554            Json::from_serde_value(json!(true)).as_bool(),
1555            OptionBool::Some(true)
1556        );
1557        assert_eq!(
1558            Json::from_serde_value(json!(-3)).as_i64(),
1559            OptionI64::Some(-3)
1560        );
1561        assert_eq!(
1562            Json::from_serde_value(json!("πŸ˜€")).as_string(),
1563            OptionString::Some(az("πŸ˜€"))
1564        );
1565
1566        let arr = Json::from_serde_value(json!([1, "two", null]));
1567        assert!(arr.is_array());
1568        assert_eq!(arr.len(), 3);
1569        assert_eq!(arr.get_index(1).expect("idx 1"), Json::string("two"));
1570
1571        let obj = Json::from_serde_value(json!({"a": 1, "b": {"c": []}}));
1572        assert!(obj.is_object());
1573        assert_eq!(obj.len(), 2);
1574        assert_eq!(obj.get_key("a").expect("a"), Json::integer(1));
1575        // The serialized payload must itself be valid JSON (invariant).
1576        assert!(Json::parse(obj.raw_string()).is_ok());
1577    }
1578
1579    #[test]
1580    #[cfg(feature = "serde-json")]
1581    fn to_serde_value_round_trips_through_from_serde_value() {
1582        use serde_json::json;
1583
1584        for v in [
1585            json!(null),
1586            json!(false),
1587            json!(0),
1588            json!(-1.5),
1589            json!(""),
1590            json!([]),
1591            json!({}),
1592            json!({"k": [1, {"n": null}], "ΓΌ": "πŸ˜€"}),
1593        ] {
1594            let back = Json::from_serde_value(v.clone()).to_serde_value();
1595            assert_eq!(back, v);
1596        }
1597    }
1598
1599    #[test]
1600    #[cfg(feature = "serde-json")]
1601    fn to_serde_value_on_a_corrupt_container_yields_null() {
1602        assert_eq!(
1603            raw(JsonType::Array, "not json").to_serde_value(),
1604            serde_json::Value::Null
1605        );
1606        assert_eq!(
1607            raw(JsonType::Object, "").to_serde_value(),
1608            serde_json::Value::Null
1609        );
1610    }
1611
1612    // ==================================================================
1613    // Json::array / Json::object  (other: no_panic_smoke)
1614    // ==================================================================
1615
1616    #[test]
1617    #[cfg(feature = "serde-json")]
1618    fn array_constructor_handles_empty_and_non_finite_members() {
1619        let empty = Json::array(JsonVec::new());
1620        assert!(empty.is_array());
1621        assert_eq!(empty.len(), 0);
1622        assert!(empty.is_empty());
1623        assert_eq!(empty.raw_string(), "[]");
1624
1625        // A NaN member cannot be represented in JSON β€” it degrades to null
1626        // (via to_serde_value) rather than producing invalid output or panicking.
1627        let with_nan = Json::array(JsonVec::from_vec(vec![
1628            Json::number(f64::NAN),
1629            Json::number(f64::INFINITY),
1630            Json::integer(1),
1631        ]));
1632        assert_eq!(with_nan.raw_string(), "[null,null,1]");
1633        assert_eq!(with_nan.len(), 3);
1634        assert!(Json::parse(with_nan.raw_string()).is_ok());
1635    }
1636
1637    #[test]
1638    #[cfg(feature = "serde-json")]
1639    fn object_constructor_dedupes_duplicate_keys_last_one_wins() {
1640        let empty = Json::object(JsonKeyValueVec::new());
1641        assert!(empty.is_object());
1642        assert!(empty.is_empty());
1643        assert_eq!(empty.raw_string(), "{}");
1644
1645        let dup = Json::object(JsonKeyValueVec::from_vec(vec![
1646            JsonKeyValue::create(az("k"), Json::integer(1)),
1647            JsonKeyValue::create(az("k"), Json::integer(2)),
1648            JsonKeyValue::create(az(""), Json::null()),
1649        ]));
1650        assert_eq!(dup.len(), 2, "duplicate keys collapse into one entry");
1651        assert_eq!(dup.get_key("k").expect("k"), Json::integer(2));
1652        assert!(dup.get_key("").expect("empty key").is_null());
1653    }
1654
1655    #[test]
1656    #[cfg(feature = "serde-json")]
1657    fn object_constructor_escapes_hostile_keys() {
1658        let obj = Json::object(JsonKeyValueVec::from_vec(vec![JsonKeyValue::create(
1659            az("\"}\nπŸ˜€"),
1660            Json::string("v"),
1661        )]));
1662        // The payload must still be parseable JSON β€” i.e. the key was escaped.
1663        let reparsed = Json::parse(obj.raw_string()).expect("hostile key must be escaped");
1664        assert_eq!(
1665            reparsed.get_key("\"}\nπŸ˜€").expect("key").as_string(),
1666            OptionString::Some(az("v"))
1667        );
1668    }
1669
1670    // ==================================================================
1671    // len / is_empty  (getter + predicate)
1672    // ==================================================================
1673
1674    #[test]
1675    #[cfg(feature = "serde-json")]
1676    fn len_is_zero_for_scalars_which_makes_is_empty_true() {
1677        // Characterization: len()/is_empty() are documented "for arrays/objects";
1678        // for scalars they report 0 / true, so `is_empty()` is NOT "has no value".
1679        for j in [
1680            Json::null(),
1681            Json::bool(true),
1682            Json::integer(7),
1683            Json::string("hello"),
1684        ] {
1685            assert_eq!(j.len(), 0);
1686            assert!(j.is_empty());
1687        }
1688    }
1689
1690    #[test]
1691    #[cfg(feature = "serde-json")]
1692    fn len_of_containers_and_corrupt_payloads() {
1693        assert_eq!(Json::parse("[1,2,3]").expect("arr").len(), 3);
1694        assert_eq!(Json::parse("{\"a\":1}").expect("obj").len(), 1);
1695        assert_eq!(Json::parse("[]").expect("[]").len(), 0);
1696        // Corrupt payload β†’ 0 rather than a panic.
1697        assert_eq!(raw(JsonType::Array, "not json").len(), 0);
1698        assert!(raw(JsonType::Object, "").is_empty());
1699        // Tag/payload mismatch (Array tag over an object payload) β†’ 0.
1700        assert_eq!(raw(JsonType::Array, "{\"a\":1}").len(), 0);
1701    }
1702
1703    // ==================================================================
1704    // get_index / get_key / keys / to_array / to_object
1705    // ==================================================================
1706
1707    #[test]
1708    #[cfg(feature = "serde-json")]
1709    fn get_index_boundaries() {
1710        let arr = Json::parse("[10,20]").expect("arr");
1711        assert_eq!(arr.get_index(0).expect("0"), Json::integer(10));
1712        assert_eq!(arr.get_index(1).expect("1"), Json::integer(20));
1713        assert!(arr.get_index(2).is_none());
1714        assert!(arr.get_index(usize::MAX).is_none());
1715
1716        assert!(Json::parse("[]").expect("[]").get_index(0).is_none());
1717        // Non-arrays never index, whatever the payload says.
1718        assert!(Json::parse("{\"0\":1}").expect("obj").get_index(0).is_none());
1719        assert!(Json::string("abc").get_index(0).is_none());
1720        assert!(raw(JsonType::Array, "not json").get_index(0).is_none());
1721        assert!(raw(JsonType::Array, "{\"a\":1}").get_index(0).is_none());
1722    }
1723
1724    #[test]
1725    #[cfg(feature = "serde-json")]
1726    fn get_key_edge_inputs() {
1727        let obj = Json::parse("{\"\":1,\"a\":null,\"πŸ˜€\":[]}").expect("obj");
1728        assert_eq!(obj.get_key("").expect("empty key"), Json::integer(1));
1729        assert!(obj.get_key("a").expect("a").is_null());
1730        assert!(obj.get_key("πŸ˜€").expect("emoji").is_array());
1731        assert!(obj.get_key("missing").is_none());
1732        // Keys are exact, not prefix/trimmed matches.
1733        assert!(obj.get_key(" a").is_none());
1734        assert!(obj.get_key("A").is_none());
1735        // A huge key must not panic.
1736        assert!(obj.get_key(&"k".repeat(1_000_000)).is_none());
1737        // Non-objects always return None.
1738        assert!(Json::parse("[1]").expect("arr").get_key("0").is_none());
1739        assert!(Json::null().get_key("").is_none());
1740        assert!(raw(JsonType::Object, "not json").get_key("a").is_none());
1741    }
1742
1743    #[test]
1744    #[cfg(feature = "serde-json")]
1745    fn keys_returns_every_key_and_nothing_for_non_objects() {
1746        let obj = Json::parse("{\"b\":1,\"a\":2,\"\":3}").expect("obj");
1747        let keys = obj.keys();
1748        assert_eq!(keys.len(), 3);
1749        for expected in ["a", "b", ""] {
1750            assert!(
1751                keys.iter().any(|k| k.as_str() == expected),
1752                "missing key {expected:?}"
1753            );
1754        }
1755        assert!(Json::parse("{}").expect("{}").keys().is_empty());
1756        assert!(Json::parse("[1,2]").expect("arr").keys().is_empty());
1757        assert!(Json::string("x").keys().is_empty());
1758        assert!(raw(JsonType::Object, "not json").keys().is_empty());
1759    }
1760
1761    #[test]
1762    #[cfg(feature = "serde-json")]
1763    fn to_array_and_to_object_on_wrong_types_and_corrupt_payloads() {
1764        let arr = Json::parse("[null,1]").expect("arr").to_array().expect("to_array");
1765        assert_eq!(arr.len(), 2);
1766        assert!(arr.as_slice()[0].is_null());
1767        assert_eq!(arr.as_slice()[1].as_i64(), OptionI64::Some(1));
1768
1769        let obj = Json::parse("{\"a\":\"v\"}").expect("obj").to_object().expect("to_object");
1770        assert_eq!(obj.len(), 1);
1771        assert_eq!(obj.as_slice()[0].key.as_str(), "a");
1772        assert_eq!(
1773            obj.as_slice()[0].value.as_string(),
1774            OptionString::Some(az("v"))
1775        );
1776
1777        assert!(Json::parse("[]").expect("[]").to_array().expect("empty").is_empty());
1778        assert!(Json::parse("{}").expect("{}").to_object().expect("empty").is_empty());
1779
1780        // Wrong type / corrupt payload / tag mismatch β†’ None, never a panic.
1781        assert!(Json::null().to_array().is_none());
1782        assert!(Json::string("[]").to_array().is_none());
1783        assert!(Json::parse("[]").expect("[]").to_object().is_none());
1784        assert!(raw(JsonType::Array, "not json").to_array().is_none());
1785        assert!(raw(JsonType::Object, "").to_object().is_none());
1786        assert!(raw(JsonType::Array, "{\"a\":1}").to_array().is_none());
1787        assert!(raw(JsonType::Object, "[1]").to_object().is_none());
1788    }
1789
1790    // ==================================================================
1791    // to_json_string / to_string_pretty
1792    // ==================================================================
1793
1794    #[test]
1795    #[cfg(feature = "serde-json")]
1796    fn to_json_string_escapes_strings_unlike_display() {
1797        let j = Json::string("a\"b\nc\\d\u{0}");
1798        let encoded = j.to_json_string();
1799        // Valid JSON that re-parses to the identical value...
1800        assert_eq!(Json::parse(encoded.as_str()).expect("escaped"), j);
1801        // ...and it differs from the (documented as non-JSON) Display output.
1802        assert_ne!(encoded.as_str(), alloc::format!("{j}"));
1803    }
1804
1805    #[test]
1806    #[cfg(feature = "serde-json")]
1807    fn to_string_pretty_matches_to_json_string_for_scalars() {
1808        for j in [
1809            Json::null(),
1810            Json::bool(false),
1811            Json::integer(-7),
1812            Json::number(0.5),
1813            Json::string("πŸ˜€"),
1814        ] {
1815            assert_eq!(j.to_string_pretty().as_str(), j.to_json_string().as_str());
1816        }
1817    }
1818
1819    #[test]
1820    #[cfg(feature = "serde-json")]
1821    fn to_string_pretty_of_containers_reparses_to_the_same_value() {
1822        let j = Json::parse("{\"a\":[1,{\"b\":null}],\"c\":{}}").expect("obj");
1823        let pretty = j.to_string_pretty();
1824        assert!(pretty.as_str().contains('\n'), "pretty output must be indented");
1825        assert_eq!(Json::parse(pretty.as_str()).expect("pretty reparses"), j);
1826
1827        // A corrupt payload is passed through verbatim instead of panicking.
1828        let corrupt = raw(JsonType::Object, "not json");
1829        assert_eq!(corrupt.to_string_pretty().as_str(), "not json");
1830        assert_eq!(raw(JsonType::Array, "").to_string_pretty().as_str(), "");
1831    }
1832
1833    // ==================================================================
1834    // jq / jq_all  (other: no_panic_smoke)
1835    // ==================================================================
1836
1837    #[test]
1838    #[cfg(feature = "serde-json")]
1839    fn jq_on_scalars_only_matches_the_empty_pointer() {
1840        for j in [
1841            Json::null(),
1842            Json::bool(true),
1843            Json::integer(1),
1844            Json::string("s"),
1845        ] {
1846            assert_eq!(j.jq(""), j);
1847            assert!(j.jq("/").is_null());
1848            assert!(j.jq("/a").is_null());
1849            assert!(j.jq("nonsense").is_null());
1850            assert_eq!(j.jq_all("").as_ref().len(), 1);
1851            assert_eq!(j.jq_all("/a").as_ref().len(), 0);
1852        }
1853    }
1854
1855    #[test]
1856    #[cfg(feature = "serde-json")]
1857    fn jq_navigates_and_degrades_to_null() {
1858        let j = Json::parse("{\"a\":{\"b\":[10,20]},\"\":1}").expect("doc");
1859        assert_eq!(j.jq(""), j, "the empty pointer selects the whole document");
1860        assert_eq!(j.jq("/a/b/1"), Json::integer(20));
1861        assert_eq!(j.jq("/"), Json::integer(1), "'/' selects the empty-string key");
1862
1863        // Misses / malformed pointers / hostile input β†’ null, never a panic.
1864        assert!(j.jq("/a/b/2").is_null(), "out-of-range index");
1865        assert!(j.jq("/a/b/-1").is_null(), "negative index is not a usize");
1866        assert!(j.jq("/a/b/99999999999999999999").is_null(), "index overflows usize");
1867        assert!(j.jq("a/b").is_null(), "pointer must start with '/'");
1868        assert!(j.jq("/missing").is_null());
1869        assert!(j.jq(&"/a".repeat(100_000)).is_null(), "huge pointer");
1870        assert!(j.jq("/a/b/1/deeper").is_null(), "descending through a scalar");
1871        assert!(raw(JsonType::Object, "not json").jq("/a").is_null());
1872    }
1873
1874    #[test]
1875    #[cfg(feature = "serde-json")]
1876    fn jq_all_wildcards_and_empty_results() {
1877        let j = Json::parse("{\"a\":[{\"v\":1},{\"v\":2}],\"b\":{\"v\":3},\"c\":7}").expect("doc");
1878
1879        assert_eq!(j.jq_all("/a/*/v").as_ref().len(), 2);
1880        // A wildcard over an object iterates its values; scalars contribute nothing.
1881        assert_eq!(j.jq_all("/*/v").as_ref().len(), 1);
1882        assert_eq!(j.jq_all("/*").as_ref().len(), 3);
1883        assert_eq!(j.jq_all("").as_ref().len(), 1);
1884
1885        // No match / malformed / corrupt β†’ empty vec.
1886        assert_eq!(j.jq_all("/missing/*").as_ref().len(), 0);
1887        assert_eq!(j.jq_all("a").as_ref().len(), 0);
1888        assert_eq!(j.jq_all("/c/*").as_ref().len(), 0, "wildcard over a scalar");
1889        assert_eq!(j.jq_all("/*/*/*/*/*").as_ref().len(), 0);
1890        assert_eq!(
1891            raw(JsonType::Array, "not json").jq_all("/*").as_ref().len(),
1892            0
1893        );
1894
1895        // A pointer that is only wildcards, far longer than the document is deep.
1896        let many = "/*".repeat(10_000);
1897        assert_eq!(j.jq_all(&many).as_ref().len(), 0);
1898    }
1899
1900    // ==================================================================
1901    // jq_all_recursive_depth  (numeric: zero / min_max / overflow)
1902    // ==================================================================
1903
1904    #[test]
1905    #[cfg(feature = "serde-json")]
1906    fn jq_all_recursive_depth_honours_the_wildcard_cap() {
1907        let value = serde_json::json!({"a": 1});
1908
1909        // depth 0 (what jq_all_recursive passes) resolves normally.
1910        assert_eq!(Json::jq_all_recursive_depth(&value, "/a", 0).len(), 1);
1911        // Exactly at the cap it still resolves...
1912        assert_eq!(
1913            Json::jq_all_recursive_depth(&value, "/a", Json::JQ_MAX_WILDCARD_DEPTH).len(),
1914            1
1915        );
1916        // ...one past it, the guard fires and yields no match instead of recursing.
1917        assert_eq!(
1918            Json::jq_all_recursive_depth(&value, "/a", Json::JQ_MAX_WILDCARD_DEPTH + 1).len(),
1919            0
1920        );
1921        // usize::MAX must hit the guard before the `depth + 1` in the wildcard arm,
1922        // so there is no add-overflow panic.
1923        assert_eq!(Json::jq_all_recursive_depth(&value, "/*", usize::MAX).len(), 0);
1924        assert_eq!(Json::jq_all_recursive_depth(&value, "", usize::MAX).len(), 0);
1925    }
1926
1927    #[test]
1928    #[cfg(feature = "serde-json")]
1929    fn jq_all_wildcard_fanout_deeper_than_the_cap_returns_empty() {
1930        // Build the document programmatically: serde_json's own 128-level parse
1931        // cap means such a document can never come from `Json::parse`, so this is
1932        // the only way to drive the wildcard recursion to its 512 limit.
1933        fn nest(depth: usize) -> serde_json::Value {
1934            let mut v = serde_json::Value::from(1_i64);
1935            for _ in 0..depth {
1936                v = serde_json::Value::Array(vec![v]);
1937            }
1938            v
1939        }
1940
1941        // Below the cap: the leaf is found.
1942        let shallow = nest(400);
1943        let found = Json::jq_all_recursive(&shallow, &"/*".repeat(400));
1944        assert_eq!(found.len(), 1);
1945        assert_eq!(found[0].as_i64(), OptionI64::Some(1));
1946
1947        // Above the cap: the guard stops the recursion and returns no match.
1948        let deep = nest(600);
1949        assert!(Json::jq_all_recursive(&deep, &"/*".repeat(600)).is_empty());
1950    }
1951}