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 error::{Error, ErrorKind, LineColumn, Position};
120pub use event::{Event, JsonNum, JsonStr, MAX_INPUT_LEN};
121pub use lexer::{Checkpoint, DEFAULT_MAX_DEPTH, Lexer, ValueKind};
122pub use parser::Parser;
123pub use de::{FromJson, parse, parse_str};
124#[cfg(feature = "alloc")]
125pub use de::{MapKey, key_to_cow};
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!(p.next_event().unwrap().unwrap(), Event::StartObject));
665        let k1 = p.object_first_key_lex().unwrap().unwrap();
666        assert!(k1.has_escapes());
667        assert_eq!(p.parse_i64_value().unwrap(), 1);
668        let k2 = p.object_next_key_lex().unwrap().unwrap();
669        assert!(!k2.has_escapes());
670        assert_eq!(p.parse_i64_value().unwrap(), 2);
671        assert!(p.object_next_key_lex().unwrap().is_none());
672        assert!(p.next_event().unwrap().is_none());
673    }
674
675    #[test]
676    fn parser_object_key_lex_empty() {
677        let mut p: Parser<'_> = Parser::new(b"{}");
678        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartObject));
679        assert!(p.object_first_key_lex().unwrap().is_none());
680        assert!(p.next_event().unwrap().is_none());
681    }
682
683    // -----------------------------------------------------------------
684    // Coverage: Parser::array_start / array_continue
685    // -----------------------------------------------------------------
686
687    #[test]
688    fn parser_array_start_continue() {
689        let mut p: Parser<'_> = Parser::new(b"[1,2,3]");
690        assert!(!p.array_start().unwrap());
691        assert_eq!(p.parse_i64_value().unwrap(), 1);
692        assert!(!p.array_continue(b']').unwrap());
693        assert_eq!(p.parse_i64_value().unwrap(), 2);
694        assert!(!p.array_continue(b']').unwrap());
695        assert_eq!(p.parse_i64_value().unwrap(), 3);
696        assert!(p.array_continue(b']').unwrap());
697        assert!(p.next_event().unwrap().is_none());
698    }
699
700    #[test]
701    fn parser_array_start_empty() {
702        let mut p: Parser<'_> = Parser::new(b"[]");
703        assert!(p.array_start().unwrap());
704        assert!(p.next_event().unwrap().is_none());
705    }
706
707    #[test]
708    fn parser_array_nested_in_object() {
709        let mut p: Parser<'_> = Parser::new(br#"{"v":[1,2]}"#);
710        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartObject));
711        let key = p.object_first_key().unwrap().unwrap();
712        assert_eq!(key, "v");
713        assert!(!p.array_start().unwrap());
714        assert_eq!(p.parse_i64_value().unwrap(), 1);
715        assert!(!p.array_continue(b']').unwrap());
716        assert_eq!(p.parse_i64_value().unwrap(), 2);
717        assert!(p.array_continue(b']').unwrap());
718        assert!(p.object_next_key().unwrap().is_none());
719        assert!(p.next_event().unwrap().is_none());
720    }
721
722    // -----------------------------------------------------------------
723    // Coverage: ErrorKind::static_msg
724    // -----------------------------------------------------------------
725
726    #[test]
727    fn error_kind_display_covers_all_variants() {
728        use alloc::format;
729        let cases: &[(ErrorKind, &str)] = &[
730            (ErrorKind::UnexpectedEof, "unexpected end of input"),
731            (ErrorKind::InvalidEscape, "invalid string escape"),
732            (ErrorKind::InvalidUnicodeEscape, "invalid \\u escape"),
733            (ErrorKind::UnpairedSurrogate, "unpaired UTF-16 surrogate in \\u escape"),
734            (ErrorKind::InvalidUtf8, "invalid UTF-8"),
735            (ErrorKind::InvalidNumber, "invalid number literal"),
736            (ErrorKind::NumberOutOfRange, "number does not fit target type"),
737            (ErrorKind::ControlCharInString, "control character in string literal"),
738            (ErrorKind::TrailingData, "trailing data after JSON value"),
739            (ErrorKind::DepthLimitExceeded, "nesting depth limit exceeded"),
740            (ErrorKind::ExpectedValue, "expected JSON value"),
741            (ErrorKind::ExpectedString, "expected string"),
742            (ErrorKind::ExpectedNumber, "expected number"),
743            (ErrorKind::ExpectedBool, "expected boolean"),
744            (ErrorKind::ExpectedNull, "expected null"),
745            (ErrorKind::ExpectedArray, "expected array"),
746            (ErrorKind::ExpectedObject, "expected object"),
747            (ErrorKind::TypeMismatch, "type mismatch"),
748            (ErrorKind::DuplicateKey, "duplicate object key"),
749            (ErrorKind::MissingField, "missing required field"),
750            (ErrorKind::UnknownField, "unknown field"),
751            (ErrorKind::NonFiniteFloat, "non-finite float not representable in JSON"),
752            (ErrorKind::UnexpectedByte(0x7B), "unexpected byte 0x7b"),
753        ];
754        for (kind, expected) in cases {
755            assert_eq!(format!("{kind}"), *expected, "mismatch for {kind:?}");
756        }
757    }
758
759    // -----------------------------------------------------------------
760    // Coverage: Lexer::consume_utf8_multibyte — all leading-byte ranges
761    // -----------------------------------------------------------------
762
763    #[test]
764    fn utf8_f4_leading_byte_valid() {
765        let s = "\"\u{100000}\"";
766        let v: &str = parse_str(s).unwrap();
767        assert_eq!(v, "\u{100000}");
768    }
769
770    #[test]
771    fn utf8_f4_leading_byte_invalid_continuation() {
772        let mut bytes = b"\"".to_vec();
773        bytes.push(0xF4);
774        bytes.push(0x90); // too high for F4 (must be 0x80..0x8F)
775        bytes.push(0x80);
776        bytes.push(0x80);
777        bytes.push(b'"');
778        let r = parse::<&str>(&bytes);
779        assert_eq!(r.unwrap_err().kind, ErrorKind::InvalidUtf8);
780    }
781
782    #[test]
783    fn utf8_multibyte_all_leading_ranges() {
784        let cases: &[&str] = &[
785            "\"\u{0080}\"",   // C2: 2-byte
786            "\"\u{0800}\"",   // E0 A0: 3-byte low
787            "\"\u{1000}\"",   // E1: 3-byte mid
788            "\"\u{D7FF}\"",   // ED 9F: 3-byte just below surrogates
789            "\"\u{E000}\"",   // EE: 3-byte private use
790            "\"\u{10000}\"",  // F0 90: 4-byte low
791            "\"\u{40000}\"",  // F1: 4-byte mid
792            "\"\u{100000}\"", // F4 80: 4-byte high
793        ];
794        for input in cases {
795            let v: &str = parse_str(input).unwrap();
796            let expected = &input[1..input.len() - 1];
797            assert_eq!(v, expected);
798        }
799    }
800
801    // -----------------------------------------------------------------
802    // Coverage: write_escape_byte — all control bytes
803    // -----------------------------------------------------------------
804
805    #[test]
806    fn serialize_all_control_bytes() {
807        for b in 0x00_u8..=0x1F {
808            let s = alloc::string::String::from(b as char);
809            let json = to_string(&s).unwrap();
810            assert!(json.starts_with('"') && json.ends_with('"'));
811            let inner = &json[1..json.len() - 1];
812            match b {
813                b'"' | b'\\' => unreachable!(),
814                b'\n' => assert_eq!(inner, "\\n"),
815                b'\r' => assert_eq!(inner, "\\r"),
816                b'\t' => assert_eq!(inner, "\\t"),
817                0x08 => assert_eq!(inner, "\\b"),
818                0x0C => assert_eq!(inner, "\\f"),
819                _ => {
820                    let expected = alloc::format!("\\u00{b:02x}");
821                    assert_eq!(inner, expected, "byte 0x{b:02x}");
822                }
823            }
824        }
825    }
826
827    // -----------------------------------------------------------------
828    // Coverage: Parser::next_event — error-path branches
829    // -----------------------------------------------------------------
830
831    #[test]
832    fn next_event_rejects_trailing_comma_in_array() {
833        let mut p: Parser<'_> = Parser::new(b"[1,]");
834        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartArray));
835        let _ = p.next_event().unwrap().unwrap(); // Int(1)
836        let err = p.next_event().unwrap_err();
837        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b']'));
838    }
839
840    #[test]
841    fn next_event_rejects_trailing_comma_in_object() {
842        let mut p: Parser<'_> = Parser::new(br#"{"a":1,}"#);
843        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartObject));
844        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Key(_)));
845        let _ = p.next_event().unwrap().unwrap(); // Int(1)
846        let err = p.next_event().unwrap_err();
847        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b'}'));
848    }
849
850    #[test]
851    fn next_event_object_colon_eof() {
852        let mut p: Parser<'_> = Parser::new(br#"{"a""#);
853        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartObject));
854        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Key(_)));
855        let err = p.next_event().unwrap_err();
856        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
857    }
858
859    #[test]
860    fn next_event_object_colon_wrong_byte() {
861        let mut p: Parser<'_> = Parser::new(br#"{"a";"#);
862        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartObject));
863        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Key(_)));
864        let err = p.next_event().unwrap_err();
865        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b';'));
866    }
867
868    #[test]
869    fn next_event_empty_input() {
870        let mut p: Parser<'_> = Parser::new(b"");
871        let err = p.next_event().unwrap_err();
872        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
873    }
874
875    #[test]
876    fn next_event_eof_inside_array() {
877        let mut p: Parser<'_> = Parser::new(b"[");
878        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartArray));
879        let err = p.next_event().unwrap_err();
880        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
881    }
882
883    #[test]
884    fn next_event_eof_after_array_comma() {
885        let mut p: Parser<'_> = Parser::new(b"[1,");
886        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartArray));
887        let _ = p.next_event().unwrap().unwrap(); // 1
888        let err = p.next_event().unwrap_err();
889        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
890    }
891
892    #[test]
893    fn next_event_bad_byte_after_array_value() {
894        let mut p: Parser<'_> = Parser::new(b"[1;");
895        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartArray));
896        let _ = p.next_event().unwrap().unwrap(); // 1
897        let err = p.next_event().unwrap_err();
898        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b';'));
899    }
900
901    #[test]
902    fn next_event_eof_in_object_key_position() {
903        let mut p: Parser<'_> = Parser::new(b"{");
904        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartObject));
905        let err = p.next_event().unwrap_err();
906        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
907    }
908
909    #[test]
910    fn next_event_bad_byte_in_object_key_position() {
911        let mut p: Parser<'_> = Parser::new(b"{1");
912        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartObject));
913        let err = p.next_event().unwrap_err();
914        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b'1'));
915    }
916
917    #[test]
918    fn next_event_eof_after_object_value() {
919        let mut p: Parser<'_> = Parser::new(br#"{"a":1"#);
920        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartObject));
921        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Key(_)));
922        let _ = p.next_event().unwrap().unwrap(); // 1
923        let err = p.next_event().unwrap_err();
924        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
925    }
926
927    #[test]
928    fn next_event_bad_byte_after_object_value() {
929        let mut p: Parser<'_> = Parser::new(br#"{"a":1;"#);
930        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartObject));
931        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Key(_)));
932        let _ = p.next_event().unwrap().unwrap(); // 1
933        let err = p.next_event().unwrap_err();
934        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b';'));
935    }
936
937    #[test]
938    fn next_event_object_value_state_via_fast_path() {
939        let mut p: Parser<'_> = Parser::new(br#"{"x":42,"y":99}"#);
940        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartObject));
941        let k1 = p.object_first_key().unwrap().unwrap();
942        assert_eq!(k1, "x");
943        let v1 = p.next_event().unwrap().unwrap();
944        assert!(matches!(v1, Event::Number(_)));
945        let k2 = p.object_next_key().unwrap().unwrap();
946        assert_eq!(k2, "y");
947        let v2 = p.next_event().unwrap().unwrap();
948        assert!(matches!(v2, Event::Number(_)));
949        assert!(p.object_next_key().unwrap().is_none());
950        assert!(p.next_event().unwrap().is_none());
951    }
952
953    #[test]
954    fn next_event_nested_containers_close_correctly() {
955        let mut p: Parser<'_> = Parser::new(br#"{"a":[1],"b":{"c":2}}"#);
956        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartObject));
957        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Key(_))); // "a"
958        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartArray));
959        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Number(_)));
960        assert!(matches!(p.next_event().unwrap().unwrap(), Event::EndArray));
961        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Key(_))); // "b"
962        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartObject));
963        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Key(_))); // "c"
964        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Number(_)));
965        assert!(matches!(p.next_event().unwrap().unwrap(), Event::EndObject));
966        assert!(matches!(p.next_event().unwrap().unwrap(), Event::EndObject));
967        assert!(p.next_event().unwrap().is_none());
968    }
969
970    #[test]
971    fn next_event_nested_array_in_array() {
972        let input = b"[[],[1,2]]";
973        let mut p: Parser<'_> = Parser::new(input);
974        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartArray));
975        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartArray));
976        assert!(matches!(p.next_event().unwrap().unwrap(), Event::EndArray));
977        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartArray));
978        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Number(_)));
979        assert!(matches!(p.next_event().unwrap().unwrap(), Event::Number(_)));
980        assert!(matches!(p.next_event().unwrap().unwrap(), Event::EndArray));
981        assert!(matches!(p.next_event().unwrap().unwrap(), Event::EndArray));
982        assert!(p.next_event().unwrap().is_none());
983    }
984
985    #[test]
986    fn serialize_string_with_quote_and_backslash() {
987        let s = "a\"b\\c";
988        let json = to_string(&s).unwrap();
989        assert_eq!(json, r#""a\"b\\c""#);
990    }
991
992    #[test]
993    fn serialize_string_with_all_named_escapes() {
994        let s = "\x08\x0C\n\r\t";
995        let json = to_string(&s).unwrap();
996        assert_eq!(json, r#""\b\f\n\r\t""#);
997    }
998
999    // -----------------------------------------------------------------
1000    // Coverage: Parser::object_first_key — nested container state paths
1001    // -----------------------------------------------------------------
1002
1003    #[test]
1004    fn parser_object_first_key_empty_inside_array() {
1005        let mut p: Parser<'_> = Parser::new(b"[{}]");
1006        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartArray));
1007        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartObject));
1008        assert!(p.object_first_key().unwrap().is_none());
1009        assert!(matches!(p.next_event().unwrap().unwrap(), Event::EndArray));
1010        assert!(p.next_event().unwrap().is_none());
1011    }
1012
1013    #[test]
1014    fn parser_object_first_key_empty_inside_object() {
1015        let mut p: Parser<'_> = Parser::new(br#"{"a":{}}"#);
1016        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartObject));
1017        let k = p.object_first_key().unwrap().unwrap();
1018        assert_eq!(k, "a");
1019        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartObject));
1020        assert!(p.object_first_key().unwrap().is_none());
1021        assert!(p.object_next_key().unwrap().is_none());
1022        assert!(p.next_event().unwrap().is_none());
1023    }
1024
1025    #[test]
1026    fn parser_object_first_key_lex_empty_inside_array() {
1027        let mut p: Parser<'_> = Parser::new(b"[{}]");
1028        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartArray));
1029        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartObject));
1030        assert!(p.object_first_key_lex().unwrap().is_none());
1031        assert!(matches!(p.next_event().unwrap().unwrap(), Event::EndArray));
1032        assert!(p.next_event().unwrap().is_none());
1033    }
1034
1035    #[test]
1036    fn parser_object_next_key_close_inside_array() {
1037        let mut p: Parser<'_> = Parser::new(br#"[{"a":1}]"#);
1038        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartArray));
1039        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartObject));
1040        let k = p.object_first_key().unwrap().unwrap();
1041        assert_eq!(k, "a");
1042        assert_eq!(p.parse_i64_value().unwrap(), 1);
1043        assert!(p.object_next_key().unwrap().is_none());
1044        assert!(matches!(p.next_event().unwrap().unwrap(), Event::EndArray));
1045        assert!(p.next_event().unwrap().is_none());
1046    }
1047
1048    #[test]
1049    fn parser_object_next_key_lex_close_inside_array() {
1050        let mut p: Parser<'_> = Parser::new(br#"[{"a":1}]"#);
1051        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartArray));
1052        assert!(matches!(p.next_event().unwrap().unwrap(), Event::StartObject));
1053        let k = p.object_first_key_lex().unwrap().unwrap();
1054        assert!(!k.has_escapes());
1055        assert_eq!(p.parse_i64_value().unwrap(), 1);
1056        assert!(p.object_next_key_lex().unwrap().is_none());
1057        assert!(matches!(p.next_event().unwrap().unwrap(), Event::EndArray));
1058        assert!(p.next_event().unwrap().is_none());
1059    }
1060
1061    // -----------------------------------------------------------------
1062    // Coverage: Lexer object_* — error paths via raw Lexer API
1063    // -----------------------------------------------------------------
1064
1065    #[test]
1066    fn lexer_object_first_key_bad_byte() {
1067        let mut lex: Lexer<'_> = Lexer::new(b"{1");
1068        lex.object_start().unwrap();
1069        let err = lex.object_first_key().unwrap_err();
1070        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b'1'));
1071    }
1072
1073    #[test]
1074    fn lexer_object_first_key_eof() {
1075        let mut lex: Lexer<'_> = Lexer::new(b"{");
1076        lex.object_start().unwrap();
1077        let err = lex.object_first_key().unwrap_err();
1078        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
1079    }
1080
1081    #[test]
1082    fn lexer_object_first_key_lex_bad_byte() {
1083        let mut lex: Lexer<'_> = Lexer::new(b"{1");
1084        lex.object_start().unwrap();
1085        let err = lex.object_first_key_lex().unwrap_err();
1086        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b'1'));
1087    }
1088
1089    #[test]
1090    fn lexer_object_first_key_lex_eof() {
1091        let mut lex: Lexer<'_> = Lexer::new(b"{");
1092        lex.object_start().unwrap();
1093        let err = lex.object_first_key_lex().unwrap_err();
1094        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
1095    }
1096
1097    #[test]
1098    fn lexer_object_next_key_bad_byte() {
1099        let mut lex: Lexer<'_> = Lexer::new(br#"{"a":1;"#);
1100        lex.object_start().unwrap();
1101        let _ = lex.object_first_key().unwrap();
1102        let _ = lex.parse_i64_value().unwrap();
1103        let err = lex.object_next_key().unwrap_err();
1104        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b';'));
1105    }
1106
1107    #[test]
1108    fn lexer_object_next_key_eof() {
1109        let mut lex: Lexer<'_> = Lexer::new(br#"{"a":1"#);
1110        lex.object_start().unwrap();
1111        let _ = lex.object_first_key().unwrap();
1112        let _ = lex.parse_i64_value().unwrap();
1113        let err = lex.object_next_key().unwrap_err();
1114        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
1115    }
1116
1117    #[test]
1118    fn lexer_object_next_key_bad_byte_after_comma() {
1119        let mut lex: Lexer<'_> = Lexer::new(br#"{"a":1,2"#);
1120        lex.object_start().unwrap();
1121        let _ = lex.object_first_key().unwrap();
1122        let _ = lex.parse_i64_value().unwrap();
1123        let err = lex.object_next_key().unwrap_err();
1124        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b'2'));
1125    }
1126
1127    #[test]
1128    fn lexer_object_next_key_lex_bad_byte() {
1129        let mut lex: Lexer<'_> = Lexer::new(br#"{"a":1;"#);
1130        lex.object_start().unwrap();
1131        let _ = lex.object_first_key_lex().unwrap();
1132        let _ = lex.parse_i64_value().unwrap();
1133        let err = lex.object_next_key_lex().unwrap_err();
1134        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b';'));
1135    }
1136
1137    #[test]
1138    fn lexer_object_next_key_lex_eof() {
1139        let mut lex: Lexer<'_> = Lexer::new(br#"{"a":1"#);
1140        lex.object_start().unwrap();
1141        let _ = lex.object_first_key_lex().unwrap();
1142        let _ = lex.parse_i64_value().unwrap();
1143        let err = lex.object_next_key_lex().unwrap_err();
1144        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
1145    }
1146
1147    #[test]
1148    fn lexer_object_next_key_lex_bad_byte_after_comma() {
1149        let mut lex: Lexer<'_> = Lexer::new(br#"{"a":1,2"#);
1150        lex.object_start().unwrap();
1151        let _ = lex.object_first_key_lex().unwrap();
1152        let _ = lex.parse_i64_value().unwrap();
1153        let err = lex.object_next_key_lex().unwrap_err();
1154        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b'2'));
1155    }
1156
1157    #[test]
1158    fn lexer_expect_colon_bad_byte() {
1159        let mut lex: Lexer<'_> = Lexer::new(br#"{"a";"#);
1160        lex.object_start().unwrap();
1161        let err = lex.object_first_key().unwrap_err();
1162        assert_eq!(err.kind, ErrorKind::UnexpectedByte(b';'));
1163    }
1164
1165    #[test]
1166    fn lexer_expect_colon_eof() {
1167        let mut lex: Lexer<'_> = Lexer::new(br#"{"a""#);
1168        lex.object_start().unwrap();
1169        let err = lex.object_first_key().unwrap_err();
1170        assert_eq!(err.kind, ErrorKind::UnexpectedEof);
1171    }
1172
1173    // -----------------------------------------------------------------
1174    // Coverage: [T; N]::from_lex — empty array with N=0, closed early
1175    // -----------------------------------------------------------------
1176
1177    #[test]
1178    fn fixed_array_zero_length() {
1179        let arr: [i32; 0] = parse_str("[]").unwrap();
1180        assert_eq!(arr, [0_i32; 0]);
1181    }
1182
1183    #[test]
1184    fn fixed_array_empty_but_expected_nonempty() {
1185        let r = parse_str::<[i32; 3]>("[]");
1186        assert!(r.is_err());
1187    }
1188
1189    #[test]
1190    fn fixed_array_closed_early() {
1191        let r = parse_str::<[i32; 3]>("[1]");
1192        assert!(r.is_err());
1193    }
1194
1195    // -----------------------------------------------------------------
1196    // Coverage: ErrorKind::static_msg — cross-category variants
1197    // -----------------------------------------------------------------
1198
1199    #[test]
1200    fn static_msg_cross_category() {
1201        use alloc::format;
1202        let kind = ErrorKind::UnexpectedEof;
1203        let msg = format!("{kind}");
1204        assert_eq!(msg, "unexpected end of input");
1205
1206        let kind = ErrorKind::ExpectedNull;
1207        let msg = format!("{kind}");
1208        assert_eq!(msg, "expected null");
1209    }
1210
1211    // -----------------------------------------------------------------
1212    // Coverage: write_escape_byte — quote and backslash arms
1213    // -----------------------------------------------------------------
1214
1215    #[test]
1216    fn serialize_string_containing_only_quote() {
1217        let s = "\"";
1218        let json = to_string(&s).unwrap();
1219        assert_eq!(json, r#""\"""#);
1220    }
1221
1222    #[test]
1223    fn serialize_string_containing_only_backslash() {
1224        let s = "\\";
1225        let json = to_string(&s).unwrap();
1226        assert_eq!(json, r#""\\""#);
1227    }
1228
1229    // -----------------------------------------------------------------
1230    // Coverage: consume_utf8_multibyte — error paths
1231    // -----------------------------------------------------------------
1232
1233    #[test]
1234    fn utf8_invalid_leading_byte() {
1235        let bytes = b"\"\xFF\"";
1236        let r = parse::<&str>(bytes);
1237        assert_eq!(r.unwrap_err().kind, ErrorKind::InvalidUtf8);
1238    }
1239
1240    #[test]
1241    fn utf8_truncated_3byte_sequence() {
1242        let mut bytes = b"\"".to_vec();
1243        bytes.push(0xE1);
1244        bytes.push(0x80);
1245        // missing third continuation byte — EOF
1246        let r = parse::<&str>(&bytes);
1247        assert_eq!(r.unwrap_err().kind, ErrorKind::InvalidUtf8);
1248    }
1249
1250    #[test]
1251    fn utf8_bad_third_continuation_byte() {
1252        let mut bytes = b"\"".to_vec();
1253        bytes.push(0xE1);
1254        bytes.push(0x80);
1255        bytes.push(0x00); // not a continuation byte
1256        let r = parse::<&str>(&bytes);
1257        assert!(r.is_err());
1258    }
1259
1260    #[test]
1261    fn utf8_4byte_bad_third_continuation() {
1262        let mut bytes = b"\"".to_vec();
1263        bytes.push(0xF0);
1264        bytes.push(0x90);
1265        bytes.push(0x80);
1266        bytes.push(0x00); // bad fourth byte
1267        let r = parse::<&str>(&bytes);
1268        assert!(r.is_err());
1269    }
1270
1271    #[test]
1272    fn utf8_e0_bad_second_byte() {
1273        let mut bytes = b"\"".to_vec();
1274        bytes.push(0xE0);
1275        bytes.push(0x80); // too low for E0 (must be 0xA0..0xBF)
1276        bytes.push(0x80);
1277        bytes.push(b'"');
1278        let r = parse::<&str>(&bytes);
1279        assert_eq!(r.unwrap_err().kind, ErrorKind::InvalidUtf8);
1280    }
1281
1282    #[test]
1283    fn utf8_ed_bad_second_byte() {
1284        let mut bytes = b"\"".to_vec();
1285        bytes.push(0xED);
1286        bytes.push(0xA0); // too high for ED (must be 0x80..0x9F) — surrogate range
1287        bytes.push(0x80);
1288        bytes.push(b'"');
1289        let r = parse::<&str>(&bytes);
1290        assert_eq!(r.unwrap_err().kind, ErrorKind::InvalidUtf8);
1291    }
1292
1293    #[test]
1294    fn utf8_f0_bad_second_byte() {
1295        let mut bytes = b"\"".to_vec();
1296        bytes.push(0xF0);
1297        bytes.push(0x80); // too low for F0 (must be 0x90..0xBF)
1298        bytes.push(0x80);
1299        bytes.push(0x80);
1300        bytes.push(b'"');
1301        let r = parse::<&str>(&bytes);
1302        assert_eq!(r.unwrap_err().kind, ErrorKind::InvalidUtf8);
1303    }
1304
1305    // -----------------------------------------------------------------
1306    // Coverage: [T; N]::from_lex — line 295 too-long path
1307    // -----------------------------------------------------------------
1308
1309    #[test]
1310    fn fixed_array_non_array_input() {
1311        let r = parse_str::<[i32; 2]>("42");
1312        assert!(r.is_err());
1313    }
1314}
1315
1316/// Round-trip tests for [`crate::ToJson`] primitives. Pairs each impl
1317/// against the existing `FromJson` impl: serialize a value, parse it
1318/// back, assert equality. This is the contract that the two sides agree
1319/// on the wire format — if a primitive's encoding ever drifts, one of
1320/// these tests breaks before any user code does.
1321#[cfg(all(test, feature = "std"))]
1322mod ser_roundtrip {
1323    use super::{parse_str, to_string};
1324
1325    /// Test helper: takes the value by value so call sites can pass
1326    /// `rt(0_i64)` instead of `rt(&0_i64)`. The lint warning that this
1327    /// "could take &T" is correct in the abstract but ergonomic loss
1328    /// outweighs the (zero-cost) copy for `Copy` primitives, and the
1329    /// owned `String` test variants are small.
1330    #[allow(clippy::needless_pass_by_value)]
1331    fn rt<T>(v: T)
1332    where
1333        T: crate::ToJson + for<'a> crate::FromJson<'a> + PartialEq + core::fmt::Debug,
1334    {
1335        let s = to_string(&v).expect("serialize");
1336        let v2: T = parse_str(&s).expect("parse back");
1337        assert_eq!(v, v2, "round-trip diverged via {s:?}");
1338    }
1339
1340    #[test]
1341    fn bool_roundtrips() {
1342        rt(true);
1343        rt(false);
1344    }
1345
1346    #[test]
1347    fn unit_roundtrips() {
1348        rt(());
1349    }
1350
1351    #[test]
1352    fn signed_ints_roundtrip() {
1353        rt(0_i8);
1354        rt(i8::MIN);
1355        rt(i8::MAX);
1356        rt(0_i16);
1357        rt(i16::MIN);
1358        rt(i16::MAX);
1359        rt(0_i32);
1360        rt(i32::MIN);
1361        rt(i32::MAX);
1362        rt(0_i64);
1363        rt(i64::MIN);
1364        rt(i64::MAX);
1365        rt(0_isize);
1366        rt(isize::MIN);
1367        rt(isize::MAX);
1368    }
1369
1370    #[test]
1371    fn unsigned_ints_roundtrip() {
1372        rt(0_u8);
1373        rt(u8::MAX);
1374        rt(0_u16);
1375        rt(u16::MAX);
1376        rt(0_u32);
1377        rt(u32::MAX);
1378        rt(0_u64);
1379        rt(u64::MAX);
1380        rt(0_usize);
1381        rt(usize::MAX);
1382    }
1383
1384    #[test]
1385    fn wide_ints_roundtrip() {
1386        rt(0_i128);
1387        rt(i128::MIN);
1388        rt(i128::MAX);
1389        rt(0_u128);
1390        rt(u128::MAX);
1391    }
1392
1393    #[test]
1394    fn strings_roundtrip() {
1395        // Plain ASCII, escapes, control bytes, non-ASCII UTF-8.
1396        for s in [
1397            "",
1398            "hello",
1399            "a\\b",
1400            "with \"quotes\"",
1401            "tab\tnewline\nreturn\rbs\x08ff\x0cnul\x00ctl\x1f",
1402            "café 中文 😀",
1403        ] {
1404            rt(String::from(s));
1405        }
1406    }
1407
1408    /// Cow can't go through the generic `rt` helper — `FromJson<'a>` for
1409    /// `Cow<'a, str>` ties the output lifetime to the input buffer, which
1410    /// the HRTB the helper requires can't satisfy. Test the wire shape
1411    /// inline instead.
1412    #[test]
1413    fn cow_roundtrips() {
1414        use std::borrow::Cow;
1415        for src in ["owned", "with\nescape", ""] {
1416            let v = Cow::<str>::Owned(String::from(src));
1417            let s = to_string(&v).unwrap();
1418            let back: Cow<'_, str> = parse_str(&s).unwrap();
1419            assert_eq!(back, src);
1420        }
1421    }
1422
1423    #[test]
1424    fn char_roundtrips() {
1425        for c in ['a', 'Z', '中', '😀', '\n', '\t', '"', '\\'] {
1426            rt(c);
1427        }
1428    }
1429
1430    #[test]
1431    fn option_roundtrips() {
1432        rt(Option::<u32>::None);
1433        rt(Some(42_u32));
1434        rt(Option::<String>::None);
1435        rt(Some(String::from("x")));
1436    }
1437
1438    /// Reference impls forward through the inner value — verify the
1439    /// blanket `&T` impl produces the same bytes as the owned form.
1440    #[test]
1441    fn reference_forwarding() {
1442        let v: u32 = 7;
1443        let owned = to_string(&v).unwrap();
1444        let by_ref = to_string(&&v).unwrap();
1445        assert_eq!(owned, by_ref);
1446    }
1447
1448    /// Wrapper types are transparent — round-trip through the wrapper
1449    /// must produce the same bytes as the inner value.
1450    #[test]
1451    fn wrapper_types_transparent() {
1452        use std::rc::Rc;
1453        use std::sync::Arc;
1454        let v: u32 = 9;
1455        assert_eq!(to_string(&v).unwrap(), to_string(&Box::new(v)).unwrap());
1456        assert_eq!(to_string(&v).unwrap(), to_string(&Rc::new(v)).unwrap());
1457        assert_eq!(to_string(&v).unwrap(), to_string(&Arc::new(v)).unwrap());
1458    }
1459
1460    /// Pin the wire shape of a few primitives so any accidental
1461    /// formatter change (digit grouping, capitalization, exponent
1462    /// notation) shows up as a diff here, not as a downstream failure.
1463    #[test]
1464    fn pinned_wire_format() {
1465        assert_eq!(to_string(&true).unwrap(), "true");
1466        assert_eq!(to_string(&false).unwrap(), "false");
1467        assert_eq!(to_string(&()).unwrap(), "null");
1468        assert_eq!(to_string(&0_i64).unwrap(), "0");
1469        assert_eq!(to_string(&-1_i64).unwrap(), "-1");
1470        assert_eq!(to_string(&i64::MIN).unwrap(), "-9223372036854775808");
1471        assert_eq!(to_string(&u64::MAX).unwrap(), "18446744073709551615");
1472        assert_eq!(to_string(&"hi").unwrap(), "\"hi\"");
1473        // Control char inside a string → \u00XX.
1474        assert_eq!(to_string(&"\x01").unwrap(), "\"\\u0001\"");
1475    }
1476
1477    #[test]
1478    fn vec_roundtrips() {
1479        rt(Vec::<i32>::new());
1480        rt(vec![1_i32, 2, 3]);
1481        rt(vec![String::from("a"), String::from("b")]);
1482        rt(vec![Some(1_u32), None, Some(3)]);
1483    }
1484
1485    #[test]
1486    fn nested_vec_roundtrips() {
1487        rt(vec![vec![1_i32, 2], vec![3], Vec::new()]);
1488    }
1489
1490    #[test]
1491    fn fixed_array_roundtrips() {
1492        rt([1_i32, 2, 3]);
1493        rt([true, false, true]);
1494        let zero: [i32; 0] = [];
1495        rt(zero);
1496    }
1497
1498    #[test]
1499    fn tuple_roundtrips() {
1500        rt((1_i32, String::from("hi"), true));
1501        rt((1_u8, 2_u16, 3_u32, 4_u64));
1502    }
1503
1504    #[test]
1505    fn slice_pinned() {
1506        // Slice has no FromJson impl (you parse into Vec), so it can't
1507        // round-trip — pin its wire shape directly instead.
1508        let s: &[i32] = &[10, 20, 30];
1509        assert_eq!(to_string(&s).unwrap(), "[10,20,30]");
1510        let empty: &[u8] = &[];
1511        assert_eq!(to_string(&empty).unwrap(), "[]");
1512    }
1513
1514    #[test]
1515    fn btreemap_roundtrips() {
1516        use std::collections::BTreeMap;
1517        let mut m = BTreeMap::new();
1518        m.insert(String::from("a"), 1_i32);
1519        m.insert(String::from("b"), 2);
1520        rt(m);
1521    }
1522
1523    #[test]
1524    fn btreemap_with_escape_in_key() {
1525        use std::collections::BTreeMap;
1526        let mut m = BTreeMap::new();
1527        m.insert(String::from("a\nb"), 1_i32);
1528        rt(m);
1529    }
1530
1531    #[test]
1532    fn btreeset_roundtrips() {
1533        use std::collections::BTreeSet;
1534        let mut s = BTreeSet::new();
1535        s.insert(1_i32);
1536        s.insert(2);
1537        s.insert(3);
1538        rt(s);
1539    }
1540
1541    #[cfg(feature = "std")]
1542    #[test]
1543    fn hashmap_roundtrips() {
1544        // HashMap iteration order isn't stable, so don't rt() — instead
1545        // serialize, parse back into HashMap, and compare maps directly.
1546        use std::collections::HashMap;
1547        let mut m = HashMap::new();
1548        m.insert(String::from("alpha"), 1_i32);
1549        m.insert(String::from("beta"), 2);
1550        let s = to_string(&m).unwrap();
1551        let back: HashMap<String, i32> = parse_str(&s).unwrap();
1552        assert_eq!(back, m);
1553    }
1554
1555    #[cfg(feature = "std")]
1556    #[test]
1557    fn hashset_roundtrips() {
1558        use std::collections::HashSet;
1559        let mut s = HashSet::new();
1560        s.insert(1_i32);
1561        s.insert(2);
1562        s.insert(3);
1563        let json = to_string(&s).unwrap();
1564        let back: HashSet<i32> = parse_str(&json).unwrap();
1565        assert_eq!(back, s);
1566    }
1567
1568    #[cfg(feature = "std")]
1569    #[test]
1570    fn ip_addrs_roundtrip() {
1571        use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
1572        rt(Ipv4Addr::LOCALHOST);
1573        rt(Ipv6Addr::LOCALHOST);
1574        rt(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)));
1575        rt(SocketAddr::from(([127, 0, 0, 1], 8080)));
1576    }
1577
1578    #[cfg(feature = "std")]
1579    #[test]
1580    fn pathbuf_roundtrips() {
1581        use std::path::PathBuf;
1582        rt(PathBuf::from("/etc/hosts"));
1583    }
1584
1585    #[test]
1586    fn empty_collections_pinned() {
1587        use std::collections::BTreeMap;
1588        assert_eq!(to_string(&Vec::<i32>::new()).unwrap(), "[]");
1589        let empty: [i32; 0] = [];
1590        assert_eq!(to_string(&empty).unwrap(), "[]");
1591        let m: BTreeMap<String, i32> = BTreeMap::new();
1592        assert_eq!(to_string(&m).unwrap(), "{}");
1593    }
1594
1595    /// Floats via the production path (`write!`-based today, ryu later).
1596    /// `rt()` works because `f64` parses back losslessly when serialized
1597    /// via shortest-round-trip — that's the contract the `write!` impl
1598    /// inherits from libstd's `Display` (which uses ryu internally).
1599    #[test]
1600    fn finite_f64_roundtrips() {
1601        for v in [
1602            0.0_f64,
1603            -0.0,
1604            1.0,
1605            -1.0,
1606            1.5,
1607            -1.5,
1608            1.5e2,
1609            1.5e-2,
1610            f64::MIN_POSITIVE,
1611            f64::MAX,
1612            f64::MIN,
1613            std::f64::consts::PI,
1614        ] {
1615            rt(v);
1616        }
1617    }
1618
1619    #[test]
1620    fn finite_f32_roundtrips() {
1621        for v in [0.0_f32, 1.5, -1.5, f32::MIN_POSITIVE, f32::MAX] {
1622            rt(v);
1623        }
1624    }
1625
1626    #[test]
1627    fn nonfinite_f64_rejected() {
1628        use crate::ErrorKind;
1629        let r = to_string(&f64::INFINITY);
1630        assert_eq!(r.unwrap_err().kind, ErrorKind::NonFiniteFloat);
1631        let r = to_string(&f64::NEG_INFINITY);
1632        assert_eq!(r.unwrap_err().kind, ErrorKind::NonFiniteFloat);
1633        let r = to_string(&f64::NAN);
1634        assert_eq!(r.unwrap_err().kind, ErrorKind::NonFiniteFloat);
1635    }
1636
1637    #[cfg(feature = "std")]
1638    #[test]
1639    fn duration_roundtrips() {
1640        use std::time::Duration;
1641        rt(Duration::from_secs(0));
1642        rt(Duration::from_millis(1500));
1643        rt(Duration::new(42, 750_000_000));
1644    }
1645}
1646
1647/// `to_json!` macro tests. Named-struct arms (PR 4 first slice).
1648/// Each test pairs a `to_json!`-defined type against a manually-defined
1649/// `from_json!` mirror so the round-trip exercises both macros on
1650/// equivalent shapes.
1651#[cfg(all(test, feature = "std"))]
1652mod to_json_macro_tests {
1653    use super::{parse_str, to_string};
1654
1655    // The simplest possible shape: plain struct, no attrs.
1656    crate::to_json! {
1657        #[derive(Debug, PartialEq)]
1658        struct Plain {
1659            id: u32,
1660            name: String,
1661        }
1662    }
1663
1664    crate::from_json! {
1665        #[derive(Debug, PartialEq)]
1666        struct PlainParse {
1667            id: u32,
1668            name: String,
1669        }
1670    }
1671
1672    #[test]
1673    fn plain_struct_emits_object() {
1674        let v = Plain {
1675            id: 7,
1676            name: String::from("alice"),
1677        };
1678        let s = to_string(&v).unwrap();
1679        // Field order matches declaration order.
1680        assert_eq!(s, r#"{"id":7,"name":"alice"}"#);
1681        // Parse back through the equivalent FromJson type.
1682        let back: PlainParse = parse_str(&s).unwrap();
1683        assert_eq!(
1684            back,
1685            PlainParse {
1686                id: 7,
1687                name: String::from("alice")
1688            }
1689        );
1690    }
1691
1692    crate::to_json! {
1693        #[derive(Debug, PartialEq)]
1694        struct Borrowed<'input> {
1695            tag: &'input str,
1696            count: u32,
1697        }
1698    }
1699
1700    #[test]
1701    fn struct_with_lifetime() {
1702        let v = Borrowed {
1703            tag: "hi",
1704            count: 3,
1705        };
1706        let s = to_string(&v).unwrap();
1707        assert_eq!(s, r#"{"tag":"hi","count":3}"#);
1708    }
1709
1710    // rename, skip, skip_if_none.
1711    crate::to_json! {
1712        #[derive(Debug, PartialEq)]
1713        struct Decorated {
1714            #[bourne(rename = "user-id")]
1715            user_id: u32,
1716            #[bourne(skip)]
1717            cached: u32,
1718            #[bourne(skip_if_none)]
1719            note: Option<String>,
1720            value: u32,
1721        }
1722    }
1723
1724    #[test]
1725    fn rename_emits_new_key() {
1726        let v = Decorated {
1727            user_id: 1,
1728            cached: 99,
1729            note: None,
1730            value: 42,
1731        };
1732        let s = to_string(&v).unwrap();
1733        // user-id renamed; cached omitted; note omitted (None); value present.
1734        assert_eq!(s, r#"{"user-id":1,"value":42}"#);
1735    }
1736
1737    #[test]
1738    fn skip_if_none_emits_when_some() {
1739        let v = Decorated {
1740            user_id: 1,
1741            cached: 0,
1742            note: Some(String::from("hi")),
1743            value: 7,
1744        };
1745        let s = to_string(&v).unwrap();
1746        assert_eq!(s, r#"{"user-id":1,"note":"hi","value":7}"#);
1747    }
1748
1749    // Empty struct edge case.
1750    crate::to_json! {
1751        #[derive(Debug, PartialEq)]
1752        struct Empty {}
1753    }
1754
1755    #[test]
1756    fn empty_struct_emits_empty_object() {
1757        assert_eq!(to_string(&Empty {}).unwrap(), "{}");
1758    }
1759
1760    // String escaping inside emitted values (sanity — should already
1761    // work via the ToJson<String> impl, but the macro shouldn't
1762    // double-escape or corrupt the output).
1763    crate::to_json! {
1764        #[derive(Debug, PartialEq)]
1765        struct WithEscape {
1766            text: String,
1767        }
1768    }
1769
1770    #[test]
1771    fn macro_passes_strings_to_escape_path() {
1772        let v = WithEscape {
1773            text: String::from("a\nb\"c"),
1774        };
1775        let s = to_string(&v).unwrap();
1776        assert_eq!(s, r#"{"text":"a\nb\"c"}"#);
1777    }
1778
1779    // Newtype tuple struct — emits the inner value bare.
1780    crate::to_json! {
1781        #[derive(Debug, PartialEq)]
1782        struct UserId(u64);
1783    }
1784
1785    #[test]
1786    fn newtype_emits_bare_value() {
1787        let v = UserId(42);
1788        assert_eq!(to_string(&v).unwrap(), "42");
1789    }
1790
1791    crate::to_json! {
1792        #[derive(Debug, PartialEq)]
1793        struct BorrowedTag<'input>(&'input str);
1794    }
1795
1796    #[test]
1797    fn newtype_with_lifetime() {
1798        let v = BorrowedTag("hello");
1799        assert_eq!(to_string(&v).unwrap(), r#""hello""#);
1800    }
1801
1802    // Multi-field tuple struct — emits a JSON array.
1803    crate::to_json! {
1804        #[derive(Debug, PartialEq)]
1805        struct Point(i32, i32);
1806    }
1807
1808    #[test]
1809    fn tuple_struct_emits_array() {
1810        let v = Point(3, -7);
1811        assert_eq!(to_string(&v).unwrap(), "[3,-7]");
1812    }
1813
1814    crate::to_json! {
1815        #[derive(Debug, PartialEq)]
1816        struct Triple(i32, String, bool);
1817    }
1818
1819    #[test]
1820    fn three_field_tuple_struct() {
1821        let v = Triple(1, String::from("hi"), true);
1822        assert_eq!(to_string(&v).unwrap(), r#"[1,"hi",true]"#);
1823    }
1824
1825    // Externally-tagged enum — the default encoding.
1826    crate::to_json! {
1827        #[derive(Debug, PartialEq)]
1828        enum Shape {
1829            Circle,
1830            Wrapper(u32),
1831            Pair(u32, String),
1832            Box { w: u32, h: u32 },
1833            #[bourne(rename = "tri")]
1834            Triangle,
1835        }
1836    }
1837
1838    #[test]
1839    fn enum_unit_emits_string() {
1840        assert_eq!(to_string(&Shape::Circle).unwrap(), r#""Circle""#);
1841    }
1842
1843    #[test]
1844    fn enum_renamed_unit() {
1845        assert_eq!(to_string(&Shape::Triangle).unwrap(), r#""tri""#);
1846    }
1847
1848    #[test]
1849    fn enum_newtype_emits_object() {
1850        assert_eq!(to_string(&Shape::Wrapper(7)).unwrap(), r#"{"Wrapper":7}"#);
1851    }
1852
1853    #[test]
1854    fn enum_tuple_emits_object_with_array() {
1855        let v = Shape::Pair(1, String::from("x"));
1856        assert_eq!(to_string(&v).unwrap(), r#"{"Pair":[1,"x"]}"#);
1857    }
1858
1859    #[test]
1860    fn enum_struct_variant_emits_nested_object() {
1861        let v = Shape::Box { w: 10, h: 20 };
1862        assert_eq!(to_string(&v).unwrap(), r#"{"Box":{"w":10,"h":20}}"#);
1863    }
1864
1865    // Internally-tagged enum.
1866    crate::to_json! {
1867        #[bourne(tag = "type")]
1868        #[derive(Debug, PartialEq)]
1869        enum Event {
1870            Heartbeat,
1871            #[bourne(rename = "click")]
1872            Click { x: u32, y: u32 },
1873        }
1874    }
1875
1876    #[test]
1877    fn internal_tag_unit_emits_object_with_tag() {
1878        assert_eq!(
1879            to_string(&Event::Heartbeat).unwrap(),
1880            r#"{"type":"Heartbeat"}"#
1881        );
1882    }
1883
1884    #[test]
1885    fn internal_tag_struct_inlines_fields() {
1886        let v = Event::Click { x: 1, y: 2 };
1887        assert_eq!(to_string(&v).unwrap(), r#"{"type":"click","x":1,"y":2}"#);
1888    }
1889
1890    // Adjacently-tagged enum.
1891    crate::to_json! {
1892        #[bourne(tag = "t", content = "c")]
1893        #[derive(Debug, PartialEq)]
1894        enum Msg {
1895            Ping,
1896            Echo(String),
1897            Pair(u32, u32),
1898            Body { text: String },
1899        }
1900    }
1901
1902    #[test]
1903    fn adjacent_unit_emits_only_tag() {
1904        assert_eq!(to_string(&Msg::Ping).unwrap(), r#"{"t":"Ping"}"#);
1905    }
1906
1907    #[test]
1908    fn adjacent_newtype_emits_content() {
1909        let v = Msg::Echo(String::from("hi"));
1910        assert_eq!(to_string(&v).unwrap(), r#"{"t":"Echo","c":"hi"}"#);
1911    }
1912
1913    #[test]
1914    fn adjacent_tuple_emits_content_array() {
1915        let v = Msg::Pair(1, 2);
1916        assert_eq!(to_string(&v).unwrap(), r#"{"t":"Pair","c":[1,2]}"#);
1917    }
1918
1919    #[test]
1920    fn adjacent_struct_emits_content_object() {
1921        let v = Msg::Body {
1922            text: String::from("ok"),
1923        };
1924        assert_eq!(to_string(&v).unwrap(), r#"{"t":"Body","c":{"text":"ok"}}"#);
1925    }
1926
1927    // Untagged enum.
1928    crate::to_json! {
1929        #[bourne(untagged)]
1930        #[derive(Debug, PartialEq)]
1931        enum Mixed {
1932            Nothing,
1933            One(u32),
1934            Two(u32, u32),
1935            Body { name: String },
1936        }
1937    }
1938
1939    #[test]
1940    fn untagged_unit_emits_null() {
1941        assert_eq!(to_string(&Mixed::Nothing).unwrap(), "null");
1942    }
1943
1944    #[test]
1945    fn untagged_newtype_emits_inner() {
1946        assert_eq!(to_string(&Mixed::One(42)).unwrap(), "42");
1947    }
1948
1949    #[test]
1950    fn untagged_tuple_emits_array() {
1951        assert_eq!(to_string(&Mixed::Two(1, 2)).unwrap(), "[1,2]");
1952    }
1953
1954    #[test]
1955    fn untagged_struct_emits_object() {
1956        let v = Mixed::Body {
1957            name: String::from("x"),
1958        };
1959        assert_eq!(to_string(&v).unwrap(), r#"{"name":"x"}"#);
1960    }
1961}
1962
1963/// Sink-adapter tests: `to_writer` (`io::Write`) and `to_fmt` (`fmt::Write`)
1964/// must produce identical bytes to the canonical `to_string` path.
1965#[cfg(all(test, feature = "std"))]
1966mod sink_adapter_tests {
1967    use super::{to_fmt, to_string};
1968
1969    #[test]
1970    fn fmt_sink_matches_to_string_for_struct() {
1971        let m = vec![("a", 1_i32), ("b", 2)]
1972            .into_iter()
1973            .collect::<std::collections::BTreeMap<_, _>>();
1974        let canonical = to_string(&m).unwrap();
1975        let mut out = String::new();
1976        to_fmt(&m, &mut out).unwrap();
1977        assert_eq!(out, canonical);
1978    }
1979
1980    #[test]
1981    fn fmt_sink_handles_floats_with_grisu3() {
1982        let canonical = to_string(&1.5_f64).unwrap();
1983        let mut out = String::new();
1984        to_fmt(&1.5_f64, &mut out).unwrap();
1985        assert_eq!(out, canonical);
1986    }
1987
1988    #[test]
1989    fn fmt_sink_rejects_non_finite() {
1990        let mut out = String::new();
1991        let err = to_fmt(&f64::INFINITY, &mut out).unwrap_err();
1992        assert_eq!(err.kind, crate::ErrorKind::NonFiniteFloat);
1993    }
1994
1995    #[cfg(feature = "std")]
1996    #[test]
1997    fn io_writer_matches_to_string_for_struct() {
1998        use super::to_writer;
1999        let v = vec![1_i32, 2, 3];
2000        let canonical = to_string(&v).unwrap();
2001        let mut buf = Vec::<u8>::new();
2002        to_writer(&v, &mut buf).unwrap();
2003        assert_eq!(String::from_utf8(buf).unwrap(), canonical);
2004    }
2005
2006    #[cfg(feature = "std")]
2007    #[test]
2008    fn io_writer_handles_floats() {
2009        use super::to_writer;
2010        let canonical = to_string(&-2.7e-5_f64).unwrap();
2011        let mut buf = Vec::<u8>::new();
2012        to_writer(&-2.7e-5_f64, &mut buf).unwrap();
2013        assert_eq!(String::from_utf8(buf).unwrap(), canonical);
2014    }
2015
2016    #[cfg(feature = "std")]
2017    #[test]
2018    fn io_writer_rejects_non_finite() {
2019        use super::to_writer;
2020        let mut buf = Vec::<u8>::new();
2021        let err = to_writer(&f64::NAN, &mut buf).unwrap_err();
2022        assert_eq!(err.kind(), std::io::ErrorKind::InvalidData);
2023    }
2024
2025    #[test]
2026    fn pretty_empty_object_compact() {
2027        use super::to_string_pretty;
2028        let m: std::collections::BTreeMap<String, i32> = std::collections::BTreeMap::new();
2029        assert_eq!(to_string_pretty(&m).unwrap(), "{}");
2030    }
2031
2032    #[test]
2033    fn pretty_empty_array_compact() {
2034        use super::to_string_pretty;
2035        let v: Vec<i32> = Vec::new();
2036        assert_eq!(to_string_pretty(&v).unwrap(), "[]");
2037    }
2038
2039    #[test]
2040    fn pretty_array_indents_two_spaces() {
2041        use super::to_string_pretty;
2042        let v = vec![1_i32, 2, 3];
2043        assert_eq!(to_string_pretty(&v).unwrap(), "[\n  1,\n  2,\n  3\n]");
2044    }
2045
2046    #[test]
2047    fn pretty_object_keys_have_one_space_after_colon() {
2048        use super::to_string_pretty;
2049        let m: std::collections::BTreeMap<&str, i32> = [("a", 1), ("b", 2)].into_iter().collect();
2050        assert_eq!(
2051            to_string_pretty(&m).unwrap(),
2052            "{\n  \"a\": 1,\n  \"b\": 2\n}",
2053        );
2054    }
2055
2056    #[test]
2057    fn pretty_nested_indents_proportionally() {
2058        use super::to_string_pretty;
2059        let v: Vec<Vec<i32>> = vec![vec![1, 2], vec![3]];
2060        assert_eq!(
2061            to_string_pretty(&v).unwrap(),
2062            "[\n  [\n    1,\n    2\n  ],\n  [\n    3\n  ]\n]",
2063        );
2064    }
2065
2066    #[cfg(feature = "indexmap")]
2067    #[test]
2068    fn indexmap_preserves_insertion_order_on_parse() {
2069        use crate::parse_str;
2070        // Distinct, non-alphabetical order so a hash-bucket walk
2071        // would visibly reshuffle. IndexMap must yield the keys in
2072        // the order they appeared in the JSON.
2073        let json = r#"{"zebra":1,"alpha":2,"mango":3}"#;
2074        let m: indexmap::IndexMap<String, i32> = parse_str(json).unwrap();
2075        let keys: Vec<&str> = m.keys().map(String::as_str).collect();
2076        assert_eq!(keys, ["zebra", "alpha", "mango"]);
2077        assert_eq!(m["alpha"], 2);
2078    }
2079
2080    #[cfg(feature = "indexmap")]
2081    #[test]
2082    fn indexmap_round_trips_preserving_order() {
2083        use super::to_string;
2084        use crate::parse_str;
2085        let mut m = indexmap::IndexMap::<String, i32>::new();
2086        m.insert("z".into(), 1);
2087        m.insert("a".into(), 2);
2088        m.insert("m".into(), 3);
2089        let s = to_string(&m).unwrap();
2090        // Wire shape should preserve declaration order.
2091        assert_eq!(s, r#"{"z":1,"a":2,"m":3}"#);
2092        // Round-trip back into IndexMap must keep that order.
2093        let back: indexmap::IndexMap<String, i32> = parse_str(&s).unwrap();
2094        assert_eq!(
2095            back.keys().map(String::as_str).collect::<Vec<_>>(),
2096            ["z", "a", "m"],
2097        );
2098    }
2099
2100    #[cfg(feature = "indexmap")]
2101    #[test]
2102    fn indexmap_rejects_duplicate_keys() {
2103        use crate::parse_str;
2104        let r: Result<indexmap::IndexMap<String, i32>, _> = parse_str(r#"{"a":1,"a":2}"#);
2105        assert_eq!(r.unwrap_err().kind, crate::ErrorKind::DuplicateKey);
2106    }
2107
2108    #[cfg(feature = "indexmap")]
2109    #[test]
2110    fn indexset_round_trips() {
2111        use super::to_string;
2112        use crate::parse_str;
2113        let mut s = indexmap::IndexSet::<i32>::new();
2114        s.insert(3);
2115        s.insert(1);
2116        s.insert(2);
2117        let json = to_string(&s).unwrap();
2118        assert_eq!(json, "[3,1,2]");
2119        let back: indexmap::IndexSet<i32> = parse_str(&json).unwrap();
2120        assert_eq!(back.iter().copied().collect::<Vec<_>>(), [3, 1, 2]);
2121    }
2122
2123    #[test]
2124    fn pretty_round_trips_through_compact_parse() {
2125        use super::{parse_str, to_string_pretty};
2126        let m: std::collections::BTreeMap<String, Vec<i32>> = [
2127            (String::from("a"), vec![1, 2]),
2128            (String::from("b"), vec![3]),
2129        ]
2130        .into_iter()
2131        .collect();
2132        let pretty = to_string_pretty(&m).unwrap();
2133        // Whitespace is irrelevant to the parser; the pretty form must
2134        // re-parse to the same map.
2135        let back: std::collections::BTreeMap<String, Vec<i32>> = parse_str(&pretty).unwrap();
2136        assert_eq!(back, m);
2137    }
2138
2139    #[cfg(feature = "std")]
2140    #[test]
2141    fn io_writer_propagates_underlying_error() {
2142        use super::to_writer;
2143        // A writer that always errors: assert the io::Error reaches us
2144        // unmolested instead of getting flattened to a generic kind.
2145        struct FailingWriter;
2146        impl std::io::Write for FailingWriter {
2147            fn write(&mut self, _: &[u8]) -> std::io::Result<usize> {
2148                Err(std::io::Error::other("disk full"))
2149            }
2150            fn flush(&mut self) -> std::io::Result<()> {
2151                Ok(())
2152            }
2153        }
2154        let mut w = FailingWriter;
2155        let err = to_writer(&"hi", &mut w).unwrap_err();
2156        assert_eq!(err.to_string(), "disk full");
2157    }
2158}
2159
2160/// `json!` combined macro tests. Each type gets both `FromJson` and
2161/// `ToJson` from a single invocation — the struct/enum is emitted once.
2162#[cfg(all(test, feature = "std"))]
2163mod json_macro_tests {
2164    use super::{parse_str, to_string};
2165
2166    crate::json! {
2167        #[derive(Debug, PartialEq)]
2168        struct Plain {
2169            id: u32,
2170            name: String,
2171        }
2172    }
2173
2174    #[test]
2175    fn plain_struct_round_trips() {
2176        let v = Plain {
2177            id: 7,
2178            name: String::from("alice"),
2179        };
2180        let s = to_string(&v).unwrap();
2181        assert_eq!(s, r#"{"id":7,"name":"alice"}"#);
2182        let back: Plain = parse_str(&s).unwrap();
2183        assert_eq!(back, v);
2184    }
2185
2186    crate::json! {
2187        #[derive(Debug, PartialEq)]
2188        struct Borrowed<'input> {
2189            tag: &'input str,
2190            count: u32,
2191        }
2192    }
2193
2194    #[test]
2195    fn struct_with_lifetime_round_trips() {
2196        let json = r#"{"tag":"hi","count":3}"#;
2197        let v: Borrowed<'_> = parse_str(json).unwrap();
2198        assert_eq!(
2199            v,
2200            Borrowed {
2201                tag: "hi",
2202                count: 3
2203            }
2204        );
2205        assert_eq!(to_string(&v).unwrap(), json);
2206    }
2207
2208    crate::json! {
2209        #[derive(Debug, PartialEq)]
2210        struct Decorated {
2211            #[bourne(rename = "user-id")]
2212            user_id: u32,
2213            #[bourne(skip)]
2214            cached: u32,
2215            #[bourne(skip_if_none)]
2216            note: Option<String>,
2217            value: u32,
2218        }
2219    }
2220
2221    #[test]
2222    fn rename_skip_skip_if_none() {
2223        let v = Decorated {
2224            user_id: 1,
2225            cached: 99,
2226            note: None,
2227            value: 42,
2228        };
2229        let s = to_string(&v).unwrap();
2230        assert_eq!(s, r#"{"user-id":1,"value":42}"#);
2231        let back: Decorated = parse_str(&s).unwrap();
2232        assert_eq!(back.user_id, 1);
2233        assert_eq!(back.value, 42);
2234    }
2235
2236    crate::json! {
2237        #[derive(Debug, PartialEq)]
2238        struct UserId(u64);
2239    }
2240
2241    #[test]
2242    fn newtype_round_trips() {
2243        let v = UserId(42);
2244        let s = to_string(&v).unwrap();
2245        assert_eq!(s, "42");
2246        let back: UserId = parse_str(&s).unwrap();
2247        assert_eq!(back, v);
2248    }
2249
2250    crate::json! {
2251        #[derive(Debug, PartialEq)]
2252        struct Pair(i32, i32);
2253    }
2254
2255    #[test]
2256    fn tuple_struct_round_trips() {
2257        let v = Pair(3, -7);
2258        let s = to_string(&v).unwrap();
2259        assert_eq!(s, "[3,-7]");
2260        let back: Pair = parse_str(&s).unwrap();
2261        assert_eq!(back, v);
2262    }
2263
2264    crate::json! {
2265        #[derive(Debug, PartialEq)]
2266        enum Shape {
2267            Circle,
2268            Wrapper(u32),
2269            Pair(u32, String),
2270            Box { w: u32, h: u32 },
2271            #[bourne(rename = "tri")]
2272            Triangle,
2273        }
2274    }
2275
2276    #[test]
2277    fn externally_tagged_enum_round_trips() {
2278        let cases: Vec<(Shape, &str)> = vec![
2279            (Shape::Circle, r#""Circle""#),
2280            (Shape::Wrapper(7), r#"{"Wrapper":7}"#),
2281            (Shape::Pair(1, String::from("x")), r#"{"Pair":[1,"x"]}"#),
2282            (Shape::Box { w: 10, h: 20 }, r#"{"Box":{"w":10,"h":20}}"#),
2283            (Shape::Triangle, r#""tri""#),
2284        ];
2285        for (val, expected) in cases {
2286            let s = to_string(&val).unwrap();
2287            assert_eq!(s, expected);
2288            let back: Shape = parse_str(&s).unwrap();
2289            assert_eq!(back, val);
2290        }
2291    }
2292
2293    crate::json! {
2294        #[bourne(tag = "type")]
2295        #[derive(Debug, PartialEq)]
2296        enum Event {
2297            Heartbeat,
2298            #[bourne(rename = "click")]
2299            Click { x: u32, y: u32 },
2300        }
2301    }
2302
2303    #[test]
2304    fn internally_tagged_enum_round_trips() {
2305        let hb = Event::Heartbeat;
2306        let s = to_string(&hb).unwrap();
2307        assert_eq!(s, r#"{"type":"Heartbeat"}"#);
2308        let back: Event = parse_str(&s).unwrap();
2309        assert_eq!(back, hb);
2310
2311        let click = Event::Click { x: 1, y: 2 };
2312        let s = to_string(&click).unwrap();
2313        assert_eq!(s, r#"{"type":"click","x":1,"y":2}"#);
2314        let back: Event = parse_str(&s).unwrap();
2315        assert_eq!(back, click);
2316    }
2317
2318    crate::json! {
2319        #[bourne(tag = "t", content = "c")]
2320        #[derive(Debug, PartialEq)]
2321        enum Msg {
2322            Ping,
2323            Echo(String),
2324            Pair(u32, u32),
2325            Body { text: String },
2326        }
2327    }
2328
2329    #[test]
2330    fn adjacently_tagged_enum_round_trips() {
2331        let cases: Vec<(Msg, &str)> = vec![
2332            (Msg::Ping, r#"{"t":"Ping"}"#),
2333            (Msg::Echo(String::from("hi")), r#"{"t":"Echo","c":"hi"}"#),
2334            (Msg::Pair(1, 2), r#"{"t":"Pair","c":[1,2]}"#),
2335            (
2336                Msg::Body {
2337                    text: String::from("ok"),
2338                },
2339                r#"{"t":"Body","c":{"text":"ok"}}"#,
2340            ),
2341        ];
2342        for (val, expected) in cases {
2343            let s = to_string(&val).unwrap();
2344            assert_eq!(s, expected);
2345            let back: Msg = parse_str(&s).unwrap();
2346            assert_eq!(back, val);
2347        }
2348    }
2349
2350    crate::json! {
2351        #[bourne(untagged)]
2352        #[derive(Debug, PartialEq)]
2353        enum Mixed {
2354            Nothing,
2355            One(u32),
2356            Two(u32, u32),
2357            Body { name: String },
2358        }
2359    }
2360
2361    crate::json! {
2362        #[derive(Debug, PartialEq, Eq)]
2363        pub struct PubFields {
2364            pub id: u32,
2365            pub(crate) name: String,
2366            value: u32,
2367        }
2368    }
2369
2370    #[test]
2371    fn pub_fields_round_trip() {
2372        let v = PubFields {
2373            id: 1,
2374            name: String::from("hi"),
2375            value: 2,
2376        };
2377        let s = to_string(&v).unwrap();
2378        assert_eq!(s, r#"{"id":1,"name":"hi","value":2}"#);
2379        let back: PubFields = parse_str(&s).unwrap();
2380        assert_eq!(back, v);
2381    }
2382
2383    #[test]
2384    fn untagged_enum_serializes() {
2385        assert_eq!(to_string(&Mixed::Nothing).unwrap(), "null");
2386        assert_eq!(to_string(&Mixed::One(42)).unwrap(), "42");
2387        assert_eq!(to_string(&Mixed::Two(1, 2)).unwrap(), "[1,2]");
2388        assert_eq!(
2389            to_string(&Mixed::Body {
2390                name: String::from("x")
2391            })
2392            .unwrap(),
2393            r#"{"name":"x"}"#,
2394        );
2395    }
2396}
2397
2398/// Unsafe-boundary tests for the public surface — pinned at the safety
2399/// contracts of the `_unchecked` Vec-tail writers in `ByteSink` and the
2400/// `from_utf8_unchecked` site in `decode_escapes`. These are designed to
2401/// run under miri (CI: `MIRIFLAGS=-Zmiri-disable-isolation
2402/// RUSTFLAGS=--cfg bourne_no_simd cargo +nightly miri test -p json-bourne --lib`).
2403/// Each one targets a specific invariant; if a future refactor breaks the
2404/// caller-side capacity reservation or the UTF-8 boundary, miri here trips
2405/// on the precise unsafe before any user code does.
2406#[cfg(all(test, feature = "std"))]
2407mod unsafe_boundary_tests {
2408    use super::*;
2409    use alloc::string::String;
2410    use alloc::vec::Vec;
2411
2412    /// Slice-of-floats ser at the exact-capacity boundary. The slice
2413    /// writer's `reserve_hint` computes
2414    ///   `2 + n * (MAX_SERIALIZED_LEN + 1) = 2 + n * 33`.
2415    /// We pre-reserve exactly that, so the per-element
2416    /// `write_float_f64_taint` (which assumes ≥ 32 bytes headroom)
2417    /// runs against the tightest legal Vec capacity.
2418    #[test]
2419    fn bytesink_slice_floats_exact_capacity() {
2420        for n in [0usize, 1, 2, 3, 7, 16, 17, 32, 33] {
2421            #[allow(clippy::cast_precision_loss)]
2422            let data: Vec<f64> = (0..n).map(|i| i as f64 + 0.5).collect();
2423            let mut out: Vec<u8> = Vec::with_capacity(2 + n * 33);
2424            let mut sink = ByteSink::new(&mut out);
2425            (data.as_slice()).write_json(&mut sink).expect("ser");
2426            let s = core::str::from_utf8(&out).expect("ASCII");
2427            // Sanity: parse back as Vec<f64>.
2428            let back: Vec<f64> = parse_str(s).expect("parse back");
2429            assert_eq!(back.len(), n, "n={n}");
2430            #[allow(clippy::cast_precision_loss, clippy::float_cmp)]
2431            for (i, &v) in back.iter().enumerate() {
2432                assert_eq!(v, i as f64 + 0.5, "n={n} i={i}");
2433            }
2434        }
2435    }
2436
2437    /// Same as above but for `Vec<f32>`. f32 widens to f64 in the writer
2438    /// but uses the same taint path, so the capacity boundary is identical.
2439    #[test]
2440    fn bytesink_slice_f32_exact_capacity() {
2441        for n in [0usize, 1, 2, 16, 17] {
2442            #[allow(clippy::cast_precision_loss)]
2443            let data: Vec<f32> = (0..n).map(|i| i as f32 + 0.25).collect();
2444            let mut out: Vec<u8> = Vec::with_capacity(2 + n * 33);
2445            let mut sink = ByteSink::new(&mut out);
2446            (data.as_slice()).write_json(&mut sink).expect("ser");
2447            let s = core::str::from_utf8(&out).expect("ASCII");
2448            let back: Vec<f32> = parse_str(s).expect("parse back");
2449            assert_eq!(back.len(), n);
2450            #[allow(clippy::cast_precision_loss, clippy::float_cmp)]
2451            for (i, &v) in back.iter().enumerate() {
2452                assert_eq!(v, i as f32 + 0.25, "n={n} i={i}");
2453            }
2454        }
2455    }
2456
2457    /// Slice of f64 containing non-finite values — exercises the taint
2458    /// accumulator path and verifies the slice writer surfaces a
2459    /// `NonFiniteFloat` error rather than emitting garbage bytes.
2460    #[test]
2461    fn bytesink_slice_nonfinite_taint_path() {
2462        // The non-finite path writes substitute bytes into the Vec before
2463        // reporting the error. Miri ensures those substitute writes stay
2464        // within the reserved capacity.
2465        for (name, bad_idx, bad_val) in [
2466            ("inf at 0", 0usize, f64::INFINITY),
2467            ("nan at end", 4usize, f64::NAN),
2468            ("neg_inf middle", 2usize, f64::NEG_INFINITY),
2469        ] {
2470            let mut data: Vec<f64> = (0..5).map(f64::from).collect();
2471            data[bad_idx] = bad_val;
2472            let r = to_string(data.as_slice());
2473            assert!(r.is_err(), "{name}: expected non-finite error");
2474        }
2475    }
2476
2477    /// Slice of i64 — exercises `write_byte_unchecked` for both `[`/`,`/`]`
2478    /// at exact capacity. Integers don't use the float taint path but they
2479    /// do use `write_array_reserved`'s `write_byte_unchecked` for delimiters.
2480    #[test]
2481    fn bytesink_slice_ints_exact_capacity() {
2482        // MAX_SERIALIZED_LEN for i64 = 20 ("-9223372036854775808"), so
2483        // hint = 2 + n * 21.
2484        for n in [0usize, 1, 5, 16, 17] {
2485            #[allow(clippy::cast_possible_wrap)]
2486            let n_i64 = n as i64;
2487            #[allow(clippy::cast_possible_wrap)]
2488            let data: Vec<i64> = (0..n).map(|i| (i as i64) - n_i64 / 2).collect();
2489            let mut out: Vec<u8> = Vec::with_capacity(2 + n * 21);
2490            let mut sink = ByteSink::new(&mut out);
2491            (data.as_slice()).write_json(&mut sink).expect("ser");
2492            let s = core::str::from_utf8(&out).expect("ASCII");
2493            let back: Vec<i64> = parse_str(s).expect("parse back");
2494            assert_eq!(back, data, "n={n}");
2495        }
2496    }
2497
2498    /// `decode_escapes`' literal-byte run uses `from_utf8_unchecked` on
2499    /// the stretch between escapes. The lexer's UTF-8 invariant must hold
2500    /// across multi-byte sequences. Test escapes interleaved with 2/3/4-byte
2501    /// UTF-8 chars so the literal chunk passed to the unsafe spans
2502    /// multibyte data.
2503    #[test]
2504    fn decode_escapes_multibyte_in_literal_chunk() {
2505        // 2-byte (é = c3 a9), 3-byte (€ = e2 82 ac), 4-byte (𝄞 = f0 9d 84 9e)
2506        // chars surrounding `\n` escapes. The literal chunks bracket the
2507        // escape and must contain valid UTF-8.
2508        let cases: &[(&str, &str)] = &[
2509            (r#""café\nfin""#, "café\nfin"),
2510            (r#""price: 5€\tea""#, "price: 5€\tea"),
2511            (r#""note 𝄞\nplayed""#, "note 𝄞\nplayed"),
2512            // Many escapes between multibyte stretches.
2513            (r#""éé\nçç\tüü""#, "éé\nçç\tüü"),
2514            // Escape at start / end with multibyte in middle.
2515            (r#""\n𝄞café\t""#, "\n𝄞café\t"),
2516            // Long literal run before single escape (exercises the SIMD
2517            // scan tail).
2518            (
2519                r#""aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaéé\n""#,
2520                "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaéé\n",
2521            ),
2522        ];
2523        for (json, expected) in cases {
2524            let s: String = parse_str(json).expect("parse");
2525            assert_eq!(s, *expected, "case {json}");
2526        }
2527    }
2528
2529    /// Empty slice / array — `write_array_reserved`'s `split_first` is None,
2530    /// no `write_byte_unchecked` for elements. Just `[` and `]`.
2531    #[test]
2532    fn bytesink_empty_slice_writes_brackets_only() {
2533        let empty: Vec<i64> = Vec::new();
2534        let s = to_string(empty.as_slice()).expect("ser");
2535        assert_eq!(s, "[]");
2536
2537        let empty: Vec<f64> = Vec::new();
2538        let s = to_string(empty.as_slice()).expect("ser");
2539        assert_eq!(s, "[]");
2540    }
2541
2542    /// Single-element slice — `write_array_reserved`'s `split_first` returns
2543    /// `(first, [])` so we skip the inner loop. Verify the byte boundary.
2544    #[test]
2545    fn bytesink_single_element_slice() {
2546        let one = [42_i64];
2547        assert_eq!(to_string(one.as_slice()).unwrap(), "[42]");
2548        let one = [1.5_f64];
2549        assert_eq!(to_string(one.as_slice()).unwrap(), "[1.5]");
2550        let one = [1.5_f32];
2551        assert_eq!(to_string(one.as_slice()).unwrap(), "[1.5]");
2552    }
2553
2554    /// Empty string — `decode_escapes` is called with `raw=&[]`. The
2555    /// `from_utf8_unchecked(&raw[start..i])` slice is empty, which is
2556    /// a degenerate edge case that must not OOB.
2557    #[test]
2558    fn decode_escapes_empty_string() {
2559        let s: String = parse_str(r#""""#).expect("parse");
2560        assert_eq!(s, "");
2561    }
2562
2563    /// Pure-ASCII without any escape — the literal-byte run covers the
2564    /// whole input and `find_backslash` returns None. The unsafe slice
2565    /// is `&raw[0..raw.len()]`.
2566    #[test]
2567    fn decode_escapes_no_escapes_pure_ascii() {
2568        // Use `Vec<String>` to force the owned-decode path even though
2569        // the input has no escapes (the `&str` impl would borrow).
2570        let v: Vec<String> = parse_str(r#"["hello world","no escapes here"]"#).expect("parse");
2571        assert_eq!(
2572            v,
2573            [String::from("hello world"), String::from("no escapes here")]
2574        );
2575    }
2576
2577    /// Decode an escape immediately at the start — the first literal-byte
2578    /// run is empty, so `from_utf8_unchecked(&raw[0..0])` is called.
2579    #[test]
2580    fn decode_escapes_escape_at_start() {
2581        let v: Vec<String> = parse_str(r#"["\nhello","\tworld"]"#).expect("parse");
2582        assert_eq!(v, [String::from("\nhello"), String::from("\tworld")]);
2583    }
2584
2585    /// Decode an escape immediately at the end — after the escape there is
2586    /// no literal-byte run.
2587    #[test]
2588    fn decode_escapes_escape_at_end() {
2589        let v: Vec<String> = parse_str(r#"["hello\n","world\t"]"#).expect("parse");
2590        assert_eq!(v, [String::from("hello\n"), String::from("world\t")]);
2591    }
2592
2593    /// Back-to-back escapes — multiple zero-length literal chunks.
2594    #[test]
2595    fn decode_escapes_consecutive_escapes() {
2596        let v: Vec<String> = parse_str(r#"["\n\t\r\\","\"\"\""]"#).expect("parse");
2597        assert_eq!(v, [String::from("\n\t\r\\"), String::from(r#"""""#)]);
2598    }
2599
2600    // -----------------------------------------------------------------
2601    // Vec<T>::vec_from_lex fast-path coverage. The default
2602    // `FromJson::vec_from_lex` impl drives `from_lex` per element; the
2603    // primitive types (`&str`, integers via macros, `Duration`) override
2604    // it to skip the per-element Event detour. These overrides have
2605    // independent code paths from the default and need their own tests.
2606    // -----------------------------------------------------------------
2607
2608    /// `Vec<&str>::vec_from_lex` — borrowed strings, fused fast path.
2609    #[test]
2610    fn vec_borrowed_str_fast_path() {
2611        // Empty array — early-return branch.
2612        let v: Vec<&str> = parse_str("[]").expect("empty");
2613        assert_eq!(v, Vec::<&str>::new());
2614
2615        // Single element — first push only, no while loop.
2616        let v: Vec<&str> = parse_str(r#"["only"]"#).expect("single");
2617        assert_eq!(v, ["only"]);
2618
2619        // Many elements — exercises the while-loop body.
2620        let v: Vec<&str> = parse_str(r#"["a","b","c","d","e"]"#).expect("many");
2621        assert_eq!(v, ["a", "b", "c", "d", "e"]);
2622
2623        // Reject escape-bearing string (the borrowed path requires no escapes).
2624        let r: Result<Vec<&str>, _> = parse_str(r#"["plain","esc\nbad"]"#);
2625        assert!(r.is_err(), "borrowed path must reject escape");
2626    }
2627
2628    /// `Vec<Duration>::vec_from_lex` — fused, with non-finite / negative rejection.
2629    #[cfg(feature = "std")]
2630    #[test]
2631    fn vec_duration_fast_path() {
2632        use std::time::Duration;
2633
2634        // Empty — early return.
2635        let v: Vec<Duration> = parse_str("[]").expect("empty");
2636        assert!(v.is_empty());
2637
2638        // Single — first push.
2639        let v: Vec<Duration> = parse_str("[1.5]").expect("single");
2640        assert_eq!(v, [Duration::from_secs_f64(1.5)]);
2641
2642        // Many — loop body.
2643        let v: Vec<Duration> = parse_str("[0.0,1.0,1.5,2.25,100.125]").expect("many");
2644        assert_eq!(v.len(), 5);
2645        assert_eq!(v[0], Duration::ZERO);
2646        assert_eq!(v[4], Duration::from_secs_f64(100.125));
2647
2648        // Negative — rejected.
2649        let r: Result<Vec<Duration>, _> = parse_str("[1.0,-2.0]");
2650        assert!(r.is_err());
2651    }
2652
2653    /// `Vec<i64>::vec_from_lex` — exercises the macro-generated override
2654    /// in `de.rs::impl_int!`, including the bounds check on the empty branch.
2655    #[test]
2656    fn vec_i64_fast_path() {
2657        let v: Vec<i64> = parse_str("[]").expect("empty");
2658        assert!(v.is_empty());
2659
2660        let v: Vec<i64> = parse_str("[42]").expect("single");
2661        assert_eq!(v, [42]);
2662
2663        let v: Vec<i64> =
2664            parse_str("[1,-1,9223372036854775807,-9223372036854775808]").expect("many");
2665        assert_eq!(v, [1, -1, i64::MAX, i64::MIN]);
2666
2667        // Out-of-range for u32 should error.
2668        let r: Result<Vec<u32>, _> = parse_str("[1,99999999999]");
2669        assert!(r.is_err(), "u32 should overflow");
2670    }
2671
2672    /// `Vec<u128>::vec_from_lex` — wide-int macro path.
2673    #[test]
2674    fn vec_u128_fast_path() {
2675        let v: Vec<u128> = parse_str("[]").expect("empty");
2676        assert!(v.is_empty());
2677
2678        let v: Vec<u128> = parse_str("[0,170141183460469231731687303715884105727]").expect("many");
2679        assert_eq!(v, [0u128, i128::MAX as u128]);
2680    }
2681
2682    // -----------------------------------------------------------------
2683    // `decode_escapes` — full branch coverage. The match arms for `\b`,
2684    // `\f`, `\/`, and the various surrogate-pair error paths weren't
2685    // exercised by the existing tests.
2686    // -----------------------------------------------------------------
2687
2688    #[test]
2689    fn decode_all_simple_escapes() {
2690        // Each backslash escape variant — covers every match arm in
2691        // `decode_escapes`.
2692        let cases: &[(&str, &str)] = &[
2693            (r#""\b""#, "\u{0008}"), // backspace
2694            (r#""\f""#, "\u{000C}"), // form feed
2695            (r#""\/""#, "/"),        // solidus
2696            (r#""\\""#, "\\"),       // backslash
2697            (r#""\"""#, "\""),       // quote
2698            (r#""\n""#, "\n"),
2699            (r#""\r""#, "\r"),
2700            (r#""\t""#, "\t"),
2701        ];
2702        for &(json, expected) in cases {
2703            let s: String = parse_str(json).expect(json);
2704            assert_eq!(s, expected, "case {json}");
2705        }
2706    }
2707
2708    #[test]
2709    fn decode_unknown_escape_errors() {
2710        // `\x` is not a recognized escape — last match arm.
2711        let r: Result<String, _> = parse_str(r#""\x""#);
2712        assert!(r.is_err(), "unknown escape should error");
2713
2714        // Backslash at end-of-input — `i >= raw.len()` branch.
2715        let r: Result<String, _> = parse_str("\"\\\"");
2716        assert!(r.is_err(), "lone trailing backslash should error");
2717    }
2718
2719    #[test]
2720    fn decode_unicode_escape_short_input_errors() {
2721        // `\u` followed by < 4 hex digits — `i + 5 > raw.len()` branch.
2722        let r: Result<String, _> = parse_str(r#""\u00""#);
2723        assert!(r.is_err(), "short \\u should error");
2724        let r: Result<String, _> = parse_str(r#""\u""#);
2725        assert!(r.is_err());
2726    }
2727
2728    #[test]
2729    fn decode_unicode_lone_low_surrogate_errors() {
2730        // `\uDC00` standalone is a lone low surrogate — second
2731        // `0xDC00..=0xDFFF` branch.
2732        let r: Result<String, _> = parse_str(r#""\uDC00""#);
2733        assert!(r.is_err(), "lone low surrogate should error");
2734    }
2735
2736    #[test]
2737    fn decode_unicode_high_surrogate_then_invalid_low_errors() {
2738        // High surrogate \uD800 followed by another `\u` escape whose
2739        // codepoint is OUTSIDE the low-surrogate range — exercises the
2740        // explicit range-check arm (raw[i+1]==`\\` AND raw[i+2]==`u` but
2741        // the parsed low_value isn't a low surrogate).
2742        let json = "\"\\uD800\\u0041\""; // high then 'A' as A
2743        let r: Result<String, _> = parse_str(json);
2744        assert!(
2745            r.is_err(),
2746            "high surrogate then non-low-surrogate \\u must error"
2747        );
2748    }
2749
2750    /// High surrogate followed by `\uXXXX` where XXXX is a valid low
2751    /// surrogate, but the four hex digits are at end-of-input. Covers
2752    /// the `i + 7 > raw.len()` short-buffer guard.
2753    #[test]
2754    fn decode_unicode_high_surrogate_then_short_second_escape_errors() {
2755        let json = r#""\uD800\u""#;
2756        let r: Result<String, _> = parse_str(json);
2757        assert!(r.is_err(), "high surrogate then truncated \\u must error");
2758    }
2759
2760    /// Invalid hex inside the second \u of a surrogate pair.
2761    #[test]
2762    fn decode_unicode_high_surrogate_then_invalid_hex_errors() {
2763        let json = "\"\\uD800\\uZZZZ\"";
2764        let r: Result<String, _> = parse_str(json);
2765        assert!(r.is_err(), "high surrogate then bad hex must error");
2766    }
2767
2768    #[test]
2769    fn decode_unicode_high_surrogate_then_non_u_escape_errors() {
2770        // `\uD800` followed by `\n` (not a `\u` escape).
2771        let r: Result<String, _> = parse_str(r#""\uD800\n""#);
2772        assert!(
2773            r.is_err(),
2774            "high surrogate not followed by \\u should error"
2775        );
2776
2777        // `\uD800` followed by non-backslash byte (EOF or literal).
2778        let r: Result<String, _> = parse_str(r#""\uD800A""#);
2779        assert!(
2780            r.is_err(),
2781            "high surrogate not followed by escape should error"
2782        );
2783    }
2784
2785    #[test]
2786    fn decode_unicode_surrogate_pair_round_trips() {
2787        // Valid surrogate pair for U+1F600 (GRINNING FACE): high=D83D
2788        // low=DE00. Encoded as `\u` escapes (not the literal emoji) so
2789        // decode_escapes' surrogate arm runs — literal multi-byte UTF-8
2790        // goes through the lexer's `consume_utf8_multibyte` instead.
2791        let json = "\"\\uD83D\\uDE00\"";
2792        let s: String = parse_str(json).expect("parse");
2793        assert_eq!(s, "\u{1F600}");
2794
2795        // Surrogate pair adjacent to literal ASCII / other escapes.
2796        let json = "\"a\\uD83D\\uDE00b\\uD83D\\uDE01c\"";
2797        let s: String = parse_str(json).expect("parse");
2798        assert_eq!(s, "a\u{1F600}b\u{1F601}c");
2799    }
2800
2801    #[test]
2802    fn decode_unicode_bmp_escape_round_trips() {
2803        // Standard BMP unicode escape — non-surrogate path. U+00E9 ('é')
2804        // encoded as `é` so decode_escapes' \u arm runs (a literal
2805        // "é" goes through consume_utf8_multibyte instead).
2806        let json = "\"\\u00E9\"";
2807        let s: String = parse_str(json).expect("parse");
2808        assert_eq!(s, "é");
2809
2810        // BMP escapes at various code points.
2811        let json = "\"A\\u00A3\\u20AC\""; // A, £, €
2812        let s: String = parse_str(json).expect("parse");
2813        assert_eq!(s, "A£€");
2814    }
2815
2816    #[test]
2817    fn decode_invalid_hex_in_u_escape_errors() {
2818        // `\u00ZX` — invalid hex.
2819        let r: Result<String, _> = parse_str(r#""\u00ZX""#);
2820        assert!(r.is_err(), "invalid hex should error");
2821    }
2822
2823    // -----------------------------------------------------------------
2824    // `to_decimal_uncentred` — the float path for powers of 2 (mantissa
2825    // == 1 << 52). Hit when the IEEE 754 mantissa lands exactly on the
2826    // uncentred boundary. These values are rare in random samples, so
2827    // need explicit tests.
2828    // -----------------------------------------------------------------
2829
2830    /// Powers of 2 hit the uncentred decompose path. Each gets a
2831    /// shortest-roundtrip rendering through `to_decimal_uncentred`.
2832    #[test]
2833    fn to_decimal_uncentred_powers_of_two_round_trip() {
2834        // f64 values where mantissa == 1 << 52 and exponent != EXPONENT_MIN:
2835        // these are 2.0, 4.0, 8.0, 16.0, ..., up to 2^1023.
2836        let cases = [
2837            2.0_f64,
2838            4.0,
2839            8.0,
2840            16.0,
2841            32.0,
2842            64.0,
2843            128.0,
2844            256.0,
2845            512.0,
2846            1024.0,
2847            2.0_f64.powi(20),
2848            2.0_f64.powi(50),
2849            2.0_f64.powi(100),
2850            2.0_f64.powi(500),
2851            2.0_f64.powi(1023), // largest finite power of 2
2852            // Negative powers of 2 — half exponent.
2853            2.0_f64.powi(-1), // 0.5
2854            2.0_f64.powi(-2), // 0.25
2855            2.0_f64.powi(-10),
2856            2.0_f64.powi(-50),
2857            2.0_f64.powi(-100),
2858            2.0_f64.powi(-1000),
2859        ];
2860        for &v in &cases {
2861            let s = to_string(&v).expect("ser");
2862            let back: f64 = parse_str(&s).expect("parse back");
2863            #[allow(clippy::float_cmp)]
2864            {
2865                assert_eq!(back, v, "round-trip for {v:e}: emitted {s:?}");
2866            }
2867        }
2868    }
2869
2870    /// Negative powers of 2 — sign path through `to_decimal_uncentred`.
2871    #[test]
2872    fn to_decimal_uncentred_negative_powers_round_trip() {
2873        for k in [-30, -10, -1, 1, 10, 30, 100, 500] {
2874            let v = -(2.0_f64.powi(k));
2875            let s = to_string(&v).expect("ser");
2876            let back: f64 = parse_str(&s).expect("parse back");
2877            #[allow(clippy::float_cmp)]
2878            {
2879                assert_eq!(back, v, "round-trip for {v:e}: emitted {s:?}");
2880            }
2881        }
2882    }
2883
2884    /// Subnormal f64 — minimum positive denormal, uses uncentred path
2885    /// at a different boundary.
2886    #[test]
2887    fn to_decimal_uncentred_subnormals_round_trip() {
2888        let cases = [
2889            5e-324_f64,        // smallest subnormal
2890            f64::MIN_POSITIVE, // smallest normal
2891            -f64::MIN_POSITIVE,
2892            f64::MAX,
2893            -f64::MAX,
2894        ];
2895        for &v in &cases {
2896            let s = to_string(&v).expect("ser");
2897            let back: f64 = parse_str(&s).expect("parse back");
2898            #[allow(clippy::float_cmp)]
2899            {
2900                assert_eq!(back, v, "round-trip for {v:e}: emitted {s:?}");
2901            }
2902        }
2903    }
2904
2905    // -----------------------------------------------------------------
2906    // Direct-sink coverage. Every public sink type (StringSink,
2907    // PrettyStringSink, ByteSink unchecked methods) needs at least one
2908    // call so the CRAP gate's zero-coverage trigger doesn't fire.
2909    // -----------------------------------------------------------------
2910
2911    /// `StringSink` — the `&mut String` sink. Exercises `write_byte`,
2912    /// `write_str_raw`, and `write_float_f64` directly.
2913    #[test]
2914    fn string_sink_writes_directly() {
2915        let mut out = String::new();
2916        let mut sink = StringSink::new(&mut out);
2917        // write_byte and write_str_raw via the trait
2918        sink.write_byte(b'[').unwrap();
2919        sink.write_str_raw("1").unwrap();
2920        sink.write_byte(b',').unwrap();
2921        // write_float_f64 (StringSink override)
2922        sink.write_float_f64(2.5).unwrap();
2923        sink.write_byte(b']').unwrap();
2924        assert_eq!(out, "[1,2.5]");
2925
2926        // Non-finite rejection through the StringSink path.
2927        let mut out = String::new();
2928        let mut sink = StringSink::new(&mut out);
2929        assert!(sink.write_float_f64(f64::NAN).is_err());
2930    }
2931
2932    /// `PrettyStringSink::with_indent` — custom indent variant.
2933    /// Existing pretty tests only cover the default-indent constructor.
2934    #[test]
2935    fn pretty_sink_with_indent_uses_custom_indent() {
2936        let mut out = String::new();
2937        let mut sink = PrettyStringSink::with_indent(&mut out, "\t");
2938        let data = (1_i32, 2_i32, 3_i32);
2939        data.write_json(&mut sink).unwrap();
2940        // Tab indent → contains a `\n\t` sequence.
2941        assert!(out.contains("\n\t"), "got: {out:?}");
2942    }
2943
2944    /// `PrettyStringSink::write_float_f64` — covers the pretty-sink
2945    /// float arm (existing tests use the simple-byte and string paths).
2946    #[test]
2947    fn pretty_sink_handles_floats() {
2948        let v: alloc::vec::Vec<f64> = alloc::vec![1.5, 2.5, 3.0];
2949        let s = to_string_pretty(v.as_slice()).unwrap();
2950        assert!(s.contains("1.5"));
2951        assert!(s.contains("3.0"));
2952    }
2953
2954    /// `ByteSink::write_float_f64_unchecked` — non-finite triggers the
2955    /// outlined `cold_nonfinite_byte_sink` error. Exercises the cold path.
2956    #[test]
2957    fn bytesink_unchecked_float_nonfinite_returns_error() {
2958        let mut out: Vec<u8> = Vec::with_capacity(64);
2959        let mut sink = ByteSink::new(&mut out);
2960        // SAFETY: ample reserved capacity (≥32 bytes).
2961        #[allow(unsafe_code)]
2962        let r = unsafe { sink.write_float_f64_unchecked(f64::INFINITY) };
2963        assert!(r.is_err(), "non-finite must error through cold arm");
2964    }
2965
2966    /// `ByteSink::write_float_f64_unchecked_finite` — direct call with
2967    /// a finite value. The slice fast path calls this internally.
2968    #[test]
2969    fn bytesink_unchecked_finite_writes_value() {
2970        let mut out: Vec<u8> = Vec::with_capacity(64);
2971        let mut sink = ByteSink::new(&mut out);
2972        // Pick a finite value that round-trips exactly through the formatter
2973        // and `f64::parse`. 2.5 is exact in binary; avoiding 3.14 also dodges
2974        // clippy's approx_constant warning about PI.
2975        let value = 2.5_f64;
2976        // SAFETY: reserved 64 bytes, value is finite.
2977        #[allow(unsafe_code)]
2978        unsafe {
2979            sink.write_float_f64_unchecked_finite(value).unwrap();
2980        }
2981        let s = core::str::from_utf8(&out).unwrap();
2982        let parsed: f64 = s.parse().unwrap();
2983        // Bit-pattern compare: write→parse must round-trip exactly.
2984        assert_eq!(parsed.to_bits(), value.to_bits());
2985    }
2986
2987    /// `ToJson for Path` (std-only) — covers `Path::write_json`.
2988    #[cfg(feature = "std")]
2989    #[test]
2990    fn path_serializes_via_to_string() {
2991        use std::path::Path;
2992        let p = Path::new("/tmp/file.txt");
2993        let s = to_string(p).unwrap();
2994        assert_eq!(s, "\"/tmp/file.txt\"");
2995    }
2996
2997    /// `MapKeyOut for Cow<'_, str>` — used when a HashMap/BTreeMap
2998    /// key type is `Cow<'_, str>`. Exercises `Cow::as_str`.
2999    #[test]
3000    fn map_with_cow_str_keys_serializes() {
3001        use alloc::borrow::Cow;
3002        use alloc::collections::BTreeMap;
3003        let mut m: BTreeMap<Cow<'_, str>, i32> = BTreeMap::new();
3004        m.insert(Cow::Borrowed("a"), 1);
3005        m.insert(Cow::Owned(String::from("b")), 2);
3006        let s = to_string(&m).unwrap();
3007        assert!(s.contains(r#""a":1"#));
3008        assert!(s.contains(r#""b":2"#));
3009    }
3010
3011    /// `f64::pre_validate_slice` — the post-taint stub returns Ok(()).
3012    /// Direct call to ensure the function is exercised.
3013    #[test]
3014    fn f64_pre_validate_slice_is_noop() {
3015        let slice: &[f64] = &[1.0, 2.0, f64::INFINITY];
3016        // The fn body just returns Ok; calling it through the trait
3017        // exercises both the dispatch and the body.
3018        let r = <f64 as ToJson>::pre_validate_slice(slice);
3019        assert!(r.is_ok());
3020    }
3021
3022    /// `crate::ser::float::format_f64_write` and `reject_non_finite`
3023    /// are exercised by `StringSink::write_float_f64` (via the `float::`
3024    /// module). Existing tests cover that, but cover them explicitly
3025    /// for the non-finite branch through the public `to_fmt` entry
3026    /// point which uses `FmtWriteSink` (a different code path).
3027    #[test]
3028    fn to_fmt_writes_finite_and_rejects_nonfinite() {
3029        let mut s = String::new();
3030        to_fmt(&1.5_f64, &mut s).unwrap();
3031        assert_eq!(s, "1.5");
3032
3033        let mut s = String::new();
3034        assert!(to_fmt(&f64::INFINITY, &mut s).is_err());
3035    }
3036
3037    /// `fmt_write_error` is invoked when the underlying `core::fmt::Write`
3038    /// impl returns an error. Construct a writer that always fails and
3039    /// verify `to_fmt` propagates a typed `Error` (rather than the
3040    /// detail-less `fmt::Error`).
3041    #[test]
3042    fn to_fmt_propagates_underlying_write_failure() {
3043        use core::fmt;
3044
3045        /// Writer that errors on every call — drives `fmt_write_error`.
3046        struct AlwaysFail;
3047        impl fmt::Write for AlwaysFail {
3048            fn write_str(&mut self, _s: &str) -> fmt::Result {
3049                Err(fmt::Error)
3050            }
3051        }
3052
3053        // Any non-empty serializable value flushes at least one byte
3054        // through `write_str` / `write_char`, which the writer rejects.
3055        let mut sink = AlwaysFail;
3056        let r = to_fmt(&42_i64, &mut sink);
3057        assert!(r.is_err(), "expected propagated error from failing writer");
3058
3059        // Also exercise the float path through FmtWriteSink, which uses
3060        // a separate `fmt_write_error()` call site.
3061        let mut sink = AlwaysFail;
3062        let r = to_fmt(&1.5_f64, &mut sink);
3063        assert!(r.is_err());
3064    }
3065}