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

            
6
use alloc::string::String;
7
use alloc::vec::Vec;
8
use core::fmt;
9
use 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)]
23
pub 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)]
40
pub 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.
51
impl Default for Json {
52
    fn default() -> Self {
53
        Self::null()
54
    }
55
}
56

            
57
impl Default for JsonInternal {
58
26466
    fn default() -> Self {
59
26466
        Self {
60
26466
            string_value: AzString::from(String::new()),
61
26466
            number_value: 0.0,
62
26466
            bool_value: false,
63
26466
        }
64
26466
    }
65
}
66

            
67
/// Type of a JSON value
68
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69
#[repr(C)]
70
pub 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)]
88
pub 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

            
97
impl fmt::Display for JsonParseError {
98
4
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99
4
        if self.line > 0 {
100
2
            write!(f, "{}:{}: {}", self.line, self.column, self.message.as_str())
101
        } else {
102
2
            write!(f, "{}", self.message.as_str())
103
        }
104
4
    }
105
}
106

            
107
#[cfg(feature = "std")]
108
impl std::error::Error for JsonParseError {}
109

            
110
/// A key-value pair in a JSON object
111
#[derive(Debug, Clone, PartialEq)]
112
#[repr(C)]
113
pub struct JsonKeyValue {
114
    /// The key
115
    pub key: AzString,
116
    /// The value
117
    pub value: Json,
118
}
119

            
120
impl JsonKeyValue {
121
    /// Create a new key-value pair
122
9
    #[must_use] pub const fn create(key: AzString, value: Json) -> Self {
123
9
        Self { key, value }
124
9
    }
125
}
126

            
127
// ============================================================================
128
// FFI-safe collection types
129
// ============================================================================
130

            
131
/// Option type for JsonKeyValue
132
impl_option!(JsonKeyValue, OptionJsonKeyValue, copy = false, [Debug, Clone, PartialEq]);
133

            
134
/// Vec of JsonKeyValue (FFI-safe)
135
impl_vec!(JsonKeyValue, JsonKeyValueVec, JsonKeyValueVecDestructor, JsonKeyValueVecDestructorType, JsonKeyValueVecSlice, OptionJsonKeyValue);
136
impl_vec_clone!(JsonKeyValue, JsonKeyValueVec, JsonKeyValueVecDestructor);
137
impl_vec_debug!(JsonKeyValue, JsonKeyValueVec);
138

            
139
impl 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
4
    #[must_use] pub fn copy_from_array(ptr: *const JsonKeyValue, len: usize) -> Self {
144
4
        if ptr.is_null() || len == 0 {
145
3
            return Self::new();
146
1
        }
147
1
        let slice = unsafe { core::slice::from_raw_parts(ptr, len) };
148
1
        Self::from_vec(slice.to_vec())
149
4
    }
150
}
151

            
152
// FFI-safe JsonVec using impl_vec! macro
153
impl_vec!(Json, JsonVec, JsonVecDestructor, JsonVecDestructorType, JsonVecSlice, OptionJson);
154
impl_vec_clone!(Json, JsonVec, JsonVecDestructor);
155
impl_vec_debug!(Json, JsonVec);
156
impl_vec_partialeq!(Json, JsonVec);
157
impl_vec_mut!(Json, JsonVec);
158

            
159
impl 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
4
    #[must_use] pub fn copy_from_array(ptr: *const Json, len: usize) -> Self {
164
4
        if ptr.is_null() || len == 0 {
165
3
            return Self::new();
166
1
        }
167
1
        let slice = unsafe { core::slice::from_raw_parts(ptr, len) };
168
1
        Self::from_vec(slice.to_vec())
169
4
    }
170
}
171

            
172
// FFI-safe Result type for JSON parsing
173
impl_result!(
174
    Json,
175
    JsonParseError,
176
    ResultJsonJsonParseError,
177
    copy = false,
178
    [Debug, Clone, PartialEq]
179
);
180

            
181
// FFI-safe Option types for JSON
182
impl_option!(Json, OptionJson, copy = false, [Clone, Debug, PartialEq]);
183
impl_option!(JsonVec, OptionJsonVec, copy = false, [Clone, Debug]);
184
impl_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
188
impl_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
201
93
fn f64_as_i64(n: f64) -> Option<i64> {
202
93
    if n.fract() == 0.0 && n >= -(2_f64.powi(63)) && n < 2_f64.powi(63) {
203
37
        Some(n as i64)
204
    } else {
205
56
        None
206
    }
207
93
}
208

            
209
// ============================================================================
210
// Non-serde methods on Json (pure data, no parsing)
211
// ============================================================================
212

            
213
impl Json {
214
    /// Create a null JSON value
215
48
    #[must_use] pub fn null() -> Self {
216
48
        Self {
217
48
            value_type: JsonType::Null,
218
48
            internal: JsonInternal::default(),
219
48
        }
220
48
    }
221

            
222
    /// Create a boolean JSON value
223
28
    #[must_use] pub fn bool(value: bool) -> Self {
224
28
        Self {
225
28
            value_type: JsonType::Bool,
226
28
            internal: JsonInternal {
227
28
                string_value: AzString::from(String::new()),
228
28
                number_value: 0.0,
229
28
                bool_value: value,
230
28
            },
231
28
        }
232
28
    }
233

            
234
    /// Create a number JSON value (floating-point)
235
85
    #[must_use] pub fn number(value: f64) -> Self {
236
85
        Self {
237
85
            value_type: JsonType::Number,
238
85
            internal: JsonInternal {
239
85
                string_value: AzString::from(String::new()),
240
85
                number_value: value,
241
85
                bool_value: false,
242
85
            },
243
85
        }
244
85
    }
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
31
    #[must_use] pub fn integer(value: i64) -> Self {
252
31
        Self {
253
31
            value_type: JsonType::Number,
254
31
            internal: JsonInternal {
255
31
                string_value: AzString::from(String::new()),
256
31
                number_value: value as f64,
257
31
                bool_value: false,
258
31
            },
259
31
        }
260
31
    }
261

            
262
    /// Create a string JSON value
263
39
    pub fn string(value: impl Into<String>) -> Self {
264
39
        Self {
265
39
            value_type: JsonType::String,
266
39
            internal: JsonInternal {
267
39
                string_value: AzString::from(value.into()),
268
39
                number_value: 0.0,
269
39
                bool_value: false,
270
39
            },
271
39
        }
272
39
    }
273

            
274
    /// Check if this is null
275
40
    #[must_use] pub fn is_null(&self) -> bool {
276
40
        self.value_type == JsonType::Null
277
40
    }
278

            
279
    /// Check if this is a boolean
280
7
    #[must_use] pub fn is_bool(&self) -> bool {
281
7
        self.value_type == JsonType::Bool
282
7
    }
283

            
284
    /// Check if this is a number
285
14
    #[must_use] pub fn is_number(&self) -> bool {
286
14
        self.value_type == JsonType::Number
287
14
    }
288

            
289
    /// Check if this is a string
290
7
    #[must_use] pub fn is_string(&self) -> bool {
291
7
        self.value_type == JsonType::String
292
7
    }
293

            
294
    /// Check if this is an array
295
12
    #[must_use] pub fn is_array(&self) -> bool {
296
12
        self.value_type == JsonType::Array
297
12
    }
298

            
299
    /// Check if this is an object
300
17
    #[must_use] pub fn is_object(&self) -> bool {
301
17
        self.value_type == JsonType::Object
302
17
    }
303

            
304
    /// Get as boolean (returns None if not a bool)
305
6
    #[must_use] pub fn as_bool(&self) -> OptionBool {
306
6
        if self.value_type == JsonType::Bool {
307
5
            OptionBool::Some(self.internal.bool_value)
308
        } else {
309
1
            OptionBool::None
310
        }
311
6
    }
312

            
313
    /// Get as number (returns None if not a number)
314
15
    #[must_use] pub fn as_number(&self) -> OptionF64 {
315
15
        if self.value_type == JsonType::Number {
316
12
            OptionF64::Some(self.internal.number_value)
317
        } else {
318
3
            OptionF64::None
319
        }
