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