Skip to main content

json_bourne/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3//! Type-driven JSON: parse straight into the caller's chosen type and
4//! serialize from it, with no generic `Value` middle layer.
5//!
6//! `json-bourne` skips the dynamic-tree intermediate that crates like
7//! `serde_json` use. Each type knows how to deserialize itself from a
8//! [`Lexer`] (via [`FromJson`](trait@FromJson)) or write itself to a [`JsonWrite`]
9//! sink (via [`ToJson`]). The typed structure already enforces JSON's
10//! grammar, so the per-event state machine is pure overhead for typed
11//! consumers — skipping it makes the typed path ~2× faster on
12//! integer / string-heavy payloads.
13//!
14//! - **No proc-macros.** [`from_json!`], [`to_json!`], and [`json!`]
15//!   are declarative `macro_rules!`. Empty dependency graph.
16//! - **`no_std` everywhere.** The streaming [`Lexer`] / [`Parser`]
17//!   layer is `no_std` always; with the default `std` feature off
18//!   the crate is `no_std + alloc`, and with `alloc` off too it is
19//!   pure `no_std`.
20//! - **Borrowed strings by default.** `&'input str` and `Cow<'input,
21//!   str>` parse zero-copy when the input contains no escapes.
22//! - **Bounded by construction.** Container nesting is depth-limited
23//!   (default 128, const-generic). The streaming parser is a state
24//!   machine with no recursion.
25//!
26//! # Quick start
27//!
28//! Parse a primitive directly into a Rust type:
29//!
30//! ```
31//! use json_bourne::parse_str;
32//! let n: u32 = parse_str("42").unwrap();
33//! assert_eq!(n, 42);
34//! ```
35//!
36//! Parse a struct (no proc-macro — `from_json!` is declarative):
37//!
38//! ```
39//! use json_bourne::{from_json, parse_str};
40//!
41//! from_json! {
42//!     #[derive(Debug, PartialEq)]
43//!     struct User<'input> {
44//!         id: u64,
45//!         name: &'input str,
46//!         active: bool,
47//!     }
48//! }
49//!
50//! let u: User<'_> = parse_str(r#"{"id":1,"name":"alice","active":true}"#).unwrap();
51//! assert_eq!(u.name, "alice");
52//! ```
53//!
54//! Serialize back out:
55//!
56//! ```
57//! use json_bourne::{to_json, to_string};
58//!
59//! to_json! {
60//!     struct Point { x: i32, y: i32 }
61//! }
62//!
63//! let s = to_string(&Point { x: 3, y: -7 }).unwrap();
64//! assert_eq!(s, r#"{"x":3,"y":-7}"#);
65//! ```
66//!
67//! # Output destinations
68//!
69//! | Sink                       | Entry point                 |
70//! |----------------------------|-----------------------------|
71//! | `String`                   | [`to_string`]               |
72//! | `Vec<u8>`                  | [`to_vec`]                  |
73//! | `String` (pretty-printed)  | [`to_string_pretty`]        |
74//! | any [`std::io::Write`]     | [`to_writer`] (std only)    |
75//! | any [`core::fmt::Write`]   | [`to_fmt`]                  |
76//!
77//! Custom sinks implement [`JsonWrite`] directly.
78//!
79//! # Features
80//!
81//! | Feature     | Default | Purpose                                     |
82//! |-------------|---------|---------------------------------------------|
83//! | `std`       | yes     | `HashMap`, `std::net`, `std::path`, `to_writer` |
84//! | `alloc`     | yes     | `String`, `Vec`, `Box`/`Rc`/`Arc`, escape decoding |
85//! | `indexmap`  | no      | `IndexMap` / `IndexSet` (insertion order)   |
86//!
87//! `default-features = false` plus `["alloc"]` gives a `no_std + alloc`
88//! build. `default-features = false` alone gives pure `no_std`: only the
89//! streaming [`Lexer`] / [`Parser`] and typed APIs that don't need a
90//! heap (e.g. parsing into stack-allocated primitives) are available.
91// Targeted uses of `unsafe` inside the streaming layer (lexer.rs / event.rs):
92//   1. `from_utf8_unchecked` after the lexer has validated every byte against
93//      the RFC 3629 byte ranges inline. The safe alternative re-walks the
94//      entire string per call and was 48% of `vec_borrowed_str` runtime.
95//   2. `core::arch::x86_64` SSE2 intrinsics in `scan_ascii_string_run_simd`.
96//      SSE2 is part of the x86_64 ABI baseline, so the `#[target_feature]`
97//      precondition is statically guaranteed on x86_64 — the unsafe is
98//      mechanical (intrinsics are unsafe by signature), not a memory-safety
99//      escape hatch. Non-x86_64 targets compile to the scalar path.
100//
101// Workspace lint is `deny` (not `forbid`) for exactly this kind of
102// localized, justified exception.
103#![allow(unsafe_code)]
104
105#[cfg(feature = "alloc")]
106extern crate alloc;
107
108mod de;
109mod error;
110mod event;
111#[cfg(feature = "alloc")]
112mod float;
113mod lexer;
114mod parser;
115mod ser;
116#[cfg(all(test, feature = "std"))]
117mod teju_gen;
118
119pub use de::{FromJson, parse, parse_str};
120#[cfg(feature = "alloc")]
121pub use de::{MapKey, key_to_cow};
122pub use error::{Error, ErrorKind, LineColumn, Position};
123pub use event::{Event, JsonNum, JsonStr, MAX_INPUT_LEN};
124pub use lexer::{Checkpoint, DEFAULT_MAX_DEPTH, Lexer, ValueKind};
125pub use parser::Parser;
126#[cfg(feature = "alloc")]
127pub use ser::{
128    ByteSink, FmtWriteSink, MapKeyOut, PrettyStringSink, StringSink, to_fmt, to_string,
129    to_string_pretty, to_vec,
130};
131#[cfg(feature = "std")]
132pub use ser::{IoWriteSink, to_writer};
133pub use ser::{JsonWrite, ToJson};
134
135mod macros;
136
137#[cfg(all(test, feature = "std"))]
138mod tests {
139    use super::*;
140
141    /// Regression: after `object_first_key` consumes the key + `:`, the
142    /// parser must be in a state where `next_event` correctly reads the
143    /// next byte as the start of a value (not as the start of a key).
144    /// Mixing the fast-path object API with `next_event` per-value is a
145    /// supported pattern — `UserBourne::from_event` in the bench does
146    /// exactly this for fields whose typed path doesn't have a fused
147    /// `parse_*_value` method (e.g. `bool`, `Option<&str>`, `Vec<&str>`).
148    #[test]
149    fn object_first_key_then_next_event_for_value() {
150        let mut p: Parser<'_> = Parser::new(br#"{"flag":true}"#);
151        let start = p.next_event().unwrap().unwrap();
152        assert!(matches!(start, Event::StartObject));
153
154        // Fast-path: read the key, leaving cursor past `:`.
155        let key = p.object_first_key().unwrap().unwrap();
156        assert_eq!(key, "flag");
157
158        // Now fall back to next_event for the value.
159        let val = p.next_event().unwrap().unwrap();
160        assert_eq!(val, Event::Bool(true));
161
162        // Object close.
163        let close = p.next_event().unwrap().unwrap();
164        assert!(matches!(close, Event::EndObject));
165        assert!(p.next_event().unwrap().is_none());
166    }
167
168    /// Same shape, but using `object_next_key` after the first value to
169    /// pull the next key. Exercises both fast-path entry points.
170    #[test]
171    fn object_next_key_handoff_to_next_event() {
172        let mut p: Parser<'_> = Parser::new(br#"{"a":1,"b":true}"#);
173        let _ = p.next_event().unwrap().unwrap(); // StartObject
174
175        let k1 = p.object_first_key().unwrap().unwrap();
176        assert_eq!(k1, "a");
177        let v1 = p.parse_i64_value().unwrap();
178        assert_eq!(v1, 1);
179
180        let k2 = p.object_next_key().unwrap().unwrap();
181        assert_eq!(k2, "b");
182        let v2 = p.next_event().unwrap().unwrap();
183        assert_eq!(v2, Event::Bool(true));
184
185        assert!(p.object_next_key().unwrap().is_none());
186        assert!(p.next_event().unwrap().is_none());
187    }
188
189    #[test]
190    fn primitives_round_through_typed_parse() {
191        assert!(parse_str::<bool>("true").unwrap());
192        assert!(!parse_str::<bool>("false").unwrap());
193        assert_eq!(parse_str::<i32>("-7").unwrap(), -7);
194        assert_eq!(parse_str::<u64>("9999999999").unwrap(), 9_999_999_999);
195        let f: f64 = parse_str("1.5e2").unwrap();
196        assert!((f - 150.0).abs() < f64::EPSILON);
197        assert_eq!(parse_str::<&str>("\"hello\"").unwrap(), "hello");
198    }
199
200    #[test]
201    fn integer_overflow_is_rejected_at_parse_site() {
202        let r = parse_str::<u8>("256");
203        assert_eq!(r.unwrap_err().kind, ErrorKind::NumberOutOfRange);
204    }
205
206    #[test]
207    fn type_mismatch_is_rejected_at_parse_site() {
208        let r = parse_str::<u32>("\"five\"");
209        assert_eq!(r.unwrap_err().kind, ErrorKind::ExpectedNumber);
210    }
211
212    #[test]
213    fn option_handles_null_and_value() {
214        let none: Option<u32> = parse_str("null").unwrap();
215        assert_eq!(none, None);
216        let some: Option<u32> = parse_str("17").unwrap();
217        assert_eq!(some, Some(17));
218    }
219
220    #[test]
221    fn fixed_array_exact_length() {
222        let arr: [i32; 3] = parse_str("[1,2,3]").unwrap();
223        assert_eq!(arr, [1, 2, 3]);
224    }
225
226    #[test]
227    fn fixed_array_too_short_errors() {
228        let r = parse_str::<[i32; 3]>("[1,2]");
229        assert!(r.is_err());
230    }
231
232    #[test]
233    fn fixed_array_too_long_errors() {
234        let r = parse_str::<[i32; 3]>("[1,2,3,4]");
235        assert!(r.is_err());
236    }
237
238    #[test]
239    fn tuple_heterogeneous() {
240        let v: (i32, &str, bool) = parse_str(r#"[1, "hi", true]"#).unwrap();
241        assert_eq!(v, (1, "hi", true));
242    }
243
244    #[test]
245    fn borrowed_str_lifetime() {
246        let input = String::from(r#""borrowed""#);
247        let s: &str = parse_str(&input).unwrap();
248        assert_eq!(s, "borrowed");
249    }
250
251    #[test]
252    fn rejects_trailing_data_at_typed_layer() {
253        let r = parse_str::<u32>("1 2");
254        assert!(r.is_err());
255    }
256
257    /// Regression: `parse_i64_value`'s fast-path accumulator must accept
258    /// `i64::MIN` (text `"-9223372036854775808"`). Earlier versions
259    /// accumulated as `i64`, so the unsigned magnitude (= `i64::MAX + 1`)
260    /// overflowed before the negation step and the input was rejected as
261    /// `NumberOutOfRange`. Pin both the value and the boundary +/- 1.
262    #[cfg(feature = "std")]
263    #[test]
264    fn duration_round_trips_fractional_seconds() {
265        use std::time::Duration;
266        let d: Duration = parse_str("1.5").unwrap();
267        assert_eq!(d, Duration::new(1, 500_000_000));
268        let d: Duration = parse_str("0").unwrap();
269        assert_eq!(d, Duration::ZERO);
270        // Negative is rejected.
271        assert!(parse_str::<Duration>("-1").is_err());
272    }
273
274    #[cfg(feature = "std")]
275    #[test]
276    fn system_time_round_trips_around_epoch() {
277        use std::time::{Duration, SystemTime, UNIX_EPOCH};
278        // Exact epoch.
279        let t: SystemTime = parse_str("0").unwrap();
280        assert_eq!(t, UNIX_EPOCH);
281        // Positive: 1.5s after epoch.
282        let t: SystemTime = parse_str("1.5").unwrap();
283        assert_eq!(t, UNIX_EPOCH + Duration::new(1, 500_000_000));
284        // Negative: 2s before epoch — the SystemTime model supports
285        // pre-epoch timestamps.
286        let t: SystemTime = parse_str("-2").unwrap();
287        assert_eq!(t, UNIX_EPOCH - Duration::from_secs(2));
288        // Wire round-trip.
289        let s = to_string(&(UNIX_EPOCH + Duration::from_secs(42))).unwrap();
290        let back: SystemTime = parse_str(&s).unwrap();
291        assert_eq!(back, UNIX_EPOCH + Duration::from_secs(42));
292    }
293
294    #[cfg(feature = "std")]
295    #[test]
296    fn ip_and_socket_addrs_parse_from_strings() {
297        use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
298        let v4: Ipv4Addr = parse_str(r#""127.0.0.1""#).unwrap();
299        assert_eq!(v4, Ipv4Addr::LOCALHOST);
300        let v6: Ipv6Addr = parse_str(r#""::1""#).unwrap();
301        assert_eq!(v6, Ipv6Addr::LOCALHOST);
302        let ip: IpAddr = parse_str(r#""10.0.0.1""#).unwrap();
303        assert!(matches!(ip, IpAddr::V4(_)));
304        let sa: SocketAddr = parse_str(r#""127.0.0.1:8080""#).unwrap();
305        assert_eq!(sa.port(), 8080);
306        // Garbage rejected.
307        assert!(parse_str::<Ipv4Addr>(r#""not-an-ip""#).is_err());
308    }
309
310    #[cfg(feature = "std")]
311    #[test]
312    fn pathbuf_parses_from_string() {
313        use std::path::PathBuf;
314        let p: PathBuf = parse_str(r#""/etc/hosts""#).unwrap();
315        assert_eq!(p, PathBuf::from("/etc/hosts"));
316    }
317
318    #[cfg(feature = "std")]
319    #[test]
320    fn hashmap_string_key() {
321        use std::collections::HashMap;
322        let m: HashMap<String, i32> = parse_str(r#"{"a":1,"b":2}"#).unwrap();
323        assert_eq!(m.get("a"), Some(&1));
324        assert_eq!(m.get("b"), Some(&2));
325        assert_eq!(m.len(), 2);
326    }
327
328    #[cfg(feature = "std")]
329    #[test]
330    fn hashmap_borrowed_key_zero_copy() {
331        use std::collections::HashMap;
332        let input = String::from(r#"{"alpha":1,"beta":2}"#);
333        let m: HashMap<&str, i32> = parse_str(&input).unwrap();
334        // Both keys must point inside the input buffer.
335        let input_start = input.as_ptr() as usize;
336        let input_end = input_start + input.len();
337        for k in m.keys() {
338            let p = k.as_ptr() as usize;
339            assert!((input_start..input_end).contains(&p), "key was copied");
340        }
341    }
342
343    #[cfg(feature = "alloc")]
344    #[test]
345    fn btreemap_round_trips() {
346        use std::collections::BTreeMap;
347        let m: BTreeMap<String, Vec<i32>> = parse_str(r#"{"x":[1,2],"y":[3]}"#).unwrap();
348        assert_eq!(m["x"], vec![1, 2]);
349        assert_eq!(m["y"], vec![3]);
350    }
351
352    #[cfg(feature = "std")]
353    #[test]
354    fn map_rejects_duplicate_key() {
355        use std::collections::HashMap;
356        let r: Result<HashMap<String, i32>, _> = parse_str(r#"{"a":1,"a":2}"#);
357        assert_eq!(r.unwrap_err().kind, ErrorKind::DuplicateKey);
358    }
359
360    #[cfg(feature = "std")]
361    #[test]
362    fn map_rejects_non_object() {
363        use std::collections::HashMap;
364        let r: Result<HashMap<String, i32>, _> = parse_str("[1,2]");
365        assert!(r.is_err());
366    }
367
368    #[cfg(feature = "std")]
369    #[test]
370    fn hashset_round_trips() {
371        use std::collections::HashSet;
372        let s: HashSet<i32> = parse_str("[1,2,3,2,1]").unwrap();
373        assert_eq!(s.len(), 3);
374        assert!(s.contains(&1));
375        assert!(s.contains(&2));
376        assert!(s.contains(&3));
377    }
378
379    #[cfg(feature = "alloc")]
380    #[test]
381    fn btreeset_round_trips() {
382        use std::collections::BTreeSet;
383        let s: BTreeSet<i32> = parse_str("[3,1,2]").unwrap();
384        assert_eq!(s.into_iter().collect::<Vec<_>>(), vec![1, 2, 3]);
385    }
386
387    #[cfg(feature = "alloc")]
388    #[test]
389    fn box_rc_arc_are_transparent_wrappers() {
390        let b: Box<u32> = parse_str("42").unwrap();
391        assert_eq!(*b, 42);
392        let r: std::rc::Rc<&str> = parse_str(r#""hello""#).unwrap();
393        assert_eq!(*r, "hello");
394        let a: std::sync::Arc<Vec<i32>> = parse_str("[1,2,3]").unwrap();
395        assert_eq!(*a, vec![1, 2, 3]);
396    }
397
398    #[cfg(feature = "alloc")]
399    #[test]
400    fn char_accepts_single_scalar() {
401        assert_eq!(parse_str::<char>(r#""a""#).unwrap(), 'a');
402        assert_eq!(parse_str::<char>(r#""中""#).unwrap(), '中');
403        // Escape that decodes to a single scalar.
404        assert_eq!(parse_str::<char>(r#""\n""#).unwrap(), '\n');
405        // Surrogate pair → single char above the BMP.
406        assert_eq!(parse_str::<char>(r#""😀""#).unwrap(), '😀');
407    }
408
409    #[cfg(feature = "alloc")]
410    #[test]
411    #[allow(clippy::unicode_not_nfc)] // intentional: tests NFD input
412    fn char_rejects_empty_or_multiple() {
413        assert!(parse_str::<char>(r#""""#).is_err());
414        assert!(parse_str::<char>(r#""ab""#).is_err());
415        // Combining mark sequence is multiple scalars even though it
416        // renders as one grapheme — char is a Unicode scalar, not a
417        // grapheme cluster.
418        assert!(parse_str::<char>(r#""é""#).is_err());
419        // Wrong type.
420        assert!(parse_str::<char>("42").is_err());
421    }
422
423    #[test]
424    fn i128_round_trips_full_range() {
425        assert_eq!(parse_str::<i128>("0").unwrap(), 0);
426        assert_eq!(
427            parse_str::<i128>("170141183460469231731687303715884105727").unwrap(),
428            i128::MAX,
429        );
430        assert_eq!(
431            parse_str::<i128>("-170141183460469231731687303715884105728").unwrap(),
432            i128::MIN,
433        );
434        assert!(parse_str::<i128>("170141183460469231731687303715884105728").is_err());
435    }
436
437    #[test]
438    fn u128_round_trips_full_range() {
439        assert_eq!(parse_str::<u128>("0").unwrap(), 0);
440        assert_eq!(
441            parse_str::<u128>("340282366920938463463374607431768211455").unwrap(),
442            u128::MAX,
443        );
444        assert!(parse_str::<u128>("340282366920938463463374607431768211456").is_err());
445        // Negative literals are not valid u128 input.
446        assert!(parse_str::<u128>("-1").is_err());
447    }
448
449    /// Pin the 38/39-digit boundary in `parse_i128_value` /
450    /// `parse_u128_value`. The fast path skips overflow checks for the
451    /// first 38 digits; the 39th switches to checked arithmetic. A
452    /// regression that off-by-ones the boundary would either accept an
453    /// out-of-range 39-digit literal or wrongly reject the largest
454    /// in-range one.
455    #[test]
456    fn i128_38_vs_39_digit_boundary() {
457        // 38 nines = 10^38 - 1. Comfortably inside i128 range.
458        let thirty_eight_nines = "9".repeat(38);
459        let v: i128 = parse_str(&thirty_eight_nines).unwrap();
460        assert_eq!(v.to_string(), thirty_eight_nines);
461
462        // 10^38 — the smallest 39-digit value. Inside i128 range.
463        let ten_pow_38 = format!("1{}", "0".repeat(38));
464        let v: i128 = parse_str(&ten_pow_38).unwrap();
465        assert_eq!(v.to_string(), ten_pow_38);
466
467        // 39 nines = 10^39 - 1, larger than i128::MAX. Must reject.
468        let thirty_nine_nines = "9".repeat(39);
469        assert!(parse_str::<i128>(&thirty_nine_nines).is_err());
470    }
471
472    #[test]
473    fn vec_i128_round_trips() {
474        // Exercises the new `vec_from_lex` override on the wide-int impl.
475        let v: Vec<i128> = parse_str(
476            "[0,1,-1,170141183460469231731687303715884105727,-170141183460469231731687303715884105728]",
477        )
478        .unwrap();
479        assert_eq!(v, vec![0, 1, -1, i128::MAX, i128::MIN]);
480    }
481
482    #[test]
483    fn vec_u128_round_trips() {
484        let v: Vec<u128> = parse_str("[0,1,2,340282366920938463463374607431768211455]").unwrap();
485        assert_eq!(v, vec![0, 1, 2, u128::MAX]);
486    }
487
488    /// JSON's grammar excludes non-finite floats. The lexer prevents
489    /// `inf`/`NaN`/`Infinity` from ever appearing as input text (the
490    /// number opener must be `-` or a digit), so the failure mode that
491    /// matters is *finite* literals whose magnitude overflows `f64` —
492    /// `str::parse::<f64>` silently returns `±inf` on those, and we
493    /// must reject them.
494    #[test]
495    fn f32_rejects_overflow_to_infinity() {
496        // 1e40 fits f64 but exceeds f32::MAX (~3.4e38), so the
497        // narrowing cast would round to ±inf. The impl rejects.
498        let r = parse_str::<f32>("1e40");
499        assert_eq!(r.unwrap_err().kind, ErrorKind::NumberOutOfRange);
500        let r = parse_str::<f32>("-1e40");
501        assert_eq!(r.unwrap_err().kind, ErrorKind::NumberOutOfRange);
502        // f32::MAX itself round-trips.
503        let v: f32 = parse_str("3.4028235e38").unwrap();
504        assert!(v.is_finite());
505    }
506
507    #[test]
508    fn f64_rejects_overflow_to_infinity() {
509        let r = parse_str::<f64>("1e400");
510        assert_eq!(r.unwrap_err().kind, ErrorKind::NumberOutOfRange);
511        let r = parse_str::<f64>("-1e400");
512        assert_eq!(r.unwrap_err().kind, ErrorKind::NumberOutOfRange);
513    }
514
515    #[test]
516    fn i64_min_is_parseable() {
517        assert_eq!(parse_str::<i64>("-9223372036854775808").unwrap(), i64::MIN);
518        assert_eq!(
519            parse_str::<i64>("-9223372036854775807").unwrap(),
520            i64::MIN + 1,
521        );
522        assert_eq!(parse_str::<i64>("9223372036854775807").unwrap(), i64::MAX);
523        // Just past i64::MIN must reject.
524        assert!(parse_str::<i64>("-9223372036854775809").is_err());
525        // Just past i64::MAX must reject.
526        assert!(parse_str::<i64>("9223372036854775808").is_err());
527        // Same boundaries via Vec<i64> (the bench-fixture path that surfaced this).
528        let v: Vec<i64> = parse_str("[-9223372036854775808]").unwrap();
529        assert_eq!(v, vec![i64::MIN]);
530    }
531
532    #[cfg(feature = "alloc")]
533    #[test]
534    fn vec_and_string() {
535        let v: Vec<i32> = parse_str("[10, 20, 30]").unwrap();
536        assert_eq!(v, vec![10, 20, 30]);
537        let s: String = parse_str(r#""owned""#).unwrap();
538        assert_eq!(s, "owned");
539    }
540
541    #[cfg(feature = "alloc")]
542    #[test]
543    fn nested_vec() {
544        let v: Vec<Vec<i32>> = parse_str("[[1,2],[3]]").unwrap();
545        assert_eq!(v, vec![vec![1, 2], vec![3]]);
546    }
547
548    // -----------------------------------------------------------------
549    // Escape decoding into String / Cow<str>.
550    // -----------------------------------------------------------------
551
552    #[cfg(feature = "alloc")]
553    #[test]
554    fn string_decodes_simple_escapes() {
555        let s: String = parse_str(r#""line1\nline2\ttab\"quote\\back\/slash""#).unwrap();
556        assert_eq!(s, "line1\nline2\ttab\"quote\\back/slash");
557    }
558
559    #[cfg(feature = "alloc")]
560    #[test]
561    fn string_decodes_b_and_f_escapes() {
562        // \b = U+0008, \f = U+000C — the rarely-used pair.
563        let s: String = parse_str(r#""\b\f""#).unwrap();
564        assert_eq!(s, "\u{0008}\u{000C}");
565    }
566
567    #[cfg(feature = "alloc")]
568    #[test]
569    fn string_decodes_unicode_bmp_escape() {
570        // Latin-1 (1-byte codepoint), Latin-Extended (2-byte UTF-8),
571        // and CJK (3-byte UTF-8) — covers each BMP encoding length.
572        let s: String = parse_str(r#""café 中文 ~""#).unwrap();
573        assert_eq!(s, "café 中文 ~");
574    }
575
576    #[cfg(feature = "alloc")]
577    #[test]
578    fn string_decodes_surrogate_pair() {
579        // U+1F600 (😀, GRINNING FACE) encodes as the surrogate pair
580        // 😀 in JSON. Decoded form is 4 bytes of UTF-8.
581        let s: String = parse_str(r#""hi 😀 there""#).unwrap();
582        assert_eq!(s, "hi 😀 there");
583    }
584
585    #[cfg(feature = "alloc")]
586    #[test]
587    fn string_rejects_lone_surrogate() {
588        let r = parse_str::<String>(r#""\uD800""#);
589        assert_eq!(r.unwrap_err().kind, ErrorKind::UnpairedSurrogate);
590        let r = parse_str::<String>(r#""\uDC00""#);
591        assert_eq!(r.unwrap_err().kind, ErrorKind::UnpairedSurrogate);
592    }
593
594    #[cfg(feature = "alloc")]
595    #[test]
596    fn string_rejects_high_surrogate_then_non_low() {
597        // \uD800 followed by a non-surrogate \u escape.
598        let r = parse_str::<String>(r#""\uD800A""#);
599        assert_eq!(r.unwrap_err().kind, ErrorKind::UnpairedSurrogate);
600    }
601
602    #[cfg(feature = "alloc")]
603    #[test]
604    fn string_decodes_long_mixed_input() {
605        // Mix literal stretches and several escapes — exercises the literal-
606        // run fast path and verifies the cursor advances correctly across
607        // multiple escapes in one string.
608        let json = r#""one\ttwo\nthreeAfour\\five\"six""#;
609        let s: String = parse_str(json).unwrap();
610        assert_eq!(s, "one\ttwo\nthreeAfour\\five\"six");
611    }
612
613    #[cfg(feature = "alloc")]
614    #[test]
615    fn vec_string_with_escapes_roundtrips() {
616        // Regression: this is the case `Vec<String>` could not parse
617        // before the decoder landed. Pin both empty-string and
618        // multi-escape-per-element forms.
619        let json = r#"["","a","\n","mixéd","\\\"\/"]"#;
620        let v: Vec<String> = parse_str(json).unwrap();
621        assert_eq!(v, vec!["", "a", "\n", "mixéd", "\\\"/"]);
622    }
623
624    #[cfg(feature = "alloc")]
625    #[test]
626    fn cow_borrows_when_no_escapes() {
627        use std::borrow::Cow;
628        let input = String::from(r#""borrowed""#);
629        let c: Cow<'_, str> = parse_str(&input).unwrap();
630        assert_eq!(c, "borrowed");
631        // Borrowed variant means the pointer lies inside the input buffer.
632        // A copy would land outside it.
633        match c {
634            Cow::Borrowed(s) => {
635                let input_start = input.as_ptr() as usize;
636                let input_end = input_start + input.len();
637                let s_ptr = s.as_ptr() as usize;
638                assert!(
639                    (input_start..input_end).contains(&s_ptr),
640                    "Cow::Borrowed pointer should be inside input",
641                );
642            }
643            Cow::Owned(_) => panic!("expected Borrowed for un-escaped input"),
644        }
645    }
646
647    #[cfg(feature = "alloc")]
648    #[test]
649    fn cow_owns_when_escapes_present() {
650        use std::borrow::Cow;
651        let c: Cow<'_, str> = parse_str(r#""line\nwrap""#).unwrap();
652        assert_eq!(c, "line\nwrap");
653        assert!(matches!(c, Cow::Owned(_)));
654    }
655
656    // -----------------------------------------------------------------
657    // Coverage: Parser::object_first_key_lex / object_next_key_lex
658    // -----------------------------------------------------------------
659
660    #[test]
661    fn parser_object_key_lex_with_escapes() {
662        let input = br#"{"a\nb":1,"c":2}"#;
663        let mut p: Parser<'_> = Parser::new(input);
664        assert!(matches!(
665            p.next_event().unwrap().unwrap(),
666            Event::StartObject
667        ));
668        let k1 = p.object_first_key_lex().unwrap().unwrap();
669        assert!(k1.has_escapes());
670        assert_eq!(p.parse_i64_value().unwrap(), 1);
671        let k2 = p.object_next_key_lex().unwrap().unwrap();
672        assert!(!k2.has_escapes());
673        assert_eq!(p.parse_i64_value().unwrap(), 2);
674        assert!(p.object_next_key_lex().unwrap().is_none());
675        assert!(p.next_event().unwrap().is_none());
676    }
677
678    #[test]
679    fn parser_object_key_lex_empty() {
680        let mut p: Parser<'_> = Parser::new(b"{}");
681        assert!(matches!(
682            p.next_event().unwrap().unwrap(),
683            Event::StartObject
684        ));
685        assert!(p.object_first_key_lex().unwrap().is_none());
686        assert!(p.next_event().unwrap().is_none());
687    }
688
689    // -----------------------------------------------------------------
690    // Coverage: Parser::array_start / array_continue
691    // -----------------------------------------------------------------
692
693    #[test]
694    fn parser_array_start_continue() {
695        let mut p: Parser<'_> = Parser::new(b"[1,2,3]");
696        assert!(!p.array_start().unwrap());
697        assert_eq!(p.parse_i64_value().unwrap(), 1);
698        assert!(!p.array_continue(b']').unwrap());
699        assert_eq!(p.parse_i64_value().unwrap(), 2);
700        assert!(!p.array_continue(b']').unwrap());
701        assert_eq!(p.parse_i64_value().unwrap(), 3);
702        assert!(p.array_continue(b']').unwrap());
703        assert!(p.next_event().unwrap().is_none());
704    }
705
706    #[test]
707    fn parser_array_start_empty() {
708        let mut p: Parser<'_> = Parser::new(b"[]");
709        assert!(p.array_start().unwrap());
710        assert!(p.next_event().unwrap().is_none());
711    }
712
713    #[test]
714    fn parser_array_nested_in_object() {
715        let mut p: Parser<'_> = Parser::new(br#"{"v":[1,2]}"#);
716        assert!(matches!(
717            p.next_event().unwrap().unwrap(),
718            Event::StartObject
719        ));
720        let key = p.object_first_key().unwrap().unwrap();
721        assert_eq!(key, "v");
722        assert!(!p.array_start().unwrap());
723        assert_eq!(p.parse_i64_value().unwrap(), 1);
724        assert!(!p.array_continue(b']').unwrap());
725        assert_eq!(p.parse_i64_value().unwrap(), 2);
726        assert!(p.array_continue(b']').unwrap());
727        assert!(p.object_next_key().unwrap().is_none());
728        assert!(p.next_event().unwrap().is_none());
729    }
730
731    // -----------------------------------------------------------------
732    // Coverage: ErrorKind::static_msg
733    // -----------------------------------------------------------------
734
735    #[test]
736    fn error_kind_display_covers_all_variants() {
737        use alloc::format;
738        let cases: &[(ErrorKind, &str)] = &[
739            (ErrorKind::UnexpectedEof, "unexpected end of input"),
740            (ErrorKind::InvalidEscape, "invalid string escape"),
741            (ErrorKind::InvalidUnicodeEscape, "invalid \\u escape"),
742            (
743                ErrorKind::UnpairedSurrogate,
744                "unpaired UTF-16 surrogate in \\u escape",
745            ),
746            (ErrorKind::InvalidUtf8, "invalid UTF-8"),
747            (ErrorKind::InvalidNumber, "invalid number literal"),
748            (
749                ErrorKind::NumberOutOfRange,
750                "number does not fit target type",
751            ),
752            (
753                ErrorKind::ControlCharInString,
754                "control character in string literal",
755            ),
756            (ErrorKind::TrailingData, "trailing data after JSON value"),
757            (
758                ErrorKind::DepthLimitExceeded,
759                "nesting depth limit exceeded",
760            ),
761            (ErrorKind::ExpectedValue, "expected JSON value"),
762            (ErrorKind::ExpectedString, "expected string"),
763            (ErrorKind::ExpectedNumber, "expected number"),
764            (ErrorKind::ExpectedBool, "expected boolean"),
765            (ErrorKind::ExpectedNull, "expected null"),
766            (ErrorKind::ExpectedArray, "expected array"),
767            (ErrorKind::ExpectedObject, "expected object"),
768            (ErrorKind::TypeMismatch, "type mismatch"),
769            (ErrorKind::DuplicateKey, "duplicate object key"),
770            (ErrorKind::MissingField, "missing required field"),
771            (ErrorKind::UnknownField, "unknown field"),
772            (
773                ErrorKind::NonFiniteFloat,
774                "non-finite float not representable in JSON",
775            ),
776            (ErrorKind::UnexpectedByte(0x7B), "unexpected byte 0x7b"),
777        ];
778        for (kind, expected) in cases {
779            assert_eq!(format!("{kind}"), *expected, "mismatch for {kind:?}");
780        }
781    }
782
783    // -----------------------------------------------------------------
784    // Coverage: Lexer::consume_utf8_multibyte — all leading-byte ranges
785    // -----------------------------------------------------------------
786
787    #[test]
788    fn utf8_f4_leading_byte_valid() {
789        let s = "\"\u{100000}\"";
790        let v: &str = parse_str(s).unwrap();
791        assert_eq!(v, "\u{100000}");
792    }
793
794    #[test]
795    fn utf8_f4_leading_byte_invalid_continuation() {
796        let mut bytes = b"\"".to_vec();
797        bytes.push(0xF4);
798        bytes.push(0x90); // too high for F4 (must be 0x80..0x8F)
799        bytes.push(0x80);
800        bytes.push(0x80);
801        bytes.push(b'"');
802        let r = parse::<&str>(&bytes);
803        assert_eq!(r.unwrap_err().kind, ErrorKind::InvalidUtf8);
804    }
805
806    #[test]
807    fn utf8_multibyte_all_leading_ranges() {
808        let cases: &[&str] = &[
809            "\"\u{0080}\"",   // C2: 2-byte
810            "\"\u{0800}\"",   // E0 A0: 3-byte low
811            "\"\u{1000}\"",   // E1: 3-byte mid
812            "\"\u{D7FF}\"",   // ED 9F: 3-byte just below surrogates
813            "\"\u{E000}\"",   // EE: 3-byte private use
814            "\"\u{10000}\"",  // F0 90: 4-byte low
815            "\"\u{40000}\"",  // F1: 4-byte mid
816            "\"\u{100000}\"", // F4 80: 4-byte high
817        ];
818        for input in cases {
819            let v: &str = parse_str(input).unwrap();
820            let expected = &input[1..input.len() - 1];
821            assert_eq!(v, expected);
822        }
823    }
824
825    // -----------------------------------------------------------------
826    // Coverage: write_escape_byte — all control bytes
827    // -----------------------------------------------------------------
828
829    #[test]
830    fn serialize_all_control_bytes() {
831        for b in 0x00_u8..=0x1F {
832            let s = alloc::string::String::from(b as char);
833            let json = to_string(&s).unwrap();
834            assert!(json.starts_with('"') && json.ends_with('"'));
835            let inner = &json[1..json.len() - 1];
836            match b {
837                b'"' | b'\\' => unreachable!(),
838                b'\n' => assert_eq!(inner, "\\n"),
839                b'\r' => assert_eq!(inner, "\\r"),
840                b'\t' => assert_eq!(inner, "\\t"),
841                0x08 => assert_eq!(inner, "\\b"),
842                0x0C => assert_eq!(inner, "\\f"),
843                _ => {
844                    let expected = alloc::format!("\\u00{b:02x}");
845                    assert_eq!(inner, expected, "byte 0x{b:02x}");
846                }
847            }
848        }
849    }
850
851    // -----------------------------------------------------------------
852    // Coverage: Parser::next_event — error-path branches
853    // -----------------------------------------------------------------
854
855    #[test]
856    fn next_event_rejects_trailing_comma_in_array() {
857        let mut p: Parser<'_> = Parser::new(b"[1,]");
858        assert!(matches!(
859            p.next_event().unwrap().unwrap(),
860            Event::StartArray
861        ));
862        let _ = p.next_event().unwrap().unwrap(); // Int(1)
863        let err = p.next_event().unwrap_err();
864        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b']'));
865    }
866
867    #[test]
868    fn next_event_rejects_trailing_comma_in_object() {
869        let mut p: Parser<'_> = Parser::new(br#"{"a":1,}"#);
870        assert!(matches!(
871            p.next_event().unwrap().unwrap(),
872            Event::StartObject
873        ));
874        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Key(_)));
875        let _ = p.next_event().unwrap().unwrap(); // Int(1)
876        let err = p.next_event().unwrap_err();
877        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b'}'));
878    }
879
880    #[test]
881    fn next_event_object_colon_eof() {
882        let mut p: Parser<'_> = Parser::new(br#"{"a""#);
883        assert!(matches!(
884            p.next_event().unwrap().unwrap(),
885            Event::StartObject
886        ));
887        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Key(_)));
888        let err = p.next_event().unwrap_err();
889        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
890    }
891
892    #[test]
893    fn next_event_object_colon_wrong_byte() {
894        let mut p: Parser<'_> = Parser::new(br#"{"a";"#);
895        assert!(matches!(
896            p.next_event().unwrap().unwrap(),
897            Event::StartObject
898        ));
899        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Key(_)));
900        let err = p.next_event().unwrap_err();
901        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b';'));
902    }
903
904    #[test]
905    fn next_event_empty_input() {
906        let mut p: Parser<'_> = Parser::new(b"");
907        let err = p.next_event().unwrap_err();
908        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
909    }
910
911    #[test]
912    fn next_event_eof_inside_array() {
913        let mut p: Parser<'_> = Parser::new(b"[");
914        assert!(matches!(
915            p.next_event().unwrap().unwrap(),
916            Event::StartArray
917        ));
918        let err = p.next_event().unwrap_err();
919        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
920    }
921
922    #[test]
923    fn next_event_eof_after_array_comma() {
924        let mut p: Parser<'_> = Parser::new(b"[1,");
925        assert!(matches!(
926            p.next_event().unwrap().unwrap(),
927            Event::StartArray
928        ));
929        let _ = p.next_event().unwrap().unwrap(); // 1
930        let err = p.next_event().unwrap_err();
931        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
932    }
933
934    #[test]
935    fn next_event_bad_byte_after_array_value() {
936        let mut p: Parser<'_> = Parser::new(b"[1;");
937        assert!(matches!(
938            p.next_event().unwrap().unwrap(),
939            Event::StartArray
940        ));
941        let _ = p.next_event().unwrap().unwrap(); // 1
942        let err = p.next_event().unwrap_err();
943        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b';'));
944    }
945
946    #[test]
947    fn next_event_eof_in_object_key_position() {
948        let mut p: Parser<'_> = Parser::new(b"{");
949        assert!(matches!(
950            p.next_event().unwrap().unwrap(),
951            Event::StartObject
952        ));
953        let err = p.next_event().unwrap_err();
954        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
955    }
956
957    #[test]
958    fn next_event_bad_byte_in_object_key_position() {
959        let mut p: Parser<'_> = Parser::new(b"{1");
960        assert!(matches!(
961            p.next_event().unwrap().unwrap(),
962            Event::StartObject
963        ));
964        let err = p.next_event().unwrap_err();
965        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b'1'));
966    }
967
968    #[test]
969    fn next_event_eof_after_object_value() {
970        let mut p: Parser<'_> = Parser::new(br#"{"a":1"#);
971        assert!(matches!(
972            p.next_event().unwrap().unwrap(),
973            Event::StartObject
974        ));
975        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Key(_)));
976        let _ = p.next_event().unwrap().unwrap(); // 1
977        let err = p.next_event().unwrap_err();
978        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
979    }
980
981    #[test]
982    fn next_event_bad_byte_after_object_value() {
983        let mut p: Parser<'_> = Parser::new(br#"{"a":1;"#);
984        assert!(matches!(
985            p.next_event().unwrap().unwrap(),
986            Event::StartObject
987        ));
988        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Key(_)));
989        let _ = p.next_event().unwrap().unwrap(); // 1
990        let err = p.next_event().unwrap_err();
991        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b';'));
992    }
993
994    #[test]
995    fn next_event_object_value_state_via_fast_path() {
996        let mut p: Parser<'_> = Parser::new(br#"{"x":42,"y":99}"#);
997        assert!(matches!(
998            p.next_event().unwrap().unwrap(),
999            Event::StartObject
1000        ));
1001        let k1 = p.object_first_key().unwrap().unwrap();
1002        assert_eq!(k1, "x");
1003        let v1 = p.next_event().unwrap().unwrap();
1004        assert!(matches!(v1, Event::Number(_)));
1005        let k2 = p.object_next_key().unwrap().unwrap();
1006        assert_eq!(k2, "y");
1007        let v2 = p.next_event().unwrap().unwrap();
1008        assert!(matches!(v2, Event::Number(_)));
1009        assert!(p.object_next_key().unwrap().is_none());
1010        assert!(p.next_event().unwrap().is_none());
1011    }
1012
1013    #[test]
1014    fn next_event_nested_containers_close_correctly() {
1015        let mut p: Parser<'_> = Parser::new(br#"{"a":[1],"b":{"c":2}}"#);
1016        assert!(matches!(
1017            p.next_event().unwrap().unwrap(),
1018            Event::StartObject
1019        ));
1020        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Key(_))); // "a"
1021        assert!(matches!(
1022            p.next_event().unwrap().unwrap(),
1023            Event::StartArray
1024        ));
1025        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Number(_)));
1026        assert!(matches!(p.next_event().unwrap().unwrap(), Event::EndArray));
1027        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Key(_))); // "b"
1028        assert!(matches!(
1029            p.next_event().unwrap().unwrap(),
1030            Event::StartObject
1031        ));
1032        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Key(_))); // "c"
1033        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Number(_)));
1034        assert!(matches!(p.next_event().unwrap().unwrap(), Event::EndObject));
1035        assert!(matches!(p.next_event().unwrap().unwrap(), Event::EndObject));
1036        assert!(p.next_event().unwrap().is_none());
1037    }
1038
1039    #[test]
1040    fn next_event_nested_array_in_array() {
1041        let input = b"[[],[1,2]]";
1042        let mut p: Parser<'_> = Parser::new(input);
1043        assert!(matches!(
1044            p.next_event().unwrap().unwrap(),
1045            Event::StartArray
1046        ));
1047        assert!(matches!(
1048            p.next_event().unwrap().unwrap(),
1049            Event::StartArray
1050        ));
1051        assert!(matches!(p.next_event().unwrap().unwrap(), Event::EndArray));
1052        assert!(matches!(
1053            p.next_event().unwrap().unwrap(),
1054            Event::StartArray
1055        ));
1056        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Number(_)));
1057        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Number(_)));
1058        assert!(matches!(p.next_event().unwrap().unwrap(), Event::EndArray));
1059        assert!(matches!(p.next_event().unwrap().unwrap(), Event::EndArray));
1060        assert!(p.next_event().unwrap().is_none());
1061    }
1062
1063    #[test]
1064    fn serialize_string_with_quote_and_backslash() {
1065        let s = "a\"b\\c";
1066        let json = to_string(&s).unwrap();
1067        assert_eq!(json, r#""a\"b\\c""#);
1068    }
1069
1070    #[test]
1071    fn serialize_string_with_all_named_escapes() {
1072        let s = "\x08\x0C\n\r\t";
1073        let json = to_string(&s).unwrap();
1074        assert_eq!(json, r#""\b\f\n\r\t""#);
1075    }
1076
1077    // -----------------------------------------------------------------
1078    // Coverage: Parser::object_first_key — nested container state paths
1079    // -----------------------------------------------------------------
1080
1081    #[test]
1082    fn parser_object_first_key_empty_inside_array() {
1083        let mut p: Parser<'_> = Parser::new(b"[{}]");
1084        assert!(matches!(
1085            p.next_event().unwrap().unwrap(),
1086            Event::StartArray
1087        ));
1088        assert!(matches!(
1089            p.next_event().unwrap().unwrap(),
1090            Event::StartObject
1091        ));
1092        assert!(p.object_first_key().unwrap().is_none());
1093        assert!(matches!(p.next_event().unwrap().unwrap(), Event::EndArray));
1094        assert!(p.next_event().unwrap().is_none());
1095    }
1096
1097    #[test]
1098    fn parser_object_first_key_empty_inside_object() {
1099        let mut p: Parser<'_> = Parser::new(br#"{"a":{}}"#);
1100        assert!(matches!(
1101            p.next_event().unwrap().unwrap(),
1102            Event::StartObject
1103        ));
1104        let k = p.object_first_key().unwrap().unwrap();
1105        assert_eq!(k, "a");
1106        assert!(matches!(
1107            p.next_event().unwrap().unwrap(),
1108            Event::StartObject
1109        ));
1110        assert!(p.object_first_key().unwrap().is_none());
1111        assert!(p.object_next_key().unwrap().is_none());
1112        assert!(p.next_event().unwrap().is_none());
1113    }
1114
1115    #[test]
1116    fn parser_object_first_key_lex_empty_inside_array() {
1117        let mut p: Parser<'_> = Parser::new(b"[{}]");
1118        assert!(matches!(
1119            p.next_event().unwrap().unwrap(),
1120            Event::StartArray
1121        ));
1122        assert!(matches!(
1123            p.next_event().unwrap().unwrap(),
1124            Event::StartObject
1125        ));
1126        assert!(p.object_first_key_lex().unwrap().is_none());
1127        assert!(matches!(p.next_event().unwrap().unwrap(), Event::EndArray));
1128        assert!(p.next_event().unwrap().is_none());
1129    }
1130
1131    #[test]
1132    fn parser_object_next_key_close_inside_array() {
1133        let mut p: Parser<'_> = Parser::new(br#"[{"a":1}]"#);
1134        assert!(matches!(
1135            p.next_event().unwrap().unwrap(),
1136            Event::StartArray
1137        ));
1138        assert!(matches!(
1139            p.next_event().unwrap().unwrap(),
1140            Event::StartObject
1141        ));
1142        let k = p.object_first_key().unwrap().unwrap();
1143        assert_eq!(k, "a");
1144        assert_eq!(p.parse_i64_value().unwrap(), 1);
1145        assert!(p.object_next_key().unwrap().is_none());
1146        assert!(matches!(p.next_event().unwrap().unwrap(), Event::EndArray));
1147        assert!(p.next_event().unwrap().is_none());
1148    }
1149
1150    #[test]
1151    fn parser_object_next_key_lex_close_inside_array() {
1152        let mut p: Parser<'_> = Parser::new(br#"[{"a":1}]"#);
1153        assert!(matches!(
1154            p.next_event().unwrap().unwrap(),
1155            Event::StartArray
1156        ));
1157        assert!(matches!(
1158            p.next_event().unwrap().unwrap(),
1159            Event::StartObject
1160        ));
1161        let k = p.object_first_key_lex().unwrap().unwrap();
1162        assert!(!k.has_escapes());
1163        assert_eq!(p.parse_i64_value().unwrap(), 1);
1164        assert!(p.object_next_key_lex().unwrap().is_none());
1165        assert!(matches!(p.next_event().unwrap().unwrap(), Event::EndArray));
1166        assert!(p.next_event().unwrap().is_none());
1167    }
1168
1169    // -----------------------------------------------------------------
1170    // Coverage: Lexer object_* — error paths via raw Lexer API
1171    // -----------------------------------------------------------------
1172
1173    #[test]
1174    fn lexer_object_first_key_bad_byte() {
1175        let mut lex: Lexer<'_> = Lexer::new(b"{1");
1176        lex.object_start().unwrap();
1177        let err = lex.object_first_key().unwrap_err();
1178        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b'1'));
1179    }
1180
1181    #[test]
1182    fn lexer_object_first_key_eof() {
1183        let mut lex: Lexer<'_> = Lexer::new(b"{");
1184        lex.object_start().unwrap();
1185        let err = lex.object_first_key().unwrap_err();
1186        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
1187    }
1188
1189    #[test]
1190    fn lexer_object_first_key_lex_bad_byte() {
1191        let mut lex: Lexer<'_> = Lexer::new(b"{1");
1192        lex.object_start().unwrap();
1193        let err = lex.object_first_key_lex().unwrap_err();
1194        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b'1'));
1195    }
1196
1197    #[test]
1198    fn lexer_object_first_key_lex_eof() {
1199        let mut lex: Lexer<'_> = Lexer::new(b"{");
1200        lex.object_start().unwrap();
1201        let err = lex.object_first_key_lex().unwrap_err();
1202        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
1203    }
1204
1205    #[test]
1206    fn lexer_object_next_key_bad_byte() {
1207        let mut lex: Lexer<'_> = Lexer::new(br#"{"a":1;"#);
1208        lex.object_start().unwrap();
1209        let _ = lex.object_first_key().unwrap();
1210        let _ = lex.parse_i64_value().unwrap();
1211        let err = lex.object_next_key().unwrap_err();
1212        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b';'));
1213    }
1214
1215    #[test]
1216    fn lexer_object_next_key_eof() {
1217        let mut lex: Lexer<'_> = Lexer::new(br#"{"a":1"#);
1218        lex.object_start().unwrap();
1219        let _ = lex.object_first_key().unwrap();
1220        let _ = lex.parse_i64_value().unwrap();
1221        let err = lex.object_next_key().unwrap_err();
1222        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
1223    }
1224
1225    #[test]
1226    fn lexer_object_next_key_bad_byte_after_comma() {
1227        let mut lex: Lexer<'_> = Lexer::new(br#"{"a":1,2"#);
1228        lex.object_start().unwrap();
1229        let _ = lex.object_first_key().unwrap();
1230        let _ = lex.parse_i64_value().unwrap();
1231        let err = lex.object_next_key().unwrap_err();
1232        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b'2'));
1233    }
1234
1235    #[test]
1236    fn lexer_object_next_key_lex_bad_byte() {
1237        let mut lex: Lexer<'_> = Lexer::new(br#"{"a":1;"#);
1238        lex.object_start().unwrap();
1239        let _ = lex.object_first_key_lex().unwrap();
1240        let _ = lex.parse_i64_value().unwrap();
1241        let err = lex.object_next_key_lex().unwrap_err();
1242        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b';'));
1243    }
1244
1245    #[test]
1246    fn lexer_object_next_key_lex_eof() {
1247        let mut lex: Lexer<'_> = Lexer::new(br#"{"a":1"#);
1248        lex.object_start().unwrap();
1249        let _ = lex.object_first_key_lex().unwrap();
1250        let _ = lex.parse_i64_value().unwrap();
1251        let err = lex.object_next_key_lex().unwrap_err();
1252        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
1253    }
1254
1255    #[test]
1256    fn lexer_object_next_key_lex_bad_byte_after_comma() {
1257        let mut lex: Lexer<'_> = Lexer::new(br#"{"a":1,2"#);
1258        lex.object_start().unwrap();
1259        let _ = lex.object_first_key_lex().unwrap();
1260        let _ = lex.parse_i64_value().unwrap();
1261        let err = lex.object_next_key_lex().unwrap_err();
1262        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b'2'));
1263    }
1264
1265    #[test]
1266    fn lexer_expect_colon_bad_byte() {
1267        let mut lex: Lexer<'_> = Lexer::new(br#"{"a";"#);
1268        lex.object_start().unwrap();
1269        let err = lex.object_first_key().unwrap_err();
1270        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b';'));
1271    }
1272
1273    #[test]
1274    fn lexer_expect_colon_eof() {
1275        let mut lex: Lexer<'_> = Lexer::new(br#"{"a""#);
1276        lex.object_start().unwrap();
1277        let err = lex.object_first_key().unwrap_err();
1278        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
1279    }
1280
1281    // -----------------------------------------------------------------
1282    // Coverage: [T; N]::from_lex — empty array with N=0, closed early
1283    // -----------------------------------------------------------------
1284
1285    #[test]
1286    fn fixed_array_zero_length() {
1287        let arr: [i32; 0] = parse_str("[]").unwrap();
1288        assert_eq!(arr, [0_i32; 0]);
1289    }
1290
1291    #[test]
1292    fn fixed_array_empty_but_expected_nonempty() {
1293        let r = parse_str::<[i32; 3]>("[]");
1294        assert!(r.is_err());
1295    }
1296
1297    #[test]
1298    fn fixed_array_closed_early() {
1299        let r = parse_str::<[i32; 3]>("[1]");
1300        assert!(r.is_err());
1301    }
1302
1303    // -----------------------------------------------------------------
1304    // Coverage: ErrorKind::static_msg — cross-category variants
1305    // -----------------------------------------------------------------
1306
1307    #[test]
1308    fn static_msg_cross_category() {
1309        use alloc::format;
1310        let kind = ErrorKind::UnexpectedEof;
1311        let msg = format!("{kind}");
1312        assert_eq!(msg, "unexpected end of input");
1313
1314        let kind = ErrorKind::ExpectedNull;
1315        let msg = format!("{kind}");
1316        assert_eq!(msg, "expected null");
1317    }
1318
1319    // -----------------------------------------------------------------
1320    // Coverage: write_escape_byte — quote and backslash arms
1321    // -----------------------------------------------------------------
1322
1323    #[test]
1324    fn serialize_string_containing_only_quote() {
1325        let s = "\"";
1326        let json = to_string(&s).unwrap();
1327        assert_eq!(json, r#""\"""#);
1328    }
1329
1330    #[test]
1331    fn serialize_string_containing_only_backslash() {
1332        let s = "\\";
1333        let json = to_string(&s).unwrap();
1334        assert_eq!(json, r#""\\""#);
1335    }
1336
1337    // -----------------------------------------------------------------
1338    // Coverage: consume_utf8_multibyte — error paths
1339    // -----------------------------------------------------------------
1340
1341    #[test]
1342    fn utf8_invalid_leading_byte() {
1343        let bytes = b"\"\xFF\"";
1344        let r = parse::<&str>(bytes);
1345        assert_eq!(r.unwrap_err().kind, ErrorKind::InvalidUtf8);
1346    }
1347
1348    #[test]
1349    fn utf8_truncated_3byte_sequence() {
1350        let mut bytes = b"\"".to_vec();
1351        bytes.push(0xE1);
1352        bytes.push(0x80);
1353        // missing third continuation byte — EOF
1354        let r = parse::<&str>(&bytes);
1355        assert_eq!(r.unwrap_err().kind, ErrorKind::InvalidUtf8);
1356    }
1357
1358    #[test]
1359    fn utf8_bad_third_continuation_byte() {
1360        let mut bytes = b"\"".to_vec();
1361        bytes.push(0xE1);
1362        bytes.push(0x80);
1363        bytes.push(0x00); // not a continuation byte
1364        let r = parse::<&str>(&bytes);
1365        assert!(r.is_err());
1366    }
1367
1368    #[test]
1369    fn utf8_4byte_bad_third_continuation() {
1370        let mut bytes = b"\"".to_vec();
1371        bytes.push(0xF0);
1372        bytes.push(0x90);
1373        bytes.push(0x80);
1374        bytes.push(0x00); // bad fourth byte
1375        let r = parse::<&str>(&bytes);
1376        assert!(r.is_err());
1377    }
1378
1379    #[test]
1380    fn utf8_e0_bad_second_byte() {
1381        let mut bytes = b"\"".to_vec();
1382        bytes.push(0xE0);
1383        bytes.push(0x80); // too low for E0 (must be 0xA0..0xBF)
1384        bytes.push(0x80);
1385        bytes.push(b'"');
1386        let r = parse::<&str>(&bytes);
1387        assert_eq!(r.unwrap_err().kind, ErrorKind::InvalidUtf8);
1388    }
1389
1390    #[test]
1391    fn utf8_ed_bad_second_byte() {
1392        let mut bytes = b"\"".to_vec();
1393        bytes.push(0xED);
1394        bytes.push(0xA0); // too high for ED (must be 0x80..0x9F) — surrogate range
1395        bytes.push(0x80);
1396        bytes.push(b'"');
1397        let r = parse::<&str>(&bytes);
1398        assert_eq!(r.unwrap_err().kind, ErrorKind::InvalidUtf8);
1399    }
1400
1401    #[test]
1402    fn utf8_f0_bad_second_byte() {
1403        let mut bytes = b"\"".to_vec();
1404        bytes.push(0xF0);
1405        bytes.push(0x80); // too low for F0 (must be 0x90..0xBF)
1406        bytes.push(0x80);
1407        bytes.push(0x80);
1408        bytes.push(b'"');
1409        let r = parse::<&str>(&bytes);
1410        assert_eq!(r.unwrap_err().kind, ErrorKind::InvalidUtf8);
1411    }
1412
1413    // -----------------------------------------------------------------
1414    // Coverage: [T; N]::from_lex — line 295 too-long path
1415    // -----------------------------------------------------------------
1416
1417    #[test]
1418    fn fixed_array_non_array_input() {
1419        let r = parse_str::<[i32; 2]>("42");
1420        assert!(r.is_err());
1421    }
1422}
1423
1424/// Round-trip tests for [`crate::ToJson`] primitives. Pairs each impl
1425/// against the existing `FromJson` impl: serialize a value, parse it
1426/// back, assert equality. This is the contract that the two sides agree
1427/// on the wire format — if a primitive's encoding ever drifts, one of
1428/// these tests breaks before any user code does.
1429#[cfg(all(test, feature = "std"))]
1430mod ser_roundtrip {
1431    use super::{parse_str, to_string};
1432
1433    /// Test helper: takes the value by value so call sites can pass
1434    /// `rt(0_i64)` instead of `rt(&0_i64)`. The lint warning that this
1435    /// "could take &T" is correct in the abstract but ergonomic loss
1436    /// outweighs the (zero-cost) copy for `Copy` primitives, and the
1437    /// owned `String` test variants are small.
1438    #[allow(clippy::needless_pass_by_value)]
1439    fn rt<T>(v: T)
1440    where
1441        T: crate::ToJson + for<'a> crate::FromJson<'a> + PartialEq + core::fmt::Debug,
1442    {
1443        let s = to_string(&v).expect("serialize");
1444        let v2: T = parse_str(&s).expect("parse back");
1445        assert_eq!(v, v2, "round-trip diverged via {s:?}");
1446    }
1447
1448    #[test]
1449    fn bool_roundtrips() {
1450        rt(true);
1451        rt(false);
1452    }
1453
1454    #[test]
1455    fn unit_roundtrips() {
1456        rt(());
1457    }
1458
1459    #[test]
1460    fn signed_ints_roundtrip() {
1461        rt(0_i8);
1462        rt(i8::MIN);
1463        rt(i8::MAX);
1464        rt(0_i16);
1465        rt(i16::MIN);
1466        rt(i16::MAX);
1467        rt(0_i32);
1468        rt(i32::MIN);
1469        rt(i32::MAX);
1470        rt(0_i64);
1471        rt(i64::MIN);
1472        rt(i64::MAX);
1473        rt(0_isize);
1474        rt(isize::MIN);
1475        rt(isize::MAX);
1476    }
1477
1478    #[test]
1479    fn unsigned_ints_roundtrip() {
1480        rt(0_u8);
1481        rt(u8::MAX);
1482        rt(0_u16);
1483        rt(u16::MAX);
1484        rt(0_u32);
1485        rt(u32::MAX);
1486        rt(0_u64);
1487        rt(u64::MAX);
1488        rt(0_usize);
1489        rt(usize::MAX);
1490    }
1491
1492    #[test]
1493    fn wide_ints_roundtrip() {
1494        rt(0_i128);
1495        rt(i128::MIN);
1496        rt(i128::MAX);
1497        rt(0_u128);
1498        rt(u128::MAX);
1499    }
1500
1501    #[test]
1502    fn strings_roundtrip() {
1503        // Plain ASCII, escapes, control bytes, non-ASCII UTF-8.
1504        for s in [
1505            "",
1506            "hello",
1507            "a\\b",
1508            "with \"quotes\"",
1509            "tab\tnewline\nreturn\rbs\x08ff\x0cnul\x00ctl\x1f",
1510            "café 中文 😀",
1511        ] {
1512            rt(String::from(s));
1513        }
1514    }
1515
1516    /// Cow can't go through the generic `rt` helper — `FromJson<'a>` for
1517    /// `Cow<'a, str>` ties the output lifetime to the input buffer, which
1518    /// the HRTB the helper requires can't satisfy. Test the wire shape
1519    /// inline instead.
1520    #[test]
1521    fn cow_roundtrips() {
1522        use std::borrow::Cow;
1523        for src in ["owned", "with\nescape", ""] {
1524            let v = Cow::<str>::Owned(String::from(src));
1525            let s = to_string(&v).unwrap();
1526            let back: Cow<'_, str> = parse_str(&s).unwrap();
1527            assert_eq!(back, src);
1528        }
1529    }
1530
1531    #[test]
1532    fn char_roundtrips() {
1533        for c in ['a', 'Z', '中', '😀', '\n', '\t', '"', '\\'] {
1534            rt(c);
1535        }
1536    }
1537
1538    #[test]
1539    fn option_roundtrips() {
1540        rt(Option::<u32>::None);
1541        rt(Some(42_u32));
1542        rt(Option::<String>::None);
1543        rt(Some(String::from("x")));
1544    }
1545
1546    /// Reference impls forward through the inner value — verify the
1547    /// blanket `&T` impl produces the same bytes as the owned form.
1548    #[test]
1549    fn reference_forwarding() {
1550        let v: u32 = 7;
1551        let owned = to_string(&v).unwrap();
1552        let by_ref = to_string(&&v).unwrap();
1553        assert_eq!(owned, by_ref);
1554    }
1555
1556    /// Wrapper types are transparent — round-trip through the wrapper
1557    /// must produce the same bytes as the inner value.
1558    #[test]
1559    fn wrapper_types_transparent() {
1560        use std::rc::Rc;
1561        use std::sync::Arc;
1562        let v: u32 = 9;
1563        assert_eq!(to_string(&v).unwrap(), to_string(&Box::new(v)).unwrap());
1564        assert_eq!(to_string(&v).unwrap(), to_string(&Rc::new(v)).unwrap());
1565        assert_eq!(to_string(&v).unwrap(), to_string(&Arc::new(v)).unwrap());
1566    }
1567
1568    /// Pin the wire shape of a few primitives so any accidental
1569    /// formatter change (digit grouping, capitalization, exponent
1570    /// notation) shows up as a diff here, not as a downstream failure.
1571    #[test]
1572    fn pinned_wire_format() {
1573        assert_eq!(to_string(&true).unwrap(), "true");
1574        assert_eq!(to_string(&false).unwrap(), "false");
1575        assert_eq!(to_string(&()).unwrap(), "null");
1576        assert_eq!(to_string(&0_i64).unwrap(), "0");
1577        assert_eq!(to_string(&-1_i64).unwrap(), "-1");
1578        assert_eq!(to_string(&i64::MIN).unwrap(), "-9223372036854775808");
1579        assert_eq!(to_string(&u64::MAX).unwrap(), "18446744073709551615");
1580        assert_eq!(to_string(&"hi").unwrap(), "\"hi\"");
1581        // Control char inside a string → \u00XX.
1582        assert_eq!(to_string(&"\x01").unwrap(), "\"\\u0001\"");
1583    }
1584
1585    #[test]
1586    fn vec_roundtrips() {
1587        rt(Vec::<i32>::new());
1588        rt(vec![1_i32, 2, 3]);
1589        rt(vec![String::from("a"), String::from("b")]);
1590        rt(vec![Some(1_u32), None, Some(3)]);
1591    }
1592
1593    #[test]
1594    fn nested_vec_roundtrips() {
1595        rt(vec![vec![1_i32, 2], vec![3], Vec::new()]);
1596    }
1597
1598    #[test]
1599    fn fixed_array_roundtrips() {
1600        rt([1_i32, 2, 3]);
1601        rt([true, false, true]);
1602        let zero: [i32; 0] = [];
1603        rt(zero);
1604    }
1605
1606    #[test]
1607    fn tuple_roundtrips() {
1608        rt((1_i32, String::from("hi"), true));
1609        rt((1_u8, 2_u16, 3_u32, 4_u64));
1610    }
1611
1612    #[test]
1613    fn slice_pinned() {
1614        // Slice has no FromJson impl (you parse into Vec), so it can't
1615        // round-trip — pin its wire shape directly instead.
1616        let s: &[i32] = &[10, 20, 30];
1617        assert_eq!(to_string(&s).unwrap(), "[10,20,30]");
1618        let empty: &[u8] = &[];
1619        assert_eq!(to_string(&empty).unwrap(), "[]");
1620    }
1621
1622    #[test]
1623    fn btreemap_roundtrips() {
1624        use std::collections::BTreeMap;
1625        let mut m = BTreeMap::new();
1626        m.insert(String::from("a"), 1_i32);
1627        m.insert(String::from("b"), 2);
1628        rt(m);
1629    }
1630
1631    #[test]
1632    fn btreemap_with_escape_in_key() {
1633        use std::collections::BTreeMap;
1634        let mut m = BTreeMap::new();
1635        m.insert(String::from("a\nb"), 1_i32);
1636        rt(m);
1637    }
1638
1639    #[test]
1640    fn btreeset_roundtrips() {
1641        use std::collections::BTreeSet;
1642        let mut s = BTreeSet::new();
1643        s.insert(1_i32);
1644        s.insert(2);
1645        s.insert(3);
1646        rt(s);
1647    }
1648
1649    #[cfg(feature = "std")]
1650    #[test]
1651    fn hashmap_roundtrips() {
1652        // HashMap iteration order isn't stable, so don't rt() — instead
1653        // serialize, parse back into HashMap, and compare maps directly.
1654        use std::collections::HashMap;
1655        let mut m = HashMap::new();
1656        m.insert(String::from("alpha"), 1_i32);
1657        m.insert(String::from("beta"), 2);
1658        let s = to_string(&m).unwrap();
1659        let back: HashMap<String, i32> = parse_str(&s).unwrap();
1660        assert_eq!(back, m);
1661    }
1662
1663    #[cfg(feature = "std")]
1664    #[test]
1665    fn hashset_roundtrips() {
1666        use std::collections::HashSet;
1667        let mut s = HashSet::new();
1668        s.insert(1_i32);
1669        s.insert(2);
1670        s.insert(3);
1671        let json = to_string(&s).unwrap();
1672        let back: HashSet<i32> = parse_str(&json).unwrap();
1673        assert_eq!(back, s);
1674    }
1675
1676    #[cfg(feature = "std")]
1677    #[test]
1678    fn ip_addrs_roundtrip() {
1679        use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
1680        rt(Ipv4Addr::LOCALHOST);
1681        rt(Ipv6Addr::LOCALHOST);
1682        rt(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)));
1683        rt(SocketAddr::from(([127, 0, 0, 1], 8080)));
1684    }
1685
1686    #[cfg(feature = "std")]
1687    #[test]
1688    fn pathbuf_roundtrips() {
1689        use std::path::PathBuf;
1690        rt(PathBuf::from("/etc/hosts"));
1691    }
1692
1693    #[test]
1694    fn empty_collections_pinned() {
1695        use std::collections::BTreeMap;
1696        assert_eq!(to_string(&Vec::<i32>::new()).unwrap(), "[]");
1697        let empty: [i32; 0] = [];
1698        assert_eq!(to_string(&empty).unwrap(), "[]");
1699        let m: BTreeMap<String, i32> = BTreeMap::new();
1700        assert_eq!(to_string(&m).unwrap(), "{}");
1701    }
1702
1703    /// Floats via the production path (`write!`-based today, ryu later).
1704    /// `rt()` works because `f64` parses back losslessly when serialized
1705    /// via shortest-round-trip — that's the contract the `write!` impl
1706    /// inherits from libstd's `Display` (which uses ryu internally).
1707    #[test]
1708    fn finite_f64_roundtrips() {
1709        for v in [
1710            0.0_f64,
1711            -0.0,
1712            1.0,
1713            -1.0,
1714            1.5,
1715            -1.5,
1716            1.5e2,
1717            1.5e-2,
1718            f64::MIN_POSITIVE,
1719            f64::MAX,
1720            f64::MIN,
1721            std::f64::consts::PI,
1722        ] {
1723            rt(v);
1724        }
1725    }
1726
1727    #[test]
1728    fn finite_f32_roundtrips() {
1729        for v in [0.0_f32, 1.5, -1.5, f32::MIN_POSITIVE, f32::MAX] {
1730            rt(v);
1731        }
1732    }
1733
1734    #[test]
1735    fn nonfinite_f64_rejected() {
1736        use crate::ErrorKind;
1737        let r = to_string(&f64::INFINITY);
1738        assert_eq!(r.unwrap_err().kind, ErrorKind::NonFiniteFloat);
1739        let r = to_string(&f64::NEG_INFINITY);
1740        assert_eq!(r.unwrap_err().kind, ErrorKind::NonFiniteFloat);
1741        let r = to_string(&f64::NAN);
1742        assert_eq!(r.unwrap_err().kind, ErrorKind::NonFiniteFloat);
1743    }
1744
1745    #[cfg(feature = "std")]
1746    #[test]
1747    fn duration_roundtrips() {
1748        use std::time::Duration;
1749        rt(Duration::from_secs(0));
1750        rt(Duration::from_millis(1500));
1751        rt(Duration::new(42, 750_000_000));
1752    }
1753}
1754
1755/// `to_json!` macro tests. Named-struct arms (PR 4 first slice).
1756/// Each test pairs a `to_json!`-defined type against a manually-defined
1757/// `from_json!` mirror so the round-trip exercises both macros on
1758/// equivalent shapes.
1759#[cfg(all(test, feature = "std"))]
1760mod to_json_macro_tests {
1761    use super::{parse_str, to_string};
1762
1763    // The simplest possible shape: plain struct, no attrs.
1764    crate::to_json! {
1765        #[derive(Debug, PartialEq)]
1766        struct Plain {
1767            id: u32,
1768            name: String,
1769        }
1770    }
1771
1772    crate::from_json! {
1773        #[derive(Debug, PartialEq)]
1774        struct PlainParse {
1775            id: u32,
1776            name: String,
1777        }
1778    }
1779
1780    #[test]
1781    fn plain_struct_emits_object() {
1782        let v = Plain {
1783            id: 7,
1784            name: String::from("alice"),
1785        };
1786        let s = to_string(&v).unwrap();
1787        // Field order matches declaration order.
1788        assert_eq!(s, r#"{"id":7,"name":"alice"}"#);
1789        // Parse back through the equivalent FromJson type.
1790        let back: PlainParse = parse_str(&s).unwrap();
1791        assert_eq!(
1792            back,
1793            PlainParse {
1794                id: 7,
1795                name: String::from("alice")
1796            }
1797        );
1798    }
1799
1800    crate::to_json! {
1801        #[derive(Debug, PartialEq)]
1802        struct Borrowed<'input> {
1803            tag: &'input str,
1804            count: u32,
1805        }
1806    }
1807
1808    #[test]
1809    fn struct_with_lifetime() {
1810        let v = Borrowed {
1811            tag: "hi",
1812            count: 3,
1813        };
1814        let s = to_string(&v).unwrap();
1815        assert_eq!(s, r#"{"tag":"hi","count":3}"#);
1816    }
1817
1818    // rename, skip, skip_if_none.
1819    crate::to_json! {
1820        #[derive(Debug, PartialEq)]
1821        struct Decorated {
1822            #[bourne(rename = "user-id")]
1823            user_id: u32,
1824            #[bourne(skip)]
1825            cached: u32,
1826            #[bourne(skip_if_none)]
1827            note: Option<String>,
1828            value: u32,
1829        }
1830    }
1831
1832    #[test]
1833    fn rename_emits_new_key() {
1834        let v = Decorated {
1835            user_id: 1,
1836            cached: 99,
1837            note: None,
1838            value: 42,
1839        };
1840        let s = to_string(&v).unwrap();
1841        // user-id renamed; cached omitted; note omitted (None); value present.
1842        assert_eq!(s, r#"{"user-id":1,"value":42}"#);
1843    }
1844
1845    #[test]
1846    fn skip_if_none_emits_when_some() {
1847        let v = Decorated {
1848            user_id: 1,
1849            cached: 0,
1850            note: Some(String::from("hi")),
1851            value: 7,
1852        };
1853        let s = to_string(&v).unwrap();
1854        assert_eq!(s, r#"{"user-id":1,"note":"hi","value":7}"#);
1855    }
1856
1857    // Empty struct edge case.
1858    crate::to_json! {
1859        #[derive(Debug, PartialEq)]
1860        struct Empty {}
1861    }
1862
1863    #[test]
1864    fn empty_struct_emits_empty_object() {
1865        assert_eq!(to_string(&Empty {}).unwrap(), "{}");
1866    }
1867
1868    // String escaping inside emitted values (sanity — should already
1869    // work via the ToJson<String> impl, but the macro shouldn't
1870    // double-escape or corrupt the output).
1871    crate::to_json! {
1872        #[derive(Debug, PartialEq)]
1873        struct WithEscape {
1874            text: String,
1875        }
1876    }
1877
1878    #[test]
1879    fn macro_passes_strings_to_escape_path() {
1880        let v = WithEscape {
1881            text: String::from("a\nb\"c"),
1882        };
1883        let s = to_string(&v).unwrap();
1884        assert_eq!(s, r#"{"text":"a\nb\"c"}"#);
1885    }
1886
1887    // Newtype tuple struct — emits the inner value bare.
1888    crate::to_json! {
1889        #[derive(Debug, PartialEq)]
1890        struct UserId(u64);
1891    }
1892
1893    #[test]
1894    fn newtype_emits_bare_value() {
1895        let v = UserId(42);
1896        assert_eq!(to_string(&v).unwrap(), "42");
1897    }
1898
1899    crate::to_json! {
1900        #[derive(Debug, PartialEq)]
1901        struct BorrowedTag<'input>(&'input str);
1902    }
1903
1904    #[test]
1905    fn newtype_with_lifetime() {
1906        let v = BorrowedTag("hello");
1907        assert_eq!(to_string(&v).unwrap(), r#""hello""#);
1908    }
1909
1910    // Multi-field tuple struct — emits a JSON array.
1911    crate::to_json! {
1912        #[derive(Debug, PartialEq)]
1913        struct Point(i32, i32);
1914    }
1915
1916    #[test]
1917    fn tuple_struct_emits_array() {
1918        let v = Point(3, -7);
1919        assert_eq!(to_string(&v).unwrap(), "[3,-7]");
1920    }
1921
1922    crate::to_json! {
1923        #[derive(Debug, PartialEq)]
1924        struct Triple(i32, String, bool);
1925    }
1926
1927    #[test]
1928    fn three_field_tuple_struct() {
1929        let v = Triple(1, String::from("hi"), true);
1930        assert_eq!(to_string(&v).unwrap(), r#"[1,"hi",true]"#);
1931    }
1932
1933    // Externally-tagged enum — the default encoding.
1934    crate::to_json! {
1935        #[derive(Debug, PartialEq)]
1936        enum Shape {
1937            Circle,
1938            Wrapper(u32),
1939            Pair(u32, String),
1940            Box { w: u32, h: u32 },
1941            #[bourne(rename = "tri")]
1942            Triangle,
1943        }
1944    }
1945
1946    #[test]
1947    fn enum_unit_emits_string() {
1948        assert_eq!(to_string(&Shape::Circle).unwrap(), r#""Circle""#);
1949    }
1950
1951    #[test]
1952    fn enum_renamed_unit() {
1953        assert_eq!(to_string(&Shape::Triangle).unwrap(), r#""tri""#);
1954    }
1955
1956    #[test]
1957    fn enum_newtype_emits_object() {
1958        assert_eq!(to_string(&Shape::Wrapper(7)).unwrap(), r#"{"Wrapper":7}"#);
1959    }
1960
1961    #[test]
1962    fn enum_tuple_emits_object_with_array() {
1963        let v = Shape::Pair(1, String::from("x"));
1964        assert_eq!(to_string(&v).unwrap(), r#"{"Pair":[1,"x"]}"#);
1965    }
1966
1967    #[test]
1968    fn enum_struct_variant_emits_nested_object() {
1969        let v = Shape::Box { w: 10, h: 20 };
1970        assert_eq!(to_string(&v).unwrap(), r#"{"Box":{"w":10,"h":20}}"#);
1971    }
1972
1973    // Internally-tagged enum.
1974    crate::to_json! {
1975        #[bourne(tag = "type")]
1976        #[derive(Debug, PartialEq)]
1977        enum Event {
1978            Heartbeat,
1979            #[bourne(rename = "click")]
1980            Click { x: u32, y: u32 },
1981        }
1982    }
1983
1984    #[test]
1985    fn internal_tag_unit_emits_object_with_tag() {
1986        assert_eq!(
1987            to_string(&Event::Heartbeat).unwrap(),
1988            r#"{"type":"Heartbeat"}"#
1989        );
1990    }
1991
1992    #[test]
1993    fn internal_tag_struct_inlines_fields() {
1994        let v = Event::Click { x: 1, y: 2 };
1995        assert_eq!(to_string(&v).unwrap(), r#"{"type":"click","x":1,"y":2}"#);
1996    }
1997
1998    // Adjacently-tagged enum.
1999    crate::to_json! {
2000        #[bourne(tag = "t", content = "c")]
2001        #[derive(Debug, PartialEq)]
2002        enum Msg {
2003            Ping,
2004            Echo(String),
2005            Pair(u32, u32),
2006            Body { text: String },
2007        }
2008    }
2009
2010    #[test]
2011    fn adjacent_unit_emits_only_tag() {
2012        assert_eq!(to_string(&Msg::Ping).unwrap(), r#"{"t":"Ping"}"#);
2013    }
2014
2015    #[test]
2016    fn adjacent_newtype_emits_content() {
2017        let v = Msg::Echo(String::from("hi"));
2018        assert_eq!(to_string(&v).unwrap(), r#"{"t":"Echo","c":"hi"}"#);
2019    }
2020
2021    #[test]
2022    fn adjacent_tuple_emits_content_array() {
2023        let v = Msg::Pair(1, 2);
2024        assert_eq!(to_string(&v).unwrap(), r#"{"t":"Pair","c":[1,2]}"#);
2025    }
2026
2027    #[test]
2028    fn adjacent_struct_emits_content_object() {
2029        let v = Msg::Body {
2030            text: String::from("ok"),
2031        };
2032        assert_eq!(to_string(&v).unwrap(), r#"{"t":"Body","c":{"text":"ok"}}"#);
2033    }
2034
2035    // Untagged enum.
2036    crate::to_json! {
2037        #[bourne(untagged)]
2038        #[derive(Debug, PartialEq)]
2039        enum Mixed {
2040            Nothing,
2041            One(u32),
2042            Two(u32, u32),
2043            Body { name: String },
2044        }
2045    }
2046
2047    #[test]
2048    fn untagged_unit_emits_null() {
2049        assert_eq!(to_string(&Mixed::Nothing).unwrap(), "null");
2050    }
2051
2052    #[test]
2053    fn untagged_newtype_emits_inner() {
2054        assert_eq!(to_string(&Mixed::One(42)).unwrap(), "42");
2055    }
2056
2057    #[test]
2058    fn untagged_tuple_emits_array() {
2059        assert_eq!(to_string(&Mixed::Two(1, 2)).unwrap(), "[1,2]");
2060    }
2061
2062    #[test]
2063    fn untagged_struct_emits_object() {
2064        let v = Mixed::Body {
2065            name: String::from("x"),
2066        };
2067        assert_eq!(to_string(&v).unwrap(), r#"{"name":"x"}"#);
2068    }
2069}
2070
2071/// Sink-adapter tests: `to_writer` (`io::Write`) and `to_fmt` (`fmt::Write`)
2072/// must produce identical bytes to the canonical `to_string` path.
2073#[cfg(all(test, feature = "std"))]
2074mod sink_adapter_tests {
2075    use super::{to_fmt, to_string};
2076
2077    #[test]
2078    fn fmt_sink_matches_to_string_for_struct() {
2079        let m = vec![("a", 1_i32), ("b", 2)]
2080            .into_iter()
2081            .collect::<std::collections::BTreeMap<_, _>>();
2082        let canonical = to_string(&m).unwrap();
2083        let mut out = String::new();
2084        to_fmt(&m, &mut out).unwrap();
2085        assert_eq!(out, canonical);
2086    }
2087
2088    #[test]
2089    fn fmt_sink_handles_floats_with_grisu3() {
2090        let canonical = to_string(&1.5_f64).unwrap();
2091        let mut out = String::new();
2092        to_fmt(&1.5_f64, &mut out).unwrap();
2093        assert_eq!(out, canonical);
2094    }
2095
2096    #[test]
2097    fn fmt_sink_rejects_non_finite() {
2098        let mut out = String::new();
2099        let err = to_fmt(&f64::INFINITY, &mut out).unwrap_err();
2100        assert_eq!(err.kind, crate::ErrorKind::NonFiniteFloat);
2101    }
2102
2103    #[cfg(feature = "std")]
2104    #[test]
2105    fn io_writer_matches_to_string_for_struct() {
2106        use super::to_writer;
2107        let v = vec![1_i32, 2, 3];
2108        let canonical = to_string(&v).unwrap();
2109        let mut buf = Vec::<u8>::new();
2110        to_writer(&v, &mut buf).unwrap();
2111        assert_eq!(String::from_utf8(buf).unwrap(), canonical);
2112    }
2113
2114    #[cfg(feature = "std")]
2115    #[test]
2116    fn io_writer_handles_floats() {
2117        use super::to_writer;
2118        let canonical = to_string(&-2.7e-5_f64).unwrap();
2119        let mut buf = Vec::<u8>::new();
2120        to_writer(&-2.7e-5_f64, &mut buf).unwrap();
2121        assert_eq!(String::from_utf8(buf).unwrap(), canonical);
2122    }
2123
2124    #[cfg(feature = "std")]
2125    #[test]
2126    fn io_writer_rejects_non_finite() {
2127        use super::to_writer;
2128        let mut buf = Vec::<u8>::new();
2129        let err = to_writer(&f64::NAN, &mut buf).unwrap_err();
2130        assert_eq!(err.kind(), std::io::ErrorKind::InvalidData);
2131    }
2132
2133    #[test]
2134    fn pretty_empty_object_compact() {
2135        use super::to_string_pretty;
2136        let m: std::collections::BTreeMap<String, i32> = std::collections::BTreeMap::new();
2137        assert_eq!(to_string_pretty(&m).unwrap(), "{}");
2138    }
2139
2140    #[test]
2141    fn pretty_empty_array_compact() {
2142        use super::to_string_pretty;
2143        let v: Vec<i32> = Vec::new();
2144        assert_eq!(to_string_pretty(&v).unwrap(), "[]");
2145    }
2146
2147    #[test]
2148    fn pretty_array_indents_two_spaces() {
2149        use super::to_string_pretty;
2150        let v = vec![1_i32, 2, 3];
2151        assert_eq!(to_string_pretty(&v).unwrap(), "[\n  1,\n  2,\n  3\n]");
2152    }
2153
2154    #[test]
2155    fn pretty_object_keys_have_one_space_after_colon() {
2156        use super::to_string_pretty;
2157        let m: std::collections::BTreeMap<&str, i32> = [("a", 1), ("b", 2)].into_iter().collect();
2158        assert_eq!(
2159            to_string_pretty(&m).unwrap(),
2160            "{\n  \"a\": 1,\n  \"b\": 2\n}",
2161        );
2162    }
2163
2164    #[test]
2165    fn pretty_nested_indents_proportionally() {
2166        use super::to_string_pretty;
2167        let v: Vec<Vec<i32>> = vec![vec![1, 2], vec![3]];
2168        assert_eq!(
2169            to_string_pretty(&v).unwrap(),
2170            "[\n  [\n    1,\n    2\n  ],\n  [\n    3\n  ]\n]",
2171        );
2172    }
2173
2174    #[cfg(feature = "indexmap")]
2175    #[test]
2176    fn indexmap_preserves_insertion_order_on_parse() {
2177        use crate::parse_str;
2178        // Distinct, non-alphabetical order so a hash-bucket walk
2179        // would visibly reshuffle. IndexMap must yield the keys in
2180        // the order they appeared in the JSON.
2181        let json = r#"{"zebra":1,"alpha":2,"mango":3}"#;
2182        let m: indexmap::IndexMap<String, i32> = parse_str(json).unwrap();
2183        let keys: Vec<&str> = m.keys().map(String::as_str).collect();
2184        assert_eq!(keys, ["zebra", "alpha", "mango"]);
2185        assert_eq!(m["alpha"], 2);
2186    }
2187
2188    #[cfg(feature = "indexmap")]
2189    #[test]
2190    fn indexmap_round_trips_preserving_order() {
2191        use super::to_string;
2192        use crate::parse_str;
2193        let mut m = indexmap::IndexMap::<String, i32>::new();
2194        m.insert("z".into(), 1);
2195        m.insert("a".into(), 2);
2196        m.insert("m".into(), 3);
2197        let s = to_string(&m).unwrap();
2198        // Wire shape should preserve declaration order.
2199        assert_eq!(s, r#"{"z":1,"a":2,"m":3}"#);
2200        // Round-trip back into IndexMap must keep that order.
2201        let back: indexmap::IndexMap<String, i32> = parse_str(&s).unwrap();
2202        assert_eq!(
2203            back.keys().map(String::as_str).collect::<Vec<_>>(),
2204            ["z", "a", "m"],
2205        );
2206    }
2207
2208    #[cfg(feature = "indexmap")]
2209    #[test]
2210    fn indexmap_rejects_duplicate_keys() {
2211        use crate::parse_str;
2212        let r: Result<indexmap::IndexMap<String, i32>, _> = parse_str(r#"{"a":1,"a":2}"#);
2213        assert_eq!(r.unwrap_err().kind, crate::ErrorKind::DuplicateKey);
2214    }
2215
2216    #[cfg(feature = "indexmap")]
2217    #[test]
2218    fn indexset_round_trips() {
2219        use super::to_string;
2220        use crate::parse_str;
2221        let mut s = indexmap::IndexSet::<i32>::new();
2222        s.insert(3);
2223        s.insert(1);
2224        s.insert(2);
2225        let json = to_string(&s).unwrap();
2226        assert_eq!(json, "[3,1,2]");
2227        let back: indexmap::IndexSet<i32> = parse_str(&json).unwrap();
2228        assert_eq!(back.iter().copied().collect::<Vec<_>>(), [3, 1, 2]);
2229    }
2230
2231    #[test]
2232    fn pretty_round_trips_through_compact_parse() {
2233        use super::{parse_str, to_string_pretty};
2234        let m: std::collections::BTreeMap<String, Vec<i32>> = [
2235            (String::from("a"), vec![1, 2]),
2236            (String::from("b"), vec![3]),
2237        ]
2238        .into_iter()
2239        .collect();
2240        let pretty = to_string_pretty(&m).unwrap();
2241        // Whitespace is irrelevant to the parser; the pretty form must
2242        // re-parse to the same map.
2243        let back: std::collections::BTreeMap<String, Vec<i32>> = parse_str(&pretty).unwrap();
2244        assert_eq!(back, m);
2245    }
2246
2247    #[cfg(feature = "std")]
2248    #[test]
2249    fn io_writer_propagates_underlying_error() {
2250        use super::to_writer;
2251        // A writer that always errors: assert the io::Error reaches us
2252        // unmolested instead of getting flattened to a generic kind.
2253        struct FailingWriter;
2254        impl std::io::Write for FailingWriter {
2255            fn write(&mut self, _: &[u8]) -> std::io::Result<usize> {
2256                Err(std::io::Error::other("disk full"))
2257            }
2258            fn flush(&mut self) -> std::io::Result<()> {
2259                Ok(())
2260            }
2261        }
2262        let mut w = FailingWriter;
2263        let err = to_writer(&"hi", &mut w).unwrap_err();
2264        assert_eq!(err.to_string(), "disk full");
2265    }
2266}
2267
2268/// `json!` combined macro tests. Each type gets both `FromJson` and
2269/// `ToJson` from a single invocation — the struct/enum is emitted once.
2270#[cfg(all(test, feature = "std"))]
2271mod json_macro_tests {
2272    use super::{parse_str, to_string};
2273
2274    crate::json! {
2275        #[derive(Debug, PartialEq)]
2276        struct Plain {
2277            id: u32,
2278            name: String,
2279        }
2280    }
2281
2282    #[test]
2283    fn plain_struct_round_trips() {
2284        let v = Plain {
2285            id: 7,
2286            name: String::from("alice"),
2287        };
2288        let s = to_string(&v).unwrap();
2289        assert_eq!(s, r#"{"id":7,"name":"alice"}"#);
2290        let back: Plain = parse_str(&s).unwrap();
2291        assert_eq!(back, v);
2292    }
2293
2294    crate::json! {
2295        #[derive(Debug, PartialEq)]
2296        struct Borrowed<'input> {
2297            tag: &'input str,
2298            count: u32,
2299        }
2300    }
2301
2302    #[test]
2303    fn struct_with_lifetime_round_trips() {
2304        let json = r#"{"tag":"hi","count":3}"#;
2305        let v: Borrowed<'_> = parse_str(json).unwrap();
2306        assert_eq!(
2307            v,
2308            Borrowed {
2309                tag: "hi",
2310                count: 3
2311            }
2312        );
2313        assert_eq!(to_string(&v).unwrap(), json);
2314    }
2315
2316    crate::json! {
2317        #[derive(Debug, PartialEq)]
2318        struct Decorated {
2319            #[bourne(rename = "user-id")]
2320            user_id: u32,
2321            #[bourne(skip)]
2322            cached: u32,
2323            #[bourne(skip_if_none)]
2324            note: Option<String>,
2325            value: u32,
2326        }
2327    }
2328
2329    #[test]
2330    fn rename_skip_skip_if_none() {
2331        let v = Decorated {
2332            user_id: 1,
2333            cached: 99,
2334            note: None,
2335            value: 42,
2336        };
2337        let s = to_string(&v).unwrap();
2338        assert_eq!(s, r#"{"user-id":1,"value":42}"#);
2339        let back: Decorated = parse_str(&s).unwrap();
2340        assert_eq!(back.user_id, 1);
2341        assert_eq!(back.value, 42);
2342    }
2343
2344    crate::json! {
2345        #[derive(Debug, PartialEq)]
2346        struct UserId(u64);
2347    }
2348
2349    #[test]
2350    fn newtype_round_trips() {
2351        let v = UserId(42);
2352        let s = to_string(&v).unwrap();
2353        assert_eq!(s, "42");
2354        let back: UserId = parse_str(&s).unwrap();
2355        assert_eq!(back, v);
2356    }
2357
2358    crate::json! {
2359        #[derive(Debug, PartialEq)]
2360        struct Pair(i32, i32);
2361    }
2362
2363    #[test]
2364    fn tuple_struct_round_trips() {
2365        let v = Pair(3, -7);
2366        let s = to_string(&v).unwrap();
2367        assert_eq!(s, "[3,-7]");
2368        let back: Pair = parse_str(&s).unwrap();
2369        assert_eq!(back, v);
2370    }
2371
2372    crate::json! {
2373        #[derive(Debug, PartialEq)]
2374        enum Shape {
2375            Circle,
2376            Wrapper(u32),
2377            Pair(u32, String),
2378            Box { w: u32, h: u32 },
2379            #[bourne(rename = "tri")]
2380            Triangle,
2381        }
2382    }
2383
2384    #[test]
2385    fn externally_tagged_enum_round_trips() {
2386        let cases: Vec<(Shape, &str)> = vec![
2387            (Shape::Circle, r#""Circle""#),
2388            (Shape::Wrapper(7), r#"{"Wrapper":7}"#),
2389            (Shape::Pair(1, String::from("x")), r#"{"Pair":[1,"x"]}"#),
2390            (Shape::Box { w: 10, h: 20 }, r#"{"Box":{"w":10,"h":20}}"#),
2391            (Shape::Triangle, r#""tri""#),
2392        ];
2393        for (val, expected) in cases {
2394            let s = to_string(&val).unwrap();
2395            assert_eq!(s, expected);
2396            let back: Shape = parse_str(&s).unwrap();
2397            assert_eq!(back, val);
2398        }
2399    }
2400
2401    crate::json! {
2402        #[bourne(tag = "type")]
2403        #[derive(Debug, PartialEq)]
2404        enum Event {
2405            Heartbeat,
2406            #[bourne(rename = "click")]
2407            Click { x: u32, y: u32 },
2408        }
2409    }
2410
2411    #[test]
2412    fn internally_tagged_enum_round_trips() {
2413        let hb = Event::Heartbeat;
2414        let s = to_string(&hb).unwrap();
2415        assert_eq!(s, r#"{"type":"Heartbeat"}"#);
2416        let back: Event = parse_str(&s).unwrap();
2417        assert_eq!(back, hb);
2418
2419        let click = Event::Click { x: 1, y: 2 };
2420        let s = to_string(&click).unwrap();
2421        assert_eq!(s, r#"{"type":"click","x":1,"y":2}"#);
2422        let back: Event = parse_str(&s).unwrap();
2423        assert_eq!(back, click);
2424    }
2425
2426    crate::json! {
2427        #[bourne(tag = "t", content = "c")]
2428        #[derive(Debug, PartialEq)]
2429        enum Msg {
2430            Ping,
2431            Echo(String),
2432            Pair(u32, u32),
2433            Body { text: String },
2434        }
2435    }
2436
2437    #[test]
2438    fn adjacently_tagged_enum_round_trips() {
2439        let cases: Vec<(Msg, &str)> = vec![
2440            (Msg::Ping, r#"{"t":"Ping"}"#),
2441            (Msg::Echo(String::from("hi")), r#"{"t":"Echo","c":"hi"}"#),
2442            (Msg::Pair(1, 2), r#"{"t":"Pair","c":[1,2]}"#),
2443            (
2444                Msg::Body {
2445                    text: String::from("ok"),
2446                },
2447                r#"{"t":"Body","c":{"text":"ok"}}"#,
2448            ),
2449        ];
2450        for (val, expected) in cases {
2451            let s = to_string(&val).unwrap();
2452            assert_eq!(s, expected);
2453            let back: Msg = parse_str(&s).unwrap();
2454            assert_eq!(back, val);
2455        }
2456    }
2457
2458    crate::json! {
2459        #[bourne(untagged)]
2460        #[derive(Debug, PartialEq)]
2461        enum Mixed {
2462            Nothing,
2463            One(u32),
2464            Two(u32, u32),
2465            Body { name: String },
2466        }
2467    }
2468
2469    crate::json! {
2470        #[derive(Debug, PartialEq, Eq)]
2471        pub struct PubFields {
2472            pub id: u32,
2473            pub(crate) name: String,
2474            value: u32,
2475        }
2476    }
2477
2478    #[test]
2479    fn pub_fields_round_trip() {
2480        let v = PubFields {
2481            id: 1,
2482            name: String::from("hi"),
2483            value: 2,
2484        };
2485        let s = to_string(&v).unwrap();
2486        assert_eq!(s, r#"{"id":1,"name":"hi","value":2}"#);
2487        let back: PubFields = parse_str(&s).unwrap();
2488        assert_eq!(back, v);
2489    }
2490
2491    #[test]
2492    fn untagged_enum_serializes() {
2493        assert_eq!(to_string(&Mixed::Nothing).unwrap(), "null");
2494        assert_eq!(to_string(&Mixed::One(42)).unwrap(), "42");
2495        assert_eq!(to_string(&Mixed::Two(1, 2)).unwrap(), "[1,2]");
2496        assert_eq!(
2497            to_string(&Mixed::Body {
2498                name: String::from("x")
2499            })
2500            .unwrap(),
2501            r#"{"name":"x"}"#,
2502        );
2503    }
2504}
2505
2506/// Unsafe-boundary tests for the public surface — pinned at the safety
2507/// contracts of the `_unchecked` Vec-tail writers in `ByteSink` and the
2508/// `from_utf8_unchecked` site in `decode_escapes`. These are designed to
2509/// run under miri (CI: `MIRIFLAGS=-Zmiri-disable-isolation
2510/// RUSTFLAGS=--cfg bourne_no_simd cargo +nightly miri test -p json-bourne --lib`).
2511/// Each one targets a specific invariant; if a future refactor breaks the
2512/// caller-side capacity reservation or the UTF-8 boundary, miri here trips
2513/// on the precise unsafe before any user code does.
2514#[cfg(all(test, feature = "std"))]
2515mod unsafe_boundary_tests {
2516    use super::*;
2517    use alloc::string::String;
2518    use alloc::vec::Vec;
2519
2520    /// Slice-of-floats ser at the exact-capacity boundary. The slice
2521    /// writer's `reserve_hint` computes
2522    ///   `2 + n * (MAX_SERIALIZED_LEN + 1) = 2 + n * 33`.
2523    /// We pre-reserve exactly that, so the per-element
2524    /// `write_float_f64_taint` (which assumes ≥ 32 bytes headroom)
2525    /// runs against the tightest legal Vec capacity.
2526    #[test]
2527    fn bytesink_slice_floats_exact_capacity() {
2528        for n in [0usize, 1, 2, 3, 7, 16, 17, 32, 33] {
2529            #[allow(clippy::cast_precision_loss)]
2530            let data: Vec<f64> = (0..n).map(|i| i as f64 + 0.5).collect();
2531            let mut out: Vec<u8> = Vec::with_capacity(2 + n * 33);
2532            let mut sink = ByteSink::new(&mut out);
2533            (data.as_slice()).write_json(&mut sink).expect("ser");
2534            let s = core::str::from_utf8(&out).expect("ASCII");
2535            // Sanity: parse back as Vec<f64>.
2536            let back: Vec<f64> = parse_str(s).expect("parse back");
2537            assert_eq!(back.len(), n, "n={n}");
2538            #[allow(clippy::cast_precision_loss, clippy::float_cmp)]
2539            for (i, &v) in back.iter().enumerate() {
2540                assert_eq!(v, i as f64 + 0.5, "n={n} i={i}");
2541            }
2542        }
2543    }
2544
2545    /// Same as above but for `Vec<f32>`. f32 widens to f64 in the writer
2546    /// but uses the same taint path, so the capacity boundary is identical.
2547    #[test]
2548    fn bytesink_slice_f32_exact_capacity() {
2549        for n in [0usize, 1, 2, 16, 17] {
2550            #[allow(clippy::cast_precision_loss)]
2551            let data: Vec<f32> = (0..n).map(|i| i as f32 + 0.25).collect();
2552            let mut out: Vec<u8> = Vec::with_capacity(2 + n * 33);
2553            let mut sink = ByteSink::new(&mut out);
2554            (data.as_slice()).write_json(&mut sink).expect("ser");
2555            let s = core::str::from_utf8(&out).expect("ASCII");
2556            let back: Vec<f32> = parse_str(s).expect("parse back");
2557            assert_eq!(back.len(), n);
2558            #[allow(clippy::cast_precision_loss, clippy::float_cmp)]
2559            for (i, &v) in back.iter().enumerate() {
2560                assert_eq!(v, i as f32 + 0.25, "n={n} i={i}");
2561            }
2562        }
2563    }
2564
2565    /// Slice of f64 containing non-finite values — exercises the taint
2566    /// accumulator path and verifies the slice writer surfaces a
2567    /// `NonFiniteFloat` error rather than emitting garbage bytes.
2568    #[test]
2569    fn bytesink_slice_nonfinite_taint_path() {
2570        // The non-finite path writes substitute bytes into the Vec before
2571        // reporting the error. Miri ensures those substitute writes stay
2572        // within the reserved capacity.
2573        for (name, bad_idx, bad_val) in [
2574            ("inf at 0", 0usize, f64::INFINITY),
2575            ("nan at end", 4usize, f64::NAN),
2576            ("neg_inf middle", 2usize, f64::NEG_INFINITY),
2577        ] {
2578            let mut data: Vec<f64> = (0..5).map(f64::from).collect();
2579            data[bad_idx] = bad_val;
2580            let r = to_string(data.as_slice());
2581            assert!(r.is_err(), "{name}: expected non-finite error");
2582        }
2583    }
2584
2585    /// Slice of i64 — exercises `write_byte_unchecked` for both `[`/`,`/`]`
2586    /// at exact capacity. Integers don't use the float taint path but they
2587    /// do use `write_array_reserved`'s `write_byte_unchecked` for delimiters.
2588    #[test]
2589    fn bytesink_slice_ints_exact_capacity() {
2590        // MAX_SERIALIZED_LEN for i64 = 20 ("-9223372036854775808"), so
2591        // hint = 2 + n * 21.
2592        for n in [0usize, 1, 5, 16, 17] {
2593            #[allow(clippy::cast_possible_wrap)]
2594            let n_i64 = n as i64;
2595            #[allow(clippy::cast_possible_wrap)]
2596            let data: Vec<i64> = (0..n).map(|i| (i as i64) - n_i64 / 2).collect();
2597            let mut out: Vec<u8> = Vec::with_capacity(2 + n * 21);
2598            let mut sink = ByteSink::new(&mut out);
2599            (data.as_slice()).write_json(&mut sink).expect("ser");
2600            let s = core::str::from_utf8(&out).expect("ASCII");
2601            let back: Vec<i64> = parse_str(s).expect("parse back");
2602            assert_eq!(back, data, "n={n}");
2603        }
2604    }
2605
2606    /// `decode_escapes`' literal-byte run uses `from_utf8_unchecked` on
2607    /// the stretch between escapes. The lexer's UTF-8 invariant must hold
2608    /// across multi-byte sequences. Test escapes interleaved with 2/3/4-byte
2609    /// UTF-8 chars so the literal chunk passed to the unsafe spans
2610    /// multibyte data.
2611    #[test]
2612    fn decode_escapes_multibyte_in_literal_chunk() {
2613        // 2-byte (é = c3 a9), 3-byte (€ = e2 82 ac), 4-byte (𝄞 = f0 9d 84 9e)
2614        // chars surrounding `\n` escapes. The literal chunks bracket the
2615        // escape and must contain valid UTF-8.
2616        let cases: &[(&str, &str)] = &[
2617            (r#""café\nfin""#, "café\nfin"),
2618            (r#""price: 5€\tea""#, "price: 5€\tea"),
2619            (r#""note 𝄞\nplayed""#, "note 𝄞\nplayed"),
2620            // Many escapes between multibyte stretches.
2621            (r#""éé\nçç\tüü""#, "éé\nçç\tüü"),
2622            // Escape at start / end with multibyte in middle.
2623            (r#""\n𝄞café\t""#, "\n𝄞café\t"),
2624            // Long literal run before single escape (exercises the SIMD
2625            // scan tail).
2626            (
2627                r#""aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaéé\n""#,
2628                "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaéé\n",
2629            ),
2630        ];
2631        for (json, expected) in cases {
2632            let s: String = parse_str(json).expect("parse");
2633            assert_eq!(s, *expected, "case {json}");
2634        }
2635    }
2636
2637    /// Empty slice / array — `write_array_reserved`'s `split_first` is None,
2638    /// no `write_byte_unchecked` for elements. Just `[` and `]`.
2639    #[test]
2640    fn bytesink_empty_slice_writes_brackets_only() {
2641        let empty: Vec<i64> = Vec::new();
2642        let s = to_string(empty.as_slice()).expect("ser");
2643        assert_eq!(s, "[]");
2644
2645        let empty: Vec<f64> = Vec::new();
2646        let s = to_string(empty.as_slice()).expect("ser");
2647        assert_eq!(s, "[]");
2648    }
2649
2650    /// Single-element slice — `write_array_reserved`'s `split_first` returns
2651    /// `(first, [])` so we skip the inner loop. Verify the byte boundary.
2652    #[test]
2653    fn bytesink_single_element_slice() {
2654        let one = [42_i64];
2655        assert_eq!(to_string(one.as_slice()).unwrap(), "[42]");
2656        let one = [1.5_f64];
2657        assert_eq!(to_string(one.as_slice()).unwrap(), "[1.5]");
2658        let one = [1.5_f32];
2659        assert_eq!(to_string(one.as_slice()).unwrap(), "[1.5]");
2660    }
2661
2662    /// Empty string — `decode_escapes` is called with `raw=&[]`. The
2663    /// `from_utf8_unchecked(&raw[start..i])` slice is empty, which is
2664    /// a degenerate edge case that must not OOB.
2665    #[test]
2666    fn decode_escapes_empty_string() {
2667        let s: String = parse_str(r#""""#).expect("parse");
2668        assert_eq!(s, "");
2669    }
2670
2671    /// Pure-ASCII without any escape — the literal-byte run covers the
2672    /// whole input and `find_backslash` returns None. The unsafe slice
2673    /// is `&raw[0..raw.len()]`.
2674    #[test]
2675    fn decode_escapes_no_escapes_pure_ascii() {
2676        // Use `Vec<String>` to force the owned-decode path even though
2677        // the input has no escapes (the `&str` impl would borrow).
2678        let v: Vec<String> = parse_str(r#"["hello world","no escapes here"]"#).expect("parse");
2679        assert_eq!(
2680            v,
2681            [String::from("hello world"), String::from("no escapes here")]
2682        );
2683    }
2684
2685    /// Decode an escape immediately at the start — the first literal-byte
2686    /// run is empty, so `from_utf8_unchecked(&raw[0..0])` is called.
2687    #[test]
2688    fn decode_escapes_escape_at_start() {
2689        let v: Vec<String> = parse_str(r#"["\nhello","\tworld"]"#).expect("parse");
2690        assert_eq!(v, [String::from("\nhello"), String::from("\tworld")]);
2691    }
2692
2693    /// Decode an escape immediately at the end — after the escape there is
2694    /// no literal-byte run.
2695    #[test]
2696    fn decode_escapes_escape_at_end() {
2697        let v: Vec<String> = parse_str(r#"["hello\n","world\t"]"#).expect("parse");
2698        assert_eq!(v, [String::from("hello\n"), String::from("world\t")]);
2699    }
2700
2701    /// Back-to-back escapes — multiple zero-length literal chunks.
2702    #[test]
2703    fn decode_escapes_consecutive_escapes() {
2704        let v: Vec<String> = parse_str(r#"["\n\t\r\\","\"\"\""]"#).expect("parse");
2705        assert_eq!(v, [String::from("\n\t\r\\"), String::from(r#"""""#)]);
2706    }
2707
2708    // -----------------------------------------------------------------
2709    // Vec<T>::vec_from_lex fast-path coverage. The default
2710    // `FromJson::vec_from_lex` impl drives `from_lex` per element; the
2711    // primitive types (`&str`, integers via macros, `Duration`) override
2712    // it to skip the per-element Event detour. These overrides have
2713    // independent code paths from the default and need their own tests.
2714    // -----------------------------------------------------------------
2715
2716    /// `Vec<&str>::vec_from_lex` — borrowed strings, fused fast path.
2717    #[test]
2718    fn vec_borrowed_str_fast_path() {
2719        // Empty array — early-return branch.
2720        let v: Vec<&str> = parse_str("[]").expect("empty");
2721        assert_eq!(v, Vec::<&str>::new());
2722
2723        // Single element — first push only, no while loop.
2724        let v: Vec<&str> = parse_str(r#"["only"]"#).expect("single");
2725        assert_eq!(v, ["only"]);
2726
2727        // Many elements — exercises the while-loop body.
2728        let v: Vec<&str> = parse_str(r#"["a","b","c","d","e"]"#).expect("many");
2729        assert_eq!(v, ["a", "b", "c", "d", "e"]);
2730
2731        // Reject escape-bearing string (the borrowed path requires no escapes).
2732        let r: Result<Vec<&str>, _> = parse_str(r#"["plain","esc\nbad"]"#);
2733        assert!(r.is_err(), "borrowed path must reject escape");
2734    }
2735
2736    /// `Vec<Duration>::vec_from_lex` — fused, with non-finite / negative rejection.
2737    #[cfg(feature = "std")]
2738    #[test]
2739    fn vec_duration_fast_path() {
2740        use std::time::Duration;
2741
2742        // Empty — early return.
2743        let v: Vec<Duration> = parse_str("[]").expect("empty");
2744        assert!(v.is_empty());
2745
2746        // Single — first push.
2747        let v: Vec<Duration> = parse_str("[1.5]").expect("single");
2748        assert_eq!(v, [Duration::from_secs_f64(1.5)]);
2749
2750        // Many — loop body.
2751        let v: Vec<Duration> = parse_str("[0.0,1.0,1.5,2.25,100.125]").expect("many");
2752        assert_eq!(v.len(), 5);
2753        assert_eq!(v[0], Duration::ZERO);
2754        assert_eq!(v[4], Duration::from_secs_f64(100.125));
2755
2756        // Negative — rejected.
2757        let r: Result<Vec<Duration>, _> = parse_str("[1.0,-2.0]");
2758        assert!(r.is_err());
2759    }
2760
2761    /// `Vec<i64>::vec_from_lex` — exercises the macro-generated override
2762    /// in `de.rs::impl_int!`, including the bounds check on the empty branch.
2763    #[test]
2764    fn vec_i64_fast_path() {
2765        let v: Vec<i64> = parse_str("[]").expect("empty");
2766        assert!(v.is_empty());
2767
2768        let v: Vec<i64> = parse_str("[42]").expect("single");
2769        assert_eq!(v, [42]);
2770
2771        let v: Vec<i64> =
2772            parse_str("[1,-1,9223372036854775807,-9223372036854775808]").expect("many");
2773        assert_eq!(v, [1, -1, i64::MAX, i64::MIN]);
2774
2775        // Out-of-range for u32 should error.
2776        let r: Result<Vec<u32>, _> = parse_str("[1,99999999999]");
2777        assert!(r.is_err(), "u32 should overflow");
2778    }
2779
2780    /// `Vec<u128>::vec_from_lex` — wide-int macro path.
2781    #[test]
2782    fn vec_u128_fast_path() {
2783        let v: Vec<u128> = parse_str("[]").expect("empty");
2784        assert!(v.is_empty());
2785
2786        let v: Vec<u128> = parse_str("[0,170141183460469231731687303715884105727]").expect("many");
2787        assert_eq!(v, [0u128, i128::MAX as u128]);
2788    }
2789
2790    // -----------------------------------------------------------------
2791    // `decode_escapes` — full branch coverage. The match arms for `\b`,
2792    // `\f`, `\/`, and the various surrogate-pair error paths weren't
2793    // exercised by the existing tests.
2794    // -----------------------------------------------------------------
2795
2796    #[test]
2797    fn decode_all_simple_escapes() {
2798        // Each backslash escape variant — covers every match arm in
2799        // `decode_escapes`.
2800        let cases: &[(&str, &str)] = &[
2801            (r#""\b""#, "\u{0008}"), // backspace
2802            (r#""\f""#, "\u{000C}"), // form feed
2803            (r#""\/""#, "/"),        // solidus
2804            (r#""\\""#, "\\"),       // backslash
2805            (r#""\"""#, "\""),       // quote
2806            (r#""\n""#, "\n"),
2807            (r#""\r""#, "\r"),
2808            (r#""\t""#, "\t"),
2809        ];
2810        for &(json, expected) in cases {
2811            let s: String = parse_str(json).expect(json);
2812            assert_eq!(s, expected, "case {json}");
2813        }
2814    }
2815
2816    #[test]
2817    fn decode_unknown_escape_errors() {
2818        // `\x` is not a recognized escape — last match arm.
2819        let r: Result<String, _> = parse_str(r#""\x""#);
2820        assert!(r.is_err(), "unknown escape should error");
2821
2822        // Backslash at end-of-input — `i >= raw.len()` branch.
2823        let r: Result<String, _> = parse_str("\"\\\"");
2824        assert!(r.is_err(), "lone trailing backslash should error");
2825    }
2826
2827    #[test]
2828    fn decode_unicode_escape_short_input_errors() {
2829        // `\u` followed by < 4 hex digits — `i + 5 > raw.len()` branch.
2830        let r: Result<String, _> = parse_str(r#""\u00""#);
2831        assert!(r.is_err(), "short \\u should error");
2832        let r: Result<String, _> = parse_str(r#""\u""#);
2833        assert!(r.is_err());
2834    }
2835
2836    #[test]
2837    fn decode_unicode_lone_low_surrogate_errors() {
2838        // `\uDC00` standalone is a lone low surrogate — second
2839        // `0xDC00..=0xDFFF` branch.
2840        let r: Result<String, _> = parse_str(r#""\uDC00""#);
2841        assert!(r.is_err(), "lone low surrogate should error");
2842    }
2843
2844    #[test]
2845    fn decode_unicode_high_surrogate_then_invalid_low_errors() {
2846        // High surrogate \uD800 followed by another `\u` escape whose
2847        // codepoint is OUTSIDE the low-surrogate range — exercises the
2848        // explicit range-check arm (raw[i+1]==`\\` AND raw[i+2]==`u` but
2849        // the parsed low_value isn't a low surrogate).
2850        let json = "\"\\uD800\\u0041\""; // high then 'A' as A
2851        let r: Result<String, _> = parse_str(json);
2852        assert!(
2853            r.is_err(),
2854            "high surrogate then non-low-surrogate \\u must error"
2855        );
2856    }
2857
2858    /// High surrogate followed by `\uXXXX` where XXXX is a valid low
2859    /// surrogate, but the four hex digits are at end-of-input. Covers
2860    /// the `i + 7 > raw.len()` short-buffer guard.
2861    #[test]
2862    fn decode_unicode_high_surrogate_then_short_second_escape_errors() {
2863        let json = r#""\uD800\u""#;
2864        let r: Result<String, _> = parse_str(json);
2865        assert!(r.is_err(), "high surrogate then truncated \\u must error");
2866    }
2867
2868    /// Invalid hex inside the second \u of a surrogate pair.
2869    #[test]
2870    fn decode_unicode_high_surrogate_then_invalid_hex_errors() {
2871        let json = "\"\\uD800\\uZZZZ\"";
2872        let r: Result<String, _> = parse_str(json);
2873        assert!(r.is_err(), "high surrogate then bad hex must error");
2874    }
2875
2876    #[test]
2877    fn decode_unicode_high_surrogate_then_non_u_escape_errors() {
2878        // `\uD800` followed by `\n` (not a `\u` escape).
2879        let r: Result<String, _> = parse_str(r#""\uD800\n""#);
2880        assert!(
2881            r.is_err(),
2882            "high surrogate not followed by \\u should error"
2883        );
2884
2885        // `\uD800` followed by non-backslash byte (EOF or literal).
2886        let r: Result<String, _> = parse_str(r#""\uD800A""#);
2887        assert!(
2888            r.is_err(),
2889            "high surrogate not followed by escape should error"
2890        );
2891    }
2892
2893    #[test]
2894    fn decode_unicode_surrogate_pair_round_trips() {
2895        // Valid surrogate pair for U+1F600 (GRINNING FACE): high=D83D
2896        // low=DE00. Encoded as `\u` escapes (not the literal emoji) so
2897        // decode_escapes' surrogate arm runs — literal multi-byte UTF-8
2898        // goes through the lexer's `consume_utf8_multibyte` instead.
2899        let json = "\"\\uD83D\\uDE00\"";
2900        let s: String = parse_str(json).expect("parse");
2901        assert_eq!(s, "\u{1F600}");
2902
2903        // Surrogate pair adjacent to literal ASCII / other escapes.
2904        let json = "\"a\\uD83D\\uDE00b\\uD83D\\uDE01c\"";
2905        let s: String = parse_str(json).expect("parse");
2906        assert_eq!(s, "a\u{1F600}b\u{1F601}c");
2907    }
2908
2909    #[test]
2910    fn decode_unicode_bmp_escape_round_trips() {
2911        // Standard BMP unicode escape — non-surrogate path. U+00E9 ('é')
2912        // encoded as `é` so decode_escapes' \u arm runs (a literal
2913        // "é" goes through consume_utf8_multibyte instead).
2914        let json = "\"\\u00E9\"";
2915        let s: String = parse_str(json).expect("parse");
2916        assert_eq!(s, "é");
2917
2918        // BMP escapes at various code points.
2919        let json = "\"A\\u00A3\\u20AC\""; // A, £, €
2920        let s: String = parse_str(json).expect("parse");
2921        assert_eq!(s, "A£€");
2922    }
2923
2924    #[test]
2925    fn decode_invalid_hex_in_u_escape_errors() {
2926        // `\u00ZX` — invalid hex.
2927        let r: Result<String, _> = parse_str(r#""\u00ZX""#);
2928        assert!(r.is_err(), "invalid hex should error");
2929    }
2930
2931    // -----------------------------------------------------------------
2932    // `to_decimal_uncentred` — the float path for powers of 2 (mantissa
2933    // == 1 << 52). Hit when the IEEE 754 mantissa lands exactly on the
2934    // uncentred boundary. These values are rare in random samples, so
2935    // need explicit tests.
2936    // -----------------------------------------------------------------
2937
2938    /// Powers of 2 hit the uncentred decompose path. Each gets a
2939    /// shortest-roundtrip rendering through `to_decimal_uncentred`.
2940    #[test]
2941    fn to_decimal_uncentred_powers_of_two_round_trip() {
2942        // f64 values where mantissa == 1 << 52 and exponent != EXPONENT_MIN:
2943        // these are 2.0, 4.0, 8.0, 16.0, ..., up to 2^1023.
2944        let cases = [
2945            2.0_f64,
2946            4.0,
2947            8.0,
2948            16.0,
2949            32.0,
2950            64.0,
2951            128.0,
2952            256.0,
2953            512.0,
2954            1024.0,
2955            2.0_f64.powi(20),
2956            2.0_f64.powi(50),
2957            2.0_f64.powi(100),
2958            2.0_f64.powi(500),
2959            2.0_f64.powi(1023), // largest finite power of 2
2960            // Negative powers of 2 — half exponent.
2961            2.0_f64.powi(-1), // 0.5
2962            2.0_f64.powi(-2), // 0.25
2963            2.0_f64.powi(-10),
2964            2.0_f64.powi(-50),
2965            2.0_f64.powi(-100),
2966            2.0_f64.powi(-1000),
2967        ];
2968        for &v in &cases {
2969            let s = to_string(&v).expect("ser");
2970            let back: f64 = parse_str(&s).expect("parse back");
2971            #[allow(clippy::float_cmp)]
2972            {
2973                assert_eq!(back, v, "round-trip for {v:e}: emitted {s:?}");
2974            }
2975        }
2976    }
2977
2978    /// Negative powers of 2 — sign path through `to_decimal_uncentred`.
2979    #[test]
2980    fn to_decimal_uncentred_negative_powers_round_trip() {
2981        for k in [-30, -10, -1, 1, 10, 30, 100, 500] {
2982            let v = -(2.0_f64.powi(k));
2983            let s = to_string(&v).expect("ser");
2984            let back: f64 = parse_str(&s).expect("parse back");
2985            #[allow(clippy::float_cmp)]
2986            {
2987                assert_eq!(back, v, "round-trip for {v:e}: emitted {s:?}");
2988            }
2989        }
2990    }
2991
2992    /// Subnormal f64 — minimum positive denormal, uses uncentred path
2993    /// at a different boundary.
2994    #[test]
2995    fn to_decimal_uncentred_subnormals_round_trip() {
2996        let cases = [
2997            5e-324_f64,        // smallest subnormal
2998            f64::MIN_POSITIVE, // smallest normal
2999            -f64::MIN_POSITIVE,
3000            f64::MAX,
3001            -f64::MAX,
3002        ];
3003        for &v in &cases {
3004            let s = to_string(&v).expect("ser");
3005            let back: f64 = parse_str(&s).expect("parse back");
3006            #[allow(clippy::float_cmp)]
3007            {
3008                assert_eq!(back, v, "round-trip for {v:e}: emitted {s:?}");
3009            }
3010        }
3011    }
3012
3013    // -----------------------------------------------------------------
3014    // Direct-sink coverage. Every public sink type (StringSink,
3015    // PrettyStringSink, ByteSink unchecked methods) needs at least one
3016    // call so the CRAP gate's zero-coverage trigger doesn't fire.
3017    // -----------------------------------------------------------------
3018
3019    /// `StringSink` — the `&mut String` sink. Exercises `write_byte`,
3020    /// `write_str_raw`, and `write_float_f64` directly.
3021    #[test]
3022    fn string_sink_writes_directly() {
3023        let mut out = String::new();
3024        let mut sink = StringSink::new(&mut out);
3025        // write_byte and write_str_raw via the trait
3026        sink.write_byte(b'[').unwrap();
3027        sink.write_str_raw("1").unwrap();
3028        sink.write_byte(b',').unwrap();
3029        // write_float_f64 (StringSink override)
3030        sink.write_float_f64(2.5).unwrap();
3031        sink.write_byte(b']').unwrap();
3032        assert_eq!(out, "[1,2.5]");
3033
3034        // Non-finite rejection through the StringSink path.
3035        let mut out = String::new();
3036        let mut sink = StringSink::new(&mut out);
3037        assert!(sink.write_float_f64(f64::NAN).is_err());
3038    }
3039
3040    /// `PrettyStringSink::with_indent` — custom indent variant.
3041    /// Existing pretty tests only cover the default-indent constructor.
3042    #[test]
3043    fn pretty_sink_with_indent_uses_custom_indent() {
3044        let mut out = String::new();
3045        let mut sink = PrettyStringSink::with_indent(&mut out, "\t");
3046        let data = (1_i32, 2_i32, 3_i32);
3047        data.write_json(&mut sink).unwrap();
3048        // Tab indent → contains a `\n\t` sequence.
3049        assert!(out.contains("\n\t"), "got: {out:?}");
3050    }
3051
3052    /// `PrettyStringSink::write_float_f64` — covers the pretty-sink
3053    /// float arm (existing tests use the simple-byte and string paths).
3054    #[test]
3055    fn pretty_sink_handles_floats() {
3056        let v: alloc::vec::Vec<f64> = alloc::vec![1.5, 2.5, 3.0];
3057        let s = to_string_pretty(v.as_slice()).unwrap();
3058        assert!(s.contains("1.5"));
3059        assert!(s.contains("3.0"));
3060    }
3061
3062    /// `ByteSink::write_float_f64_unchecked` — non-finite triggers the
3063    /// outlined `cold_nonfinite_byte_sink` error. Exercises the cold path.
3064    #[test]
3065    fn bytesink_unchecked_float_nonfinite_returns_error() {
3066        let mut out: Vec<u8> = Vec::with_capacity(64);
3067        let mut sink = ByteSink::new(&mut out);
3068        // SAFETY: ample reserved capacity (≥32 bytes).
3069        #[allow(unsafe_code)]
3070        let r = unsafe { sink.write_float_f64_unchecked(f64::INFINITY) };
3071        assert!(r.is_err(), "non-finite must error through cold arm");
3072    }
3073
3074    /// `ByteSink::write_float_f64_unchecked_finite` — direct call with
3075    /// a finite value. The slice fast path calls this internally.
3076    #[test]
3077    fn bytesink_unchecked_finite_writes_value() {
3078        let mut out: Vec<u8> = Vec::with_capacity(64);
3079        let mut sink = ByteSink::new(&mut out);
3080        // Pick a finite value that round-trips exactly through the formatter
3081        // and `f64::parse`. 2.5 is exact in binary; avoiding 3.14 also dodges
3082        // clippy's approx_constant warning about PI.
3083        let value = 2.5_f64;
3084        // SAFETY: reserved 64 bytes, value is finite.
3085        #[allow(unsafe_code)]
3086        unsafe {
3087            sink.write_float_f64_unchecked_finite(value).unwrap();
3088        }
3089        let s = core::str::from_utf8(&out).unwrap();
3090        let parsed: f64 = s.parse().unwrap();
3091        // Bit-pattern compare: write→parse must round-trip exactly.
3092        assert_eq!(parsed.to_bits(), value.to_bits());
3093    }
3094
3095    /// `ToJson for Path` (std-only) — covers `Path::write_json`.
3096    #[cfg(feature = "std")]
3097    #[test]
3098    fn path_serializes_via_to_string() {
3099        use std::path::Path;
3100        let p = Path::new("/tmp/file.txt");
3101        let s = to_string(p).unwrap();
3102        assert_eq!(s, "\"/tmp/file.txt\"");
3103    }
3104
3105    /// `MapKeyOut for Cow<'_, str>` — used when a HashMap/BTreeMap
3106    /// key type is `Cow<'_, str>`. Exercises `Cow::as_str`.
3107    #[test]
3108    fn map_with_cow_str_keys_serializes() {
3109        use alloc::borrow::Cow;
3110        use alloc::collections::BTreeMap;
3111        let mut m: BTreeMap<Cow<'_, str>, i32> = BTreeMap::new();
3112        m.insert(Cow::Borrowed("a"), 1);
3113        m.insert(Cow::Owned(String::from("b")), 2);
3114        let s = to_string(&m).unwrap();
3115        assert!(s.contains(r#""a":1"#));
3116        assert!(s.contains(r#""b":2"#));
3117    }
3118
3119    /// `f64::pre_validate_slice` — the post-taint stub returns Ok(()).
3120    /// Direct call to ensure the function is exercised.
3121    #[test]
3122    fn f64_pre_validate_slice_is_noop() {
3123        let slice: &[f64] = &[1.0, 2.0, f64::INFINITY];
3124        // The fn body just returns Ok; calling it through the trait
3125        // exercises both the dispatch and the body.
3126        let r = <f64 as ToJson>::pre_validate_slice(slice);
3127        assert!(r.is_ok());
3128    }
3129
3130    /// `crate::ser::float::format_f64_write` and `reject_non_finite`
3131    /// are exercised by `StringSink::write_float_f64` (via the `float::`
3132    /// module). Existing tests cover that, but cover them explicitly
3133    /// for the non-finite branch through the public `to_fmt` entry
3134    /// point which uses `FmtWriteSink` (a different code path).
3135    #[test]
3136    fn to_fmt_writes_finite_and_rejects_nonfinite() {
3137        let mut s = String::new();
3138        to_fmt(&1.5_f64, &mut s).unwrap();
3139        assert_eq!(s, "1.5");
3140
3141        let mut s = String::new();
3142        assert!(to_fmt(&f64::INFINITY, &mut s).is_err());
3143    }
3144
3145    /// `fmt_write_error` is invoked when the underlying `core::fmt::Write`
3146    /// impl returns an error. Construct a writer that always fails and
3147    /// verify `to_fmt` propagates a typed `Error` (rather than the
3148    /// detail-less `fmt::Error`).
3149    #[test]
3150    fn to_fmt_propagates_underlying_write_failure() {
3151        use core::fmt;
3152
3153        /// Writer that errors on every call — drives `fmt_write_error`.
3154        struct AlwaysFail;
3155        impl fmt::Write for AlwaysFail {
3156            fn write_str(&mut self, _s: &str) -> fmt::Result {
3157                Err(fmt::Error)
3158            }
3159        }
3160
3161        // Any non-empty serializable value flushes at least one byte
3162        // through `write_str` / `write_char`, which the writer rejects.
3163        let mut sink = AlwaysFail;
3164        let r = to_fmt(&42_i64, &mut sink);
3165        assert!(r.is_err(), "expected propagated error from failing writer");
3166
3167        // Also exercise the float path through FmtWriteSink, which uses
3168        // a separate `fmt_write_error()` call site.
3169        let mut sink = AlwaysFail;
3170        let r = to_fmt(&1.5_f64, &mut sink);
3171        assert!(r.is_err());
3172    }
3173}