320
15
    }
321

            
322
    /// Get as integer (returns None if not a number or not an integer)
323
22
    #[must_use] pub fn as_i64(&self) -> OptionI64 {
324
22
        if self.value_type == JsonType::Number {
325
20
            f64_as_i64(self.internal.number_value).map_or(OptionI64::None, OptionI64::Some)
326
        } else {
327
2
            OptionI64::None
328
        }
329
22
    }
330

            
331
    /// Get as string (returns None if not a string)
332
14
    #[must_use] pub fn as_string(&self) -> OptionString {
333
14
        if self.value_type == JsonType::String {
334
12
            OptionString::Some(self.internal.string_value.clone())
335
        } else {
336
2
            OptionString::None
337
        }
338
14
    }
339

            
340
    /// Get the raw internal string value (for arrays/objects this is the serialized JSON)
341
23
    #[must_use] pub fn raw_string(&self) -> &str {
342
23
        self.internal.string_value.as_str()
343
23
    }
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.
350
impl fmt::Display for Json {
351
21
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352
21
        match self.value_type {
353
3
            JsonType::Null => write!(f, "null"),
354
2
            JsonType::Bool => write!(f, "{}", self.internal.bool_value),
355
            JsonType::Number => {
356
10
                let num = self.internal.number_value;
357
10
                if let Some(i) = f64_as_i64(num) {
358
3
                    write!(f, "{i}")
359
                } else {
360
7
                    write!(f, "{num}")
361
                }
362
            }
363
3
            JsonType::String => write!(f, "\"{}\"", self.internal.string_value.as_str()),
364
            JsonType::Array | JsonType::Object => {
365
3
                write!(f, "{}", self.internal.string_value.as_str())
366
            }
367
        }
368
21
    }
369
}
370

            
371
// ============================================================================
372
// serde_json-dependent methods (gated behind "serde-json" feature)
373
// ============================================================================
374

            
375
#[cfg(feature = "serde-json")]
376
impl 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
4
    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
384
4
        self.to_serde_value().serialize(s)
385
4
    }
386
}
387

            
388
#[cfg(feature = "serde-json")]
389
impl<'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")]
396
impl 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
159
    pub fn parse(s: &str) -> Result<Self, JsonParseError> {
404
159
        let value: serde_json::Value = serde_json::from_str(s).map_err(|e| {
405
51
            JsonParseError {
406
51
                message: AzString::from(alloc::format!("{e}")),
407
51
                line: u32::try_from(e.line()).unwrap_or(u32::MAX),
408
51
                column: u32::try_from(e.column()).unwrap_or(u32::MAX),
409
51
            }
410
51
        })?;
411
108
        Ok(Self::from_serde_value(value))
412
159
    }
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
39
    pub fn parse_bytes(bytes: &[u8]) -> Result<Self, JsonParseError> {
421
39
        let value: serde_json::Value = serde_json::from_slice(bytes).map_err(|e| {
422
38
            JsonParseError {
423
38
                message: AzString::from(alloc::format!("{e}")),
424
38
                line: u32::try_from(e.line()).unwrap_or(u32::MAX),
425
38
                column: u32::try_from(e.column()).unwrap_or(u32::MAX),
426
38
            }
427
38
        })?;
428
1
        Ok(Self::from_serde_value(value))
429
39
    }
430

            
431
    /// Convert from `serde_json::Value`
432
155
    #[must_use] pub fn from_serde_value(value: serde_json::Value) -> Self {
433
155
        match value {
434
8
            serde_json::Value::Null => Self::null(),
435
8
            serde_json::Value::Bool(b) => Self::bool(b),
436
50
            serde_json::Value::Number(n) => Self::number(n.as_f64().unwrap_or(0.0)),
437
15
            serde_json::Value::String(s) => Self::string(s),
438
24
            serde_json::Value::Array(arr) => {
439
24
                let json_str = serde_json::to_string(&serde_json::Value::Array(arr)).unwrap_or_default();
440
24
                Self {
441
24
                    value_type: JsonType::Array,
442
24
                    internal: JsonInternal {
443
24
                        string_value: AzString::from(json_str),
444
24
                        number_value: 0.0,
445
24
                        bool_value: false,
446
24
                    },
447
24
                }
448
            }
449
50
            serde_json::Value::Object(obj) => {
450
50
                let json_str = serde_json::to_string(&serde_json::Value::Object(obj)).unwrap_or_default();
451
50
                Self {
452
50
                    value_type: JsonType::Object,
453
50
                    internal: JsonInternal {
454
50
                        string_value: AzString::from(json_str),
455
50
                        number_value: 0.0,
456
50
                        bool_value: false,
457
50
                    },
458
50
                }
459
            }
460
        }
461
155
    }
462

            
463
    /// Convert this Json to a `serde_json::Value`
464
    #[must_use]
465
28
    pub fn to_serde_value(&self) -> serde_json::Value {
466
28
        match self.value_type {
467
2
            JsonType::Null => serde_json::Value::Null,
468
1
            JsonType::Bool => serde_json::Value::Bool(self.internal.bool_value),
469
            JsonType::Number => {
470
10
                let num = self.internal.number_value;
471
10
                f64_as_i64(num).map_or_else(
472
6
                    || {
473
6
                        serde_json::Number::from_f64(num)
474
6
                            .map_or(serde_json::Value::Null, serde_json::Value::Number)
475
6
                    },
476
4
                    |i| serde_json::Value::Number(serde_json::Number::from(i)),
477
                )
478
            }
479
2
            JsonType::String => serde_json::Value::String(self.internal.string_value.as_str().to_string()),
480
            JsonType::Array | JsonType::Object => {
481
13
                serde_json::from_str(self.internal.string_value.as_str())
482
13
                    .unwrap_or(serde_json::Value::Null)
483
            }
484
        }
485
28
    }
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
2
    #[must_use] pub fn array(values: JsonVec) -> Self {
491
2
        let serde_array: Vec<serde_json::Value> = values
492
2
            .as_slice()
493
2
            .iter()
494
2
            .map(Self::to_serde_value)
495
2
            .collect();
496
2
        let json_str = serde_json::to_string(&serde_json::Value::Array(serde_array))
497
2
            .unwrap_or_else(|_| "[]".to_string());
498
2
        Self {
499
2
            value_type: JsonType::Array,
500
2
            internal: JsonInternal {
501
2
                string_value: AzString::from(json_str),
502
2
                number_value: 0.0,
503
2
                bool_value: false,
504
2
            },
505
2
        }
506
2
    }
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
3
    #[must_use] pub fn object(entries: JsonKeyValueVec) -> Self {
512
3
        let mut map = serde_json::Map::new();
513
4
        for kv in entries.as_slice() {
514
4
            map.insert(kv.key.as_str().to_string(), kv.value.to_serde_value());
515
4
        }
516
3
        let json_str = serde_json::to_string(&serde_json::Value::Object(map))
517
3
            .unwrap_or_else(|_| "{}".to_string());
518
3
        Self {
519
3
            value_type: JsonType::Object,
520
3
            internal: JsonInternal {
521
3
                string_value: AzString::from(json_str),
522
3
                number_value: 0.0,
523
3
                bool_value: false,
524
3
            },
525
3
        }
526
3
    }
527

            
528
    /// Get the number of elements (for arrays) or keys (for objects)
529
22
    #[must_use] pub fn len(&self) -> usize {
530
22
        match self.value_type {
531
            JsonType::Array => {
532
9
                if let Ok(serde_json::Value::Array(arr)) = serde_json::from_str(self.internal.string_value.as_str()) {
533
7
                    arr.len()
534
                } else {
535
2
                    0
536
                }
537
            }
538
            JsonType::Object => {
539
5
                if let Ok(serde_json::Value::Object(obj)) = serde_json::from_str(self.internal.string_value.as_str()) {
540
4
                    obj.len()
541
                } else {
542
1
                    0
543
                }
544
            }
545
8
            _ => 0,
546
        }
547
22
    }
548

            
549
    /// Check if empty (for arrays/objects)
550
7
    #[must_use] pub fn is_empty(&self) -> bool {
551
7
        self.len() == 0
552
7
    }
553

            
554
    /// Get array element by index
555
10
    #[must_use] pub fn get_index(&self, index: usize) -> Option<Self> {
556
10
        if self.value_type != JsonType::Array { return None; }
557
8
        let value: serde_json::Value = serde_json::from_str(self.internal.string_value.as_str()).ok()?;
558
7
        if let serde_json::Value::Array(arr) = value {
559
6
            arr.get(index).map(|v| Self::from_serde_value(v.clone()))
560
        } else {
561
1
            None
562
        }
563
10
    }
564

            
565
    /// Get object value by key
566
15
    #[must_use] pub fn get_key(&self, key: &str) -> Option<Self> {
567
15
        if self.value_type != JsonType::Object { return None; }
568
13
        let value: serde_json::Value = serde_json::from_str(self.internal.string_value.as_str()).ok()?;
569
12
        if let serde_json::Value::Object(obj) = value {
570
12
            obj.get(key).map(|v| Self::from_serde_value(v.clone()))
571
        } else {
572
            None
573
        }
574
15
    }
575

            
576
    /// Get all keys of an object
577
5
    #[must_use] pub fn keys(&self) -> Vec<AzString> {
578
5
        if self.value_type != JsonType::Object { return Vec::new(); }
579
3
        let value: serde_json::Value = match serde_json::from_str(self.internal.string_value.as_str()) {
580
2
            Ok(v) => v,
581
1
            Err(_) => return Vec::new(),
582
        };
583
2
        if let serde_json::Value::Object(obj) = value {
584
3
            obj.keys().map(|k| AzString::from(k.clone())).collect()
585
        } else {
586
            Vec::new()
587
        }
588
5
    }
589

            
590
    /// Convert array to Vec<Json>
591
6
    pub fn to_array(&self) -> Option<JsonVec> {
592
6
        if self.value_type != JsonType::Array { return None; }
593
4
        let value: serde_json::Value = serde_json::from_str(self.internal.string_value.as_str()).ok()?;
594
3
        if let serde_json::Value::Array(arr) = value {
595
2
            Some(arr.into_iter().map(Self::from_serde_value).collect())
596
        } else {
597
1
            None
598
        }
599
6
    }
600

            
601
    /// Convert object to Vec<JsonKeyValue>
602
5
    #[must_use] pub fn to_object(&self) -> Option<JsonKeyValueVec> {
603
5
        if self.value_type != JsonType::Object { return None; }
604
4
        let value: serde_json::Value = serde_json::from_str(self.internal.string_value.as_str()).ok()?;
605
3
        if let serde_json::Value::Object(obj) = value {
606
2
            Some(obj.into_iter().map(|(k, v)| JsonKeyValue {
607
1
                key: AzString::from(k),
608
1
                value: Self::from_serde_value(v),
609
2
            }).collect())
610
        } else {
611
1
            None
612
        }
613
5
    }
614

            
615
    /// Serialize to JSON string (returns `AzString`)
616
63
    #[must_use] pub fn to_json_string(&self) -> AzString {
617
63
        match self.value_type {
618
5
            JsonType::Null => AzString::from(alloc::string::String::from("null")),
619
8
            JsonType::Bool => AzString::from(if self.internal.bool_value { alloc::string::String::from("true") } else { alloc::string::String::from("false") }),
620
            JsonType::Number => {
621
29
                let num = self.internal.number_value;
622
29
                f64_as_i64(num).map_or_else(
623
18
                    || AzString::from(alloc::format!("{num}")),
624
11
                    |i| AzString::from(alloc::format!("{i}")),
625
                )
626
            }
627
            JsonType::String => {
628
9
                let escaped = serde_json::to_string(self.internal.string_value.as_str()).unwrap_or_default();
629
9
                AzString::from(escaped)
630
            }
631
            JsonType::Array | JsonType::Object => {
632
12
                self.internal.string_value.clone()
633
            }
634
        }
635
63
    }
636

            
637
    /// Serialize to pretty-printed JSON string
638
8
    #[must_use] pub fn to_string_pretty(&self) -> AzString {
639
8
        match self.value_type {
640
            JsonType::Null | JsonType::Bool | JsonType::Number | JsonType::String => {
641
5
                self.to_json_string()
642
            }
643
            JsonType::Array | JsonType::Object => {
644
3
                serde_json::from_str::<serde_json::Value>(self.internal.string_value.as_str())
645
3
                    .map_or_else(
646
2
                        |_| self.internal.string_value.clone(),
647
1
                        |value| {
648
1
                            AzString::from(
649
1
                                serde_json::to_string_pretty(&value).unwrap_or_default(),
650
                            )
651
1
                        },
652
                    )
653
            }
654
        }
655
8
    }
656

            
657
    /// Access a nested value using a JSON Pointer (RFC 6901).
658
27
    #[must_use] pub fn jq(&self, path: &str) -> Self {
659
27
        match self.value_type {
660
            JsonType::Null | JsonType::Bool | JsonType::Number | JsonType::String => {
661
16
                if path.is_empty() { self.clone() } else { Self::null() }
662
            }
663
            JsonType::Array | JsonType::Object => {
664
11
                let value: serde_json::Value = match serde_json::from_str(self.internal.string_value.as_str()) {
665
10
                    Ok(v) => v,
666
1
                    Err(_) => return Self::null(),
667
                };
668
10
                value
669
10
                    .pointer(path)
670
10
                    .map_or_else(Self::null, |v| Self::from_serde_value(v.clone()))
671
            }
672
        }
673
27
    }
674

            
675
    /// Access nested values using a JSON Pointer with wildcard support.
676
22
    #[must_use] pub fn jq_all(&self, path: &str) -> JsonVec {
677
22
        let result = match self.value_type {
678
            JsonType::Null | JsonType::Bool | JsonType::Number | JsonType::String => {
679
8
                if path.is_empty() { vec![self.clone()] } else { vec![] }
680
            }
681
            JsonType::Array | JsonType::Object => {
682
14
                let value: serde_json::Value = match serde_json::from_str(self.internal.string_value.as_str()) {
683
13
                    Ok(v) => v,
684
1
                    Err(_) => return JsonVec::from_vec(vec![]),
685
                };
686
13
                Self::jq_all_recursive(&value, path)
687
            }
688
        };
689
21
        JsonVec::from_vec(result)
690
22
    }
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
15
    fn jq_all_recursive(value: &serde_json::Value, path: &str) -> Vec<Self> {
708
15
        Self::jq_all_recursive_depth(value, path, 0)
709
15
    }
710

            
711
960
    fn jq_all_recursive_depth(
712
960
        value: &serde_json::Value,
713
960
        path: &str,
714
960
        depth: usize,
715
960
    ) -> Vec<Self> {
716
        // Guard the wildcard recursion; exceeding the cap yields no match rather
717
        // than crashing.
718
960
        if depth > Self::JQ_MAX_WILDCARD_DEPTH {
719
4
            return vec![];
720
956
        }
721

            
722
        // Walk non-wildcard components iteratively.
723
956
        let mut value = value;
724
956
        let mut path = path;
725
        loop {
726
1071
            if path.is_empty() {
727
15
                return vec![Self::from_serde_value(value.clone())];
728
1056
            }
729
1056
            if !path.starts_with('/') {
730
1
                return vec![];
731
1055
            }
732
1055
            let rest = &path[1..];
733
1055
            let (component, remaining) =
734
1055
                rest.find('/').map_or((rest, ""), |idx| (&rest[..idx], &rest[idx..]));
735

            
736
1055
            if component == "*" {
737
936
                let mut results = Vec::new();
738
936
                match value {
739
917
                    serde_json::Value::Array(arr) => {
740
1839
                        for item in arr {
741
922
                            results.extend(Self::jq_all_recursive_depth(
742
922
                                item,
743
922
                                remaining,
744
922
                                depth + 1,
745
922
                            ));
746
922
                        }
747
                    }
748
10
                    serde_json::Value::Object(obj) => {
749
28
                        for (_key, val) in obj {
750
18
                            results.extend(Self::jq_all_recursive_depth(
751
18
                                val,
752
18
                                remaining,
753
18
                                depth + 1,
754
18
                            ));
755
18
                        }
756
                    }
757
9
                    _ => {}
758
                }
759
936
                return results;
760
119
            }
761

            
762
            // Single-child descent: advance the cursor instead of recursing.
763
119
            let next = match value {
764
2
                serde_json::Value::Array(arr) => {
765
2
                    component.parse::<usize>().ok().and_then(|idx| arr.get(idx))
766
                }
767
116
                serde_json::Value::Object(obj) => obj.get(component),
768
1
                _ => None,
769
            };
770
119
            match next {
771
115
                Some(v) => {
772
115
                    value = v;
773
115
                    path = remaining;
774
115
                }
775
4
                None => return vec![],
776
            }
777
        }
778
960
    }
779
}
780

            
781
#[cfg(test)]
782
mod 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
1
    fn huge_pointer_on_shallow_doc_returns_empty() {
794
1
        let json = Json::parse("{\"a\":{\"b\":1}}").expect("parse");
795
1
        let pointer = "/a".repeat(200_000);
796
1
        assert_eq!(json.jq_all(&pointer).as_ref().len(), 0);
797
1
    }
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
1
    fn deep_linear_pointer_resolves_leaf() {
804
        const DEPTH: usize = 100; // below serde_json's 128-level parse cap
805

            
806
1
        let mut doc = String::new();
807
101
        for _ in 0..DEPTH {
808
100
            doc.push_str("{\"a\":");
809
100
        }
810
1
        doc.push_str("42");
811
101
        for _ in 0..DEPTH {
812
100
            doc.push('}');
813
100
        }
814

            
815
1
        let json = Json::parse(&doc).expect("deep doc should parse");
816
1
        let pointer = "/a".repeat(DEPTH);
817
1
        let out = json.jq_all(&pointer);
818
1
        assert_eq!(out.as_ref().len(), 1, "the single leaf should be found");
819
1
    }
820

            
821
    /// Ordinary wildcard + index access still works after the iterative rewrite.
822
    #[test]
823
    #[cfg(feature = "serde-json")]
824
1
    fn wildcard_and_index_still_work() {
825
1
        let json = Json::parse("{\"items\":[{\"v\":1},{\"v\":2},{\"v\":3}]}").expect("parse");
826
1
        let all = json.jq_all("/items/*/v");
827
1
        assert_eq!(all.as_ref().len(), 3);
828
1
        let one = json.jq_all("/items/1/v");
829
1
        assert_eq!(one.as_ref().len(), 1);
830
1
    }
831
}
832

            
833
#[cfg(test)]
834
#[allow(clippy::float_cmp, clippy::unreadable_literal)]
835
mod 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),
            (Json::bool(false), JsonType::Bool),
            (Json::number(f64::NAN), JsonType::Number),
            (Json::integer(0), JsonType::Number),
            (Json::string(""), JsonType::String),
            (raw(JsonType::Array, "[]"), JsonType::Array),
            (raw(JsonType::Object, "{}"), JsonType::Object),
        ];
        for (j, ty) in &cases {
            let flags = [
                j.is_null(),
                j.is_bool(),
                j.is_number(),
                j.is_string(),
                j.is_array(),
                j.is_object(),
            ];
            assert_eq!(
                flags.iter().filter(|b| **b).count(),
                1,
                "exactly one predicate must hold for {ty:?}"
            );
            assert_eq!(j.value_type, *ty);
        }
    }
    #[test]
    fn predicates_on_a_default_internal_do_not_panic() {
        // A hand-rolled Json with a default (empty) payload — the FFI worst case.
        for ty in [
            JsonType::Null,
            JsonType::Bool,
            JsonType::Number,
            JsonType::String,
            JsonType::Array,
            JsonType::Object,
        ] {
            let j = Json {
                value_type: ty,
                internal: JsonInternal::default(),
            };
            assert_eq!(j.is_null(), ty == JsonType::Null);
            assert_eq!(j.is_object(), ty == JsonType::Object);
            assert_eq!(j.raw_string(), "");
        }
    }
    #[test]
    fn bool_constructor_keeps_both_values() {
        assert_eq!(Json::bool(true).as_bool(), OptionBool::Some(true));
        assert_eq!(Json::bool(false).as_bool(), OptionBool::Some(false));
        // The unused payload fields are zeroed, which the derived PartialEq relies on.
        assert_eq!(Json::bool(true).raw_string(), "");
        assert_eq!(Json::bool(true).as_number(), OptionF64::None);
    }
    #[test]
    fn string_constructor_handles_empty_unicode_and_huge_inputs() {
        assert_eq!(Json::string("").as_string(), OptionString::Some(az("")));
        let unicode = "😀 náïve \u{0301}\u{202e}\0 中文";
        assert_eq!(
            Json::string(unicode).as_string(),
            OptionString::Some(az(unicode))
        );
        assert_eq!(Json::string(unicode).raw_string(), unicode);
        let huge = "a".repeat(1_000_000);
        let j = Json::string(huge.clone());
        assert_eq!(j.raw_string().len(), 1_000_000);
        assert_eq!(j.as_string(), OptionString::Some(AzString::from(huge)));
    }
    // ==================================================================
    // getters  (getter: basic_access / edge_access)
    // ==================================================================
    #[test]
    fn getters_return_none_on_type_mismatch() {
        let null = Json::null();
        assert_eq!(null.as_bool(), OptionBool::None);
        assert_eq!(null.as_number(), OptionF64::None);
        assert_eq!(null.as_i64(), OptionI64::None);
        assert_eq!(null.as_string(), OptionString::None);
        // A Bool whose *number* payload happens to be set must still not be
        // readable as a number (the tag, not the payload, decides).
        let mut liar = Json::bool(true);
        liar.internal.number_value = 7.0;
        liar.internal.string_value = az("7");
        assert_eq!(liar.as_number(), OptionF64::None);
        assert_eq!(liar.as_i64(), OptionI64::None);
        assert_eq!(liar.as_string(), OptionString::None);
        assert_eq!(liar.as_bool(), OptionBool::Some(true));
        // ...but raw_string() is the *unchecked* accessor and does hand it back.
        assert_eq!(liar.raw_string(), "7");
    }
    #[test]
    fn raw_string_is_empty_for_scalars_and_serialized_for_containers() {
        assert_eq!(Json::null().raw_string(), "");
        assert_eq!(Json::bool(true).raw_string(), "");
        assert_eq!(Json::number(1.5).raw_string(), "");
        assert_eq!(Json::string("hi").raw_string(), "hi");
        assert_eq!(raw(JsonType::Array, "[1,2]").raw_string(), "[1,2]");
        // Corrupt payloads are handed back verbatim, never panic.
        assert_eq!(raw(JsonType::Object, "{not json").raw_string(), "{not json");
    }
    // ==================================================================
    // Display for Json  (round_trip / serializer)
    // ==================================================================
    #[test]
    fn display_scalar_values() {
        assert_eq!(alloc::format!("{}", Json::null()), "null");
        assert_eq!(alloc::format!("{}", Json::bool(true)), "true");
        assert_eq!(alloc::format!("{}", Json::bool(false)), "false");
        // Integral floats print without a fractional part (via f64_as_i64).
        assert_eq!(alloc::format!("{}", Json::number(3.0)), "3");
        assert_eq!(alloc::format!("{}", Json::integer(-42)), "-42");
        assert_eq!(alloc::format!("{}", Json::number(1.5)), "1.5");
        assert_eq!(alloc::format!("{}", Json::string("x")), "\"x\"");
    }
    #[test]
    fn display_of_non_finite_numbers_is_not_json() {
        // Characterization: Display is documented as human-readable, NOT JSON.
        assert_eq!(alloc::format!("{}", Json::number(f64::NAN)), "NaN");
        assert_eq!(alloc::format!("{}", Json::number(f64::INFINITY)), "inf");
        assert_eq!(alloc::format!("{}", Json::number(f64::NEG_INFINITY)), "-inf");
        // -0.0 loses its sign because f64_as_i64(-0.0) == Some(0).
        assert_eq!(alloc::format!("{}", Json::number(-0.0)), "0");
    }
    #[test]
    fn display_does_not_escape_strings() {
        // Documented caveat on the Display impl: embedded quotes/newlines are
        // NOT escaped, so the output is deliberately not valid JSON.
        let j = Json::string("a\"b\nc");
        assert_eq!(alloc::format!("{j}"), "\"a\"b\nc\"");
    }
    #[test]
    fn display_of_container_emits_the_raw_payload_even_when_corrupt() {
        assert_eq!(
            alloc::format!("{}", raw(JsonType::Array, "[1, 2]")),
            "[1, 2]"
        );
        assert_eq!(
            alloc::format!("{}", raw(JsonType::Object, "<<garbage>>")),
            "<<garbage>>"
        );
        assert_eq!(alloc::format!("{}", raw(JsonType::Array, "")), "");
    }
    // ==================================================================
    // JsonParseError::fmt  (serializer)
    // ==================================================================
    #[test]
    fn parse_error_display_with_and_without_position() {
        let with_pos = JsonParseError {
            message: az("expected value"),
            line: 3,
            column: 7,
        };
        assert_eq!(alloc::format!("{with_pos}"), "3:7: expected value");
        let no_pos = JsonParseError {
            message: az("expected value"),
            line: 0,
            column: 99,
        };
        assert_eq!(alloc::format!("{no_pos}"), "expected value");
    }
    #[test]
    fn parse_error_display_edge_values_do_not_panic() {
        let empty = JsonParseError {
            message: az(""),
            line: 0,
            column: 0,
        };
        assert_eq!(alloc::format!("{empty}"), "");
        let maxed = JsonParseError {
            message: az("😀"),
            line: u32::MAX,
            column: u32::MAX,
        };
        assert_eq!(
            alloc::format!("{maxed}"),
            alloc::format!("{}:{}: 😀", u32::MAX, u32::MAX)
        );
    }
    // ==================================================================
    // JsonKeyValue::create  (other: no_panic_smoke)
    // ==================================================================
    #[test]
    fn key_value_create_preserves_key_and_value() {
        let kv = JsonKeyValue::create(az(""), Json::null());
        assert_eq!(kv.key.as_str(), "");
        assert!(kv.value.is_null());
        let big_key = "k".repeat(100_000);
        let kv = JsonKeyValue::create(
            AzString::from(big_key.clone()),
            Json::number(f64::NEG_INFINITY),
        );
        assert_eq!(kv.key.as_str().len(), big_key.len());
        assert!(kv.value.is_number());
        let kv = JsonKeyValue::create(az("😀/\u{0}"), Json::string("v"));
        assert_eq!(kv.key.as_str(), "😀/\u{0}");
        assert_eq!(kv.value.as_string(), OptionString::Some(az("v")));
    }
    // ==================================================================
    // copy_from_array  (numeric: zero / min_max / overflow)
    // ==================================================================
    #[test]
    fn json_vec_copy_from_array_null_ptr_is_empty_even_at_usize_max_len() {
        // The null check runs first, so a bogus (null, huge) pair from C must
        // yield an empty vec rather than constructing a wild slice.
        let v = JsonVec::copy_from_array(core::ptr::null(), 0);
        assert!(v.is_empty());
        let v = JsonVec::copy_from_array(core::ptr::null(), usize::MAX);
        assert!(v.is_empty());
        assert_eq!(v.len(), 0);
    }
    #[test]
    fn json_vec_copy_from_array_zero_len_with_valid_ptr_is_empty() {
        let items = [Json::null(), Json::bool(true)];
        let v = JsonVec::copy_from_array(items.as_ptr(), 0);
        assert!(v.is_empty());
    }
    #[test]
    fn json_vec_copy_from_array_deep_copies_the_elements() {
        let items = vec![
            Json::null(),
            Json::bool(true),
            Json::number(f64::NAN),
            Json::string("😀"),
        ];
        let v = JsonVec::copy_from_array(items.as_ptr(), items.len());
        assert_eq!(v.len(), 4);
        assert_eq!(v.as_slice()[3].as_string(), OptionString::Some(az("😀")));
        // The copy is independent: dropping the source must not invalidate it.
        drop(items);
        assert!(v.as_slice()[0].is_null());
        assert_eq!(v.as_slice()[1].as_bool(), OptionBool::Some(true));
        assert_eq!(v.as_slice()[3].raw_string(), "😀");
    }
    #[test]
    fn key_value_vec_copy_from_array_null_ptr_is_empty_even_at_usize_max_len() {
        let v = JsonKeyValueVec::copy_from_array(core::ptr::null(), 0);
        assert!(v.is_empty());
        let v = JsonKeyValueVec::copy_from_array(core::ptr::null(), usize::MAX);
        assert!(v.is_empty());
        assert_eq!(v.len(), 0);
    }
    #[test]
    fn key_value_vec_copy_from_array_deep_copies_the_elements() {
        let items = vec![
            JsonKeyValue::create(az("a"), Json::integer(1)),
            JsonKeyValue::create(az(""), Json::null()),
        ];
        let v = JsonKeyValueVec::copy_from_array(items.as_ptr(), items.len());
        assert_eq!(v.len(), 2);
        let zero_len = JsonKeyValueVec::copy_from_array(items.as_ptr(), 0);
        assert!(zero_len.is_empty());
        drop(items);
        assert_eq!(v.as_slice()[0].key.as_str(), "a");
        assert_eq!(v.as_slice()[0].value.as_i64(), OptionI64::Some(1));
        assert_eq!(v.as_slice()[1].key.as_str(), "");
    }
    // ==================================================================
    // Json::parse / parse_bytes — malformed input
    // ==================================================================
    #[test]
    #[cfg(feature = "serde-json")]
    fn parse_empty_and_whitespace_only_input_is_an_error() {
        for s in ["", " ", "   ", "\t\n\r", "\u{feff}"] {
            let err = Json::parse(s).expect_err("empty/blank input must not parse");
            assert!(!err.message.as_str().is_empty());
            assert!(Json::parse_bytes(s.as_bytes()).is_err());
        }
        assert!(Json::parse_bytes(b"").is_err());
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn parse_garbage_returns_err_and_never_panics() {
        for s in [
            "{", "}", "[", "]", ",", ":", "nul", "tru", "'a'", "{,}", "[,]", "{\"a\"}", "{\"a\":}",
            "[1,]", "{\"a\":1,}", "\"unterminated", "\\", "\u{0}", "01", "+1", ".5", "1.", "-",
            "0x10", "--1", "1e", "{'a':1}", "undefined",
        ] {
            assert!(Json::parse(s).is_err(), "{s:?} must be rejected");
            assert!(Json::parse_bytes(s.as_bytes()).is_err(), "{s:?} (bytes)");
        }
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn parse_leading_and_trailing_junk() {
        // Surrounding whitespace is allowed and trimmed...
        assert_eq!(Json::parse("  1  ").expect("padded"), Json::integer(1));
        assert_eq!(Json::parse("\n\t{}\r\n").expect("padded"), Json::parse("{}").expect("{}"));
        // ...but trailing non-whitespace is not.
        for s in ["1;garbage", "{} {}", "null null", "1 2", "[1] x"] {
            assert!(Json::parse(s).is_err(), "{s:?} must be rejected");
        }
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn parse_bytes_rejects_invalid_utf8_without_panicking() {
        assert!(Json::parse_bytes(&[0xFF, 0xFE, 0x00]).is_err());
        // Structurally valid JSON, but the string body is not UTF-8.
        assert!(Json::parse_bytes(&[b'"', 0xFF, b'"']).is_err());
        // Lone continuation byte inside an otherwise fine document.
        assert!(Json::parse_bytes(&[b'[', 0x80, b']']).is_err());
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn parse_deeply_nested_input_errors_instead_of_overflowing_the_stack() {
        // serde_json's 128-level recursion cap turns this into an Err.
        let deep = alloc::format!("{}{}", "[".repeat(10_000), "]".repeat(10_000));
        assert!(Json::parse(&deep).is_err());
        assert!(Json::parse_bytes(deep.as_bytes()).is_err());
        let deep_obj = alloc::format!("{}1{}", "{\"a\":".repeat(10_000), "}".repeat(10_000));
        assert!(Json::parse(&deep_obj).is_err());
        // Unbalanced (never-closing) nesting must also terminate.
        let unbalanced = "[".repeat(100_000);
        assert!(Json::parse(&unbalanced).is_err());
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn parse_extremely_long_input_terminates() {
        const N: usize = 200_000;
        let mut doc = String::with_capacity(N * 2 + 2);
        doc.push('[');
        for i in 0..N {
            if i > 0 {
                doc.push(',');
            }
            doc.push('1');
        }
        doc.push(']');
        let j = Json::parse(&doc).expect("a long flat array must parse");
        assert!(j.is_array());
        assert_eq!(j.len(), N);
        // A ~1 MB string payload.
        let payload = "a".repeat(1_000_000);
        let j = Json::parse(&alloc::format!("\"{payload}\"")).expect("long string");
        assert_eq!(j.as_string(), OptionString::Some(AzString::from(payload)));
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn parse_unicode_input() {
        let j = Json::parse("\"\u{1F600}\"").expect("emoji");
        assert_eq!(j.as_string(), OptionString::Some(az("\u{1F600}")));
        // Combining marks + RTL override + escaped NUL survive the round trip.
        let j = Json::parse("\"e\\u0301\\u202e\\u0000\"").expect("escapes");
        assert_eq!(
            j.as_string(),
            OptionString::Some(az("e\u{0301}\u{202e}\u{0}"))
        );
        // Non-ASCII keys.
        let j = Json::parse("{\"ключ\":\"значение\"}").expect("cyrillic keys");
        assert_eq!(
            j.get_key("ключ").expect("key").as_string(),
            OptionString::Some(az("значение"))
        );
        // A lone surrogate is not encodable as UTF-8 and must be rejected.
        assert!(Json::parse("\"\\ud800\"").is_err());
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn parse_boundary_numbers() {
        assert_eq!(Json::parse("0").expect("0").as_i64(), OptionI64::Some(0));
        // "-0" is accepted; the sign is not observable through as_number().
        match Json::parse("-0").expect("-0").as_number() {
            OptionF64::Some(v) => assert_eq!(v, 0.0),
            OptionF64::None => panic!("-0 must be a number"),
        }
        // i64::MIN is exactly representable in f64 and reads back exactly.
        assert_eq!(
            Json::parse("-9223372036854775808").expect("i64::MIN").as_i64(),
            OptionI64::Some(i64::MIN)
        );
        // i64::MAX is NOT: it rounds up to 2^63 on the f64 hop through
        // `Number::as_f64()`, so as_i64() reports None (silent precision loss).
        let max = Json::parse("9223372036854775807").expect("i64::MAX");
        assert_eq!(max.as_number(), OptionF64::Some(two_pow_63()));
        assert_eq!(max.as_i64(), OptionI64::None);
        // Same for u64::MAX.
        assert_eq!(
            Json::parse("18446744073709551615").expect("u64::MAX").as_i64(),
            OptionI64::None
        );
        assert!(Json::parse("1e308").expect("1e308").is_number());
        assert!(Json::parse("1e-308").expect("1e-308").is_number());
        // JSON has no NaN/Infinity literals.
        for s in ["NaN", "nan", "Infinity", "-Infinity", "inf"] {
            assert!(Json::parse(s).is_err(), "{s:?} is not valid JSON");
        }
        // Overflowing exponents must not panic; whatever serde decides, the
        // result is a deterministic Ok(number) or Err.
        if let Ok(j) = Json::parse("1e400") {
            assert!(j.is_number());
        }
        if let Ok(j) = Json::parse("1e-400") {
            assert!(j.is_number());
        }
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn parse_valid_minimal_inputs() {
        assert_eq!(Json::parse("null").expect("null"), Json::null());
        assert_eq!(Json::parse("true").expect("true"), Json::bool(true));
        assert_eq!(Json::parse("false").expect("false"), Json::bool(false));
        assert_eq!(Json::parse("1").expect("1"), Json::integer(1));
        assert_eq!(Json::parse("1.5").expect("1.5"), Json::number(1.5));
        assert_eq!(Json::parse("\"s\"").expect("str"), Json::string("s"));
        assert!(Json::parse("[]").expect("[]").is_array());
        assert!(Json::parse("{}").expect("{}").is_object());
        // parse_bytes agrees with parse.
        assert_eq!(
            Json::parse_bytes(b"{\"a\":[1,2]}").expect("bytes"),
            Json::parse("{\"a\":[1,2]}").expect("str")
        );
    }
    // ==================================================================
    // round trips  (round_trip: representative / edge / stable)
    // ==================================================================
    #[cfg(feature = "serde-json")]
    fn round_trip_corpus() -> Vec<Json> {
        vec![
            Json::null(),
            Json::bool(true),
            Json::bool(false),
            Json::integer(0),
            Json::integer(-1),
            Json::integer(i64::MIN),
            Json::number(1.5),
            Json::number(-0.25),
            Json::number(1e21),
            Json::string(""),
            Json::string("😀 \"quoted\" \\slash\\ \n\t \u{0}"),
            Json::parse("[]").expect("[]"),
            Json::parse("{}").expect("{}"),
            Json::parse("[1,[2,[3]],{\"k\":null}]").expect("nested array"),
            Json::parse("{\"a\":{\"b\":[1,2,3]},\"ünï\":\"😀\"}").expect("nested object"),
        ]
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn parse_of_to_json_string_reproduces_the_value() {
        for j in round_trip_corpus() {
            let encoded = j.to_json_string();
            let decoded = Json::parse(encoded.as_str())
                .unwrap_or_else(|e| panic!("{encoded:?} must re-parse: {e}"));
            assert_eq!(decoded, j, "round trip failed for {encoded:?}");
        }
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn serialize_parse_serialize_is_idempotent() {
        for j in round_trip_corpus() {
            let once = j.to_json_string();
            let twice = Json::parse(once.as_str()).expect("re-parse").to_json_string();
            assert_eq!(once.as_str(), twice.as_str());
        }
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn round_trip_extreme_floats() {
        for n in [f64::MAX, f64::MIN, f64::MIN_POSITIVE, -f64::MIN_POSITIVE] {
            let j = Json::number(n);
            let encoded = j.to_json_string();
            let decoded = Json::parse(encoded.as_str())
                .unwrap_or_else(|e| panic!("{encoded:?} must re-parse: {e}"));
            assert_eq!(decoded.as_number(), OptionF64::Some(n));
        }
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn non_finite_numbers_do_not_survive_to_json_string() {
        // BUG (characterized, not fixed here): `to_json_string()` promises valid
        // JSON, but for a non-finite `number_value` it falls back to Rust's float
        // Display and emits the bare tokens `NaN` / `inf` / `-inf`, which no JSON
        // parser accepts. `to_serde_value()` handles the same input correctly by
        // mapping it to `null`. Callers must therefore not assume
        // `parse(to_json_string(x))` succeeds for a hand-built non-finite number.
        for (n, token) in [
            (f64::NAN, "NaN"),
            (f64::INFINITY, "inf"),
            (f64::NEG_INFINITY, "-inf"),
        ] {
            let j = Json::number(n);
            let encoded = j.to_json_string();
            assert_eq!(encoded.as_str(), token);
            assert!(
                Json::parse(encoded.as_str()).is_err(),
                "{token} is not valid JSON"
            );
            // The serde path degrades safely instead.
            assert_eq!(j.to_serde_value(), serde_json::Value::Null);
        }
    }
    // ==================================================================
    // from_serde_value / to_serde_value
    // ==================================================================
    #[test]
    #[cfg(feature = "serde-json")]
    fn from_serde_value_maps_every_variant() {
        use serde_json::json;
        assert!(Json::from_serde_value(json!(null)).is_null());
        assert_eq!(
            Json::from_serde_value(json!(true)).as_bool(),
            OptionBool::Some(true)
        );
        assert_eq!(
            Json::from_serde_value(json!(-3)).as_i64(),
            OptionI64::Some(-3)
        );
        assert_eq!(
            Json::from_serde_value(json!("😀")).as_string(),
            OptionString::Some(az("😀"))
        );
        let arr = Json::from_serde_value(json!([1, "two", null]));
        assert!(arr.is_array());
        assert_eq!(arr.len(), 3);
        assert_eq!(arr.get_index(1).expect("idx 1"), Json::string("two"));
        let obj = Json::from_serde_value(json!({"a": 1, "b": {"c": []}}));
        assert!(obj.is_object());
        assert_eq!(obj.len(), 2);
        assert_eq!(obj.get_key("a").expect("a"), Json::integer(1));
        // The serialized payload must itself be valid JSON (invariant).
        assert!(Json::parse(obj.raw_string()).is_ok());
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn to_serde_value_round_trips_through_from_serde_value() {
        use serde_json::json;
        for v in [
            json!(null),
            json!(false),
            json!(0),
            json!(-1.5),
            json!(""),
            json!([]),
            json!({}),
            json!({"k": [1, {"n": null}], "ü": "😀"}),
        ] {
            let back = Json::from_serde_value(v.clone()).to_serde_value();
            assert_eq!(back, v);
        }
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn to_serde_value_on_a_corrupt_container_yields_null() {
        assert_eq!(
            raw(JsonType::Array, "not json").to_serde_value(),
            serde_json::Value::Null
        );
        assert_eq!(
            raw(JsonType::Object, "").to_serde_value(),
            serde_json::Value::Null
        );
    }
    // ==================================================================
    // Json::array / Json::object  (other: no_panic_smoke)
    // ==================================================================
    #[test]
    #[cfg(feature = "serde-json")]
    fn array_constructor_handles_empty_and_non_finite_members() {
        let empty = Json::array(JsonVec::new());
        assert!(empty.is_array());
        assert_eq!(empty.len(), 0);
        assert!(empty.is_empty());
        assert_eq!(empty.raw_string(), "[]");
        // A NaN member cannot be represented in JSON — it degrades to null
        // (via to_serde_value) rather than producing invalid output or panicking.
        let with_nan = Json::array(JsonVec::from_vec(vec![
            Json::number(f64::NAN),
            Json::number(f64::INFINITY),
            Json::integer(1),
        ]));
        assert_eq!(with_nan.raw_string(), "[null,null,1]");
        assert_eq!(with_nan.len(), 3);
        assert!(Json::parse(with_nan.raw_string()).is_ok());
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn object_constructor_dedupes_duplicate_keys_last_one_wins() {
        let empty = Json::object(JsonKeyValueVec::new());
        assert!(empty.is_object());
        assert!(empty.is_empty());
        assert_eq!(empty.raw_string(), "{}");
        let dup = Json::object(JsonKeyValueVec::from_vec(vec![
            JsonKeyValue::create(az("k"), Json::integer(1)),
            JsonKeyValue::create(az("k"), Json::integer(2)),
            JsonKeyValue::create(az(""), Json::null()),
        ]));
        assert_eq!(dup.len(), 2, "duplicate keys collapse into one entry");
        assert_eq!(dup.get_key("k").expect("k"), Json::integer(2));
        assert!(dup.get_key("").expect("empty key").is_null());
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn object_constructor_escapes_hostile_keys() {
        let obj = Json::object(JsonKeyValueVec::from_vec(vec![JsonKeyValue::create(
            az("\"}\n😀"),
            Json::string("v"),
        )]));
        // The payload must still be parseable JSON — i.e. the key was escaped.
        let reparsed = Json::parse(obj.raw_string()).expect("hostile key must be escaped");
        assert_eq!(
            reparsed.get_key("\"}\n😀").expect("key").as_string(),
            OptionString::Some(az("v"))
        );
    }
    // ==================================================================
    // len / is_empty  (getter + predicate)
    // ==================================================================
    #[test]
    #[cfg(feature = "serde-json")]
    fn len_is_zero_for_scalars_which_makes_is_empty_true() {
        // Characterization: len()/is_empty() are documented "for arrays/objects";
        // for scalars they report 0 / true, so `is_empty()` is NOT "has no value".
        for j in [
            Json::null(),
            Json::bool(true),
            Json::integer(7),
            Json::string("hello"),
        ] {
            assert_eq!(j.len(), 0);
            assert!(j.is_empty());
        }
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn len_of_containers_and_corrupt_payloads() {
        assert_eq!(Json::parse("[1,2,3]").expect("arr").len(), 3);
        assert_eq!(Json::parse("{\"a\":1}").expect("obj").len(), 1);
        assert_eq!(Json::parse("[]").expect("[]").len(), 0);
        // Corrupt payload → 0 rather than a panic.
        assert_eq!(raw(JsonType::Array, "not json").len(), 0);
        assert!(raw(JsonType::Object, "").is_empty());
        // Tag/payload mismatch (Array tag over an object payload) → 0.
        assert_eq!(raw(JsonType::Array, "{\"a\":1}").len(), 0);
    }
    // ==================================================================
    // get_index / get_key / keys / to_array / to_object
    // ==================================================================
    #[test]
    #[cfg(feature = "serde-json")]
    fn get_index_boundaries() {
        let arr = Json::parse("[10,20]").expect("arr");
        assert_eq!(arr.get_index(0).expect("0"), Json::integer(10));
        assert_eq!(arr.get_index(1).expect("1"), Json::integer(20));
        assert!(arr.get_index(2).is_none());
        assert!(arr.get_index(usize::MAX).is_none());
        assert!(Json::parse("[]").expect("[]").get_index(0).is_none());
        // Non-arrays never index, whatever the payload says.
        assert!(Json::parse("{\"0\":1}").expect("obj").get_index(0).is_none());
        assert!(Json::string("abc").get_index(0).is_none());
        assert!(raw(JsonType::Array, "not json").get_index(0).is_none());
        assert!(raw(JsonType::Array, "{\"a\":1}").get_index(0).is_none());
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn get_key_edge_inputs() {
        let obj = Json::parse("{\"\":1,\"a\":null,\"😀\":[]}").expect("obj");
        assert_eq!(obj.get_key("").expect("empty key"), Json::integer(1));
        assert!(obj.get_key("a").expect("a").is_null());
        assert!(obj.get_key("😀").expect("emoji").is_array());
        assert!(obj.get_key("missing").is_none());
        // Keys are exact, not prefix/trimmed matches.
        assert!(obj.get_key(" a").is_none());
        assert!(obj.get_key("A").is_none());
        // A huge key must not panic.
        assert!(obj.get_key(&"k".repeat(1_000_000)).is_none());
        // Non-objects always return None.
        assert!(Json::parse("[1]").expect("arr").get_key("0").is_none());
        assert!(Json::null().get_key("").is_none());
        assert!(raw(JsonType::Object, "not json").get_key("a").is_none());
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn keys_returns_every_key_and_nothing_for_non_objects() {
        let obj = Json::parse("{\"b\":1,\"a\":2,\"\":3}").expect("obj");
        let keys = obj.keys();
        assert_eq!(keys.len(), 3);
        for expected in ["a", "b", ""] {
            assert!(
                keys.iter().any(|k| k.as_str() == expected),
                "missing key {expected:?}"
            );
        }
        assert!(Json::parse("{}").expect("{}").keys().is_empty());
        assert!(Json::parse("[1,2]").expect("arr").keys().is_empty());
        assert!(Json::string("x").keys().is_empty());
        assert!(raw(JsonType::Object, "not json").keys().is_empty());
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn to_array_and_to_object_on_wrong_types_and_corrupt_payloads() {
        let arr = Json::parse("[null,1]").expect("arr").to_array().expect("to_array");
        assert_eq!(arr.len(), 2);
        assert!(arr.as_slice()[0].is_null());
        assert_eq!(arr.as_slice()[1].as_i64(), OptionI64::Some(1));
        let obj = Json::parse("{\"a\":\"v\"}").expect("obj").to_object().expect("to_object");
        assert_eq!(obj.len(), 1);
        assert_eq!(obj.as_slice()[0].key.as_str(), "a");
        assert_eq!(
            obj.as_slice()[0].value.as_string(),
            OptionString::Some(az("v"))
        );
        assert!(Json::parse("[]").expect("[]").to_array().expect("empty").is_empty());
        assert!(Json::parse("{}").expect("{}").to_object().expect("empty").is_empty());
        // Wrong type / corrupt payload / tag mismatch → None, never a panic.
        assert!(Json::null().to_array().is_none());
        assert!(Json::string("[]").to_array().is_none());
        assert!(Json::parse("[]").expect("[]").to_object().is_none());
        assert!(raw(JsonType::Array, "not json").to_array().is_none());
        assert!(raw(JsonType::Object, "").to_object().is_none());
        assert!(raw(JsonType::Array, "{\"a\":1}").to_array().is_none());
        assert!(raw(JsonType::Object, "[1]").to_object().is_none());
    }
    // ==================================================================
    // to_json_string / to_string_pretty
    // ==================================================================
    #[test]
    #[cfg(feature = "serde-json")]
    fn to_json_string_escapes_strings_unlike_display() {
        let j = Json::string("a\"b\nc\\d\u{0}");
        let encoded = j.to_json_string();
        // Valid JSON that re-parses to the identical value...
        assert_eq!(Json::parse(encoded.as_str()).expect("escaped"), j);
        // ...and it differs from the (documented as non-JSON) Display output.
        assert_ne!(encoded.as_str(), alloc::format!("{j}"));
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn to_string_pretty_matches_to_json_string_for_scalars() {
        for j in [
            Json::null(),
            Json::bool(false),
            Json::integer(-7),
            Json::number(0.5),
            Json::string("😀"),
        ] {
            assert_eq!(j.to_string_pretty().as_str(), j.to_json_string().as_str());
        }
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn to_string_pretty_of_containers_reparses_to_the_same_value() {
        let j = Json::parse("{\"a\":[1,{\"b\":null}],\"c\":{}}").expect("obj");
        let pretty = j.to_string_pretty();
        assert!(pretty.as_str().contains('\n'), "pretty output must be indented");
        assert_eq!(Json::parse(pretty.as_str()).expect("pretty reparses"), j);
        // A corrupt payload is passed through verbatim instead of panicking.
        let corrupt = raw(JsonType::Object, "not json");
        assert_eq!(corrupt.to_string_pretty().as_str(), "not json");
        assert_eq!(raw(JsonType::Array, "").to_string_pretty().as_str(), "");
    }
    // ==================================================================
    // jq / jq_all  (other: no_panic_smoke)
    // ==================================================================
    #[test]
    #[cfg(feature = "serde-json")]
    fn jq_on_scalars_only_matches_the_empty_pointer() {
        for j in [
            Json::null(),
            Json::bool(true),
            Json::integer(1),
            Json::string("s"),
        ] {
            assert_eq!(j.jq(""), j);
            assert!(j.jq("/").is_null());
            assert!(j.jq("/a").is_null());
            assert!(j.jq("nonsense").is_null());
            assert_eq!(j.jq_all("").as_ref().len(), 1);
            assert_eq!(j.jq_all("/a").as_ref().len(), 0);
        }
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn jq_navigates_and_degrades_to_null() {
        let j = Json::parse("{\"a\":{\"b\":[10,20]},\"\":1}").expect("doc");
        assert_eq!(j.jq(""), j, "the empty pointer selects the whole document");
        assert_eq!(j.jq("/a/b/1"), Json::integer(20));
        assert_eq!(j.jq("/"), Json::integer(1), "'/' selects the empty-string key");
        // Misses / malformed pointers / hostile input → null, never a panic.
        assert!(j.jq("/a/b/2").is_null(), "out-of-range index");
        assert!(j.jq("/a/b/-1").is_null(), "negative index is not a usize");
        assert!(j.jq("/a/b/99999999999999999999").is_null(), "index overflows usize");
        assert!(j.jq("a/b").is_null(), "pointer must start with '/'");
        assert!(j.jq("/missing").is_null());
        assert!(j.jq(&"/a".repeat(100_000)).is_null(), "huge pointer");
        assert!(j.jq("/a/b/1/deeper").is_null(), "descending through a scalar");
        assert!(raw(JsonType::Object, "not json").jq("/a").is_null());
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn jq_all_wildcards_and_empty_results() {
        let j = Json::parse("{\"a\":[{\"v\":1},{\"v\":2}],\"b\":{\"v\":3},\"c\":7}").expect("doc");
        assert_eq!(j.jq_all("/a/*/v").as_ref().len(), 2);
        // A wildcard over an object iterates its values; scalars contribute nothing.
        assert_eq!(j.jq_all("/*/v").as_ref().len(), 1);
        assert_eq!(j.jq_all("/*").as_ref().len(), 3);
        assert_eq!(j.jq_all("").as_ref().len(), 1);
        // No match / malformed / corrupt → empty vec.
        assert_eq!(j.jq_all("/missing/*").as_ref().len(), 0);
        assert_eq!(j.jq_all("a").as_ref().len(), 0);
        assert_eq!(j.jq_all("/c/*").as_ref().len(), 0, "wildcard over a scalar");
        assert_eq!(j.jq_all("/*/*/*/*/*").as_ref().len(), 0);
        assert_eq!(
            raw(JsonType::Array, "not json").jq_all("/*").as_ref().len(),
            0
        );
        // A pointer that is only wildcards, far longer than the document is deep.
        let many = "/*".repeat(10_000);
        assert_eq!(j.jq_all(&many).as_ref().len(), 0);
    }
    // ==================================================================
    // jq_all_recursive_depth  (numeric: zero / min_max / overflow)
    // ==================================================================
    #[test]
    #[cfg(feature = "serde-json")]
    fn jq_all_recursive_depth_honours_the_wildcard_cap() {
        let value = serde_json::json!({"a": 1});
        // depth 0 (what jq_all_recursive passes) resolves normally.
        assert_eq!(Json::jq_all_recursive_depth(&value, "/a", 0).len(), 1);
        // Exactly at the cap it still resolves...
        assert_eq!(
            Json::jq_all_recursive_depth(&value, "/a", Json::JQ_MAX_WILDCARD_DEPTH).len(),
            1
        );
        // ...one past it, the guard fires and yields no match instead of recursing.
        assert_eq!(
            Json::jq_all_recursive_depth(&value, "/a", Json::JQ_MAX_WILDCARD_DEPTH + 1).len(),
            0
        );
        // usize::MAX must hit the guard before the `depth + 1` in the wildcard arm,
        // so there is no add-overflow panic.
        assert_eq!(Json::jq_all_recursive_depth(&value, "/*", usize::MAX).len(), 0);
        assert_eq!(Json::jq_all_recursive_depth(&value, "", usize::MAX).len(), 0);
    }
    #[test]
    #[cfg(feature = "serde-json")]
    fn jq_all_wildcard_fanout_deeper_than_the_cap_returns_empty() {
        // Build the document programmatically: serde_json's own 128-level parse
        // cap means such a document can never come from `Json::parse`, so this is
        // the only way to drive the wildcard recursion to its 512 limit.
        fn nest(depth: usize) -> serde_json::Value {
            let mut v = serde_json::Value::from(1_i64);
            for _ in 0..depth {
                v = serde_json::Value::Array(vec![v]);
            }
            v
        }
        // Below the cap: the leaf is found.
        let shallow = nest(400);
        let found = Json::jq_all_recursive(&shallow, &"/*".repeat(400));
        assert_eq!(found.len(), 1);
        assert_eq!(found[0].as_i64(), OptionI64::Some(1));
        // Above the cap: the guard stops the recursion and returns no match.
        let deep = nest(600);
        assert!(Json::jq_all_recursive(&deep, &"/*".repeat(600)).is_empty());
    }
}