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    // Regression: a *plain* (un-renamed) field followed by a skipped
1876    // `skip_if_none` field, then a present field. The plain fast path folds
1877    // the comma+key into a compile-time literal; it must still record that
1878    // output was committed so the later runtime comma (gated on `!__first`)
1879    // is emitted. Prior to the fix this produced `.."b""c":..` (missing
1880    // comma) whenever the optional middle field was None.
1881    #[derive(Debug, PartialEq, ToJson)]
1882    struct PlainThenSkip {
1883        a: u32,
1884        b: u32,
1885        #[bourne(skip_if_none)]
1886        mid: Option<u32>,
1887        c: u32,
1888    }
1889
1890    #[test]
1891    fn plain_field_before_skipped_option_keeps_comma() {
1892        // mid = None -> the fast-path plain fields must still separate from `c`.
1893        let v = PlainThenSkip {
1894            a: 1,
1895            b: 2,
1896            mid: None,
1897            c: 3,
1898        };
1899        assert_eq!(to_string(&v).unwrap(), r#"{"a":1,"b":2,"c":3}"#);
1900        // mid = Some -> comma on both sides of the optional.
1901        let v = PlainThenSkip {
1902            a: 1,
1903            b: 2,
1904            mid: Some(9),
1905            c: 3,
1906        };
1907        assert_eq!(to_string(&v).unwrap(), r#"{"a":1,"b":2,"mid":9,"c":3}"#);
1908    }
1909
1910    // Empty struct edge case.
1911    #[derive(Debug, PartialEq, ToJson)]
1912    struct Empty {}
1913
1914    #[test]
1915    fn empty_struct_emits_empty_object() {
1916        assert_eq!(to_string(&Empty {}).unwrap(), "{}");
1917    }
1918
1919    // String escaping inside emitted values (sanity — should already
1920    // work via the ToJson<String> impl, but the macro shouldn't
1921    // double-escape or corrupt the output).
1922    #[derive(Debug, PartialEq, ToJson)]
1923    struct WithEscape {
1924        text: String,
1925    }
1926
1927    #[test]
1928    fn macro_passes_strings_to_escape_path() {
1929        let v = WithEscape {
1930            text: String::from("a\nb\"c"),
1931        };
1932        let s = to_string(&v).unwrap();
1933        assert_eq!(s, r#"{"text":"a\nb\"c"}"#);
1934    }
1935
1936    // Newtype tuple struct — emits the inner value bare.
1937    #[derive(Debug, PartialEq, ToJson)]
1938    struct UserId(u64);
1939
1940    #[test]
1941    fn newtype_emits_bare_value() {
1942        let v = UserId(42);
1943        assert_eq!(to_string(&v).unwrap(), "42");
1944    }
1945
1946    #[derive(Debug, PartialEq, ToJson)]
1947    struct BorrowedTag<'input>(&'input str);
1948
1949    #[test]
1950    fn newtype_with_lifetime() {
1951        let v = BorrowedTag("hello");
1952        assert_eq!(to_string(&v).unwrap(), r#""hello""#);
1953    }
1954
1955    // Multi-field tuple struct — emits a JSON array.
1956    #[derive(Debug, PartialEq, ToJson)]
1957    struct Point(i32, i32);
1958
1959    #[test]
1960    fn tuple_struct_emits_array() {
1961        let v = Point(3, -7);
1962        assert_eq!(to_string(&v).unwrap(), "[3,-7]");
1963    }
1964
1965    #[derive(Debug, PartialEq, ToJson)]
1966    struct Triple(i32, String, bool);
1967
1968    #[test]
1969    fn three_field_tuple_struct() {
1970        let v = Triple(1, String::from("hi"), true);
1971        assert_eq!(to_string(&v).unwrap(), r#"[1,"hi",true]"#);
1972    }
1973
1974    // Externally-tagged enum — the default encoding.
1975    #[derive(Debug, PartialEq, ToJson)]
1976    enum Shape {
1977        Circle,
1978        Wrapper(u32),
1979        Pair(u32, String),
1980        Box {
1981            w: u32,
1982            h: u32,
1983        },
1984        #[bourne(rename = "tri")]
1985        Triangle,
1986    }
1987
1988    #[test]
1989    fn enum_unit_emits_string() {
1990        assert_eq!(to_string(&Shape::Circle).unwrap(), r#""Circle""#);
1991    }
1992
1993    #[test]
1994    fn enum_renamed_unit() {
1995        assert_eq!(to_string(&Shape::Triangle).unwrap(), r#""tri""#);
1996    }
1997
1998    #[test]
1999    fn enum_newtype_emits_object() {
2000        assert_eq!(to_string(&Shape::Wrapper(7)).unwrap(), r#"{"Wrapper":7}"#);
2001    }
2002
2003    #[test]
2004    fn enum_tuple_emits_object_with_array() {
2005        let v = Shape::Pair(1, String::from("x"));
2006        assert_eq!(to_string(&v).unwrap(), r#"{"Pair":[1,"x"]}"#);
2007    }
2008
2009    #[test]
2010    fn enum_struct_variant_emits_nested_object() {
2011        let v = Shape::Box { w: 10, h: 20 };
2012        assert_eq!(to_string(&v).unwrap(), r#"{"Box":{"w":10,"h":20}}"#);
2013    }
2014
2015    // Internally-tagged enum.
2016    #[derive(Debug, PartialEq, ToJson)]
2017    #[bourne(tag = "type")]
2018    enum Event {
2019        Heartbeat,
2020        #[bourne(rename = "click")]
2021        Click {
2022            x: u32,
2023            y: u32,
2024        },
2025    }
2026
2027    #[test]
2028    fn internal_tag_unit_emits_object_with_tag() {
2029        assert_eq!(
2030            to_string(&Event::Heartbeat).unwrap(),
2031            r#"{"type":"Heartbeat"}"#
2032        );
2033    }
2034
2035    #[test]
2036    fn internal_tag_struct_inlines_fields() {
2037        let v = Event::Click { x: 1, y: 2 };
2038        assert_eq!(to_string(&v).unwrap(), r#"{"type":"click","x":1,"y":2}"#);
2039    }
2040
2041    // Adjacently-tagged enum.
2042    #[derive(Debug, PartialEq, ToJson)]
2043    #[bourne(tag = "t", content = "c")]
2044    enum Msg {
2045        Ping,
2046        Echo(String),
2047        Pair(u32, u32),
2048        Body { text: String },
2049    }
2050
2051    #[test]
2052    fn adjacent_unit_emits_only_tag() {
2053        assert_eq!(to_string(&Msg::Ping).unwrap(), r#"{"t":"Ping"}"#);
2054    }
2055
2056    #[test]
2057    fn adjacent_newtype_emits_content() {
2058        let v = Msg::Echo(String::from("hi"));
2059        assert_eq!(to_string(&v).unwrap(), r#"{"t":"Echo","c":"hi"}"#);
2060    }
2061
2062    #[test]
2063    fn adjacent_tuple_emits_content_array() {
2064        let v = Msg::Pair(1, 2);
2065        assert_eq!(to_string(&v).unwrap(), r#"{"t":"Pair","c":[1,2]}"#);
2066    }
2067
2068    #[test]
2069    fn adjacent_struct_emits_content_object() {
2070        let v = Msg::Body {
2071            text: String::from("ok"),
2072        };
2073        assert_eq!(to_string(&v).unwrap(), r#"{"t":"Body","c":{"text":"ok"}}"#);
2074    }
2075
2076    // Untagged enum.
2077    #[derive(Debug, PartialEq, ToJson)]
2078    #[bourne(untagged)]
2079    enum Mixed {
2080        Nothing,
2081        One(u32),
2082        Two(u32, u32),
2083        Body { name: String },
2084    }
2085
2086    #[test]
2087    fn untagged_unit_emits_null() {
2088        assert_eq!(to_string(&Mixed::Nothing).unwrap(), "null");
2089    }
2090
2091    #[test]
2092    fn untagged_newtype_emits_inner() {
2093        assert_eq!(to_string(&Mixed::One(42)).unwrap(), "42");
2094    }
2095
2096    #[test]
2097    fn untagged_tuple_emits_array() {
2098        assert_eq!(to_string(&Mixed::Two(1, 2)).unwrap(), "[1,2]");
2099    }
2100
2101    #[test]
2102    fn untagged_struct_emits_object() {
2103        let v = Mixed::Body {
2104            name: String::from("x"),
2105        };
2106        assert_eq!(to_string(&v).unwrap(), r#"{"name":"x"}"#);
2107    }
2108}
2109
2110/// Sink-adapter tests: `to_writer` (`io::Write`) and `to_fmt` (`fmt::Write`)
2111/// must produce identical bytes to the canonical `to_string` path.
2112#[cfg(all(test, feature = "std"))]
2113mod sink_adapter_tests {
2114    use super::{to_fmt, to_string};
2115
2116    #[test]
2117    fn fmt_sink_matches_to_string_for_struct() {
2118        let m = vec![("a", 1_i32), ("b", 2)]
2119            .into_iter()
2120            .collect::<std::collections::BTreeMap<_, _>>();
2121        let canonical = to_string(&m).unwrap();
2122        let mut out = String::new();
2123        to_fmt(&m, &mut out).unwrap();
2124        assert_eq!(out, canonical);
2125    }
2126
2127    #[test]
2128    fn fmt_sink_handles_floats_with_grisu3() {
2129        let canonical = to_string(&1.5_f64).unwrap();
2130        let mut out = String::new();
2131        to_fmt(&1.5_f64, &mut out).unwrap();
2132        assert_eq!(out, canonical);
2133    }
2134
2135    #[test]
2136    fn fmt_sink_rejects_non_finite() {
2137        let mut out = String::new();
2138        let err = to_fmt(&f64::INFINITY, &mut out).unwrap_err();
2139        assert_eq!(err.kind, crate::ErrorKind::NonFiniteFloat);
2140    }
2141
2142    #[cfg(feature = "std")]
2143    #[test]
2144    fn io_writer_matches_to_string_for_struct() {
2145        use super::to_writer;
2146        let v = vec![1_i32, 2, 3];
2147        let canonical = to_string(&v).unwrap();
2148        let mut buf = Vec::<u8>::new();
2149        to_writer(&v, &mut buf).unwrap();
2150        assert_eq!(String::from_utf8(buf).unwrap(), canonical);
2151    }
2152
2153    #[cfg(feature = "std")]
2154    #[test]
2155    fn io_writer_handles_floats() {
2156        use super::to_writer;
2157        let canonical = to_string(&-2.7e-5_f64).unwrap();
2158        let mut buf = Vec::<u8>::new();
2159        to_writer(&-2.7e-5_f64, &mut buf).unwrap();
2160        assert_eq!(String::from_utf8(buf).unwrap(), canonical);
2161    }
2162
2163    #[cfg(feature = "std")]
2164    #[test]
2165    fn io_writer_rejects_non_finite() {
2166        use super::to_writer;
2167        let mut buf = Vec::<u8>::new();
2168        let err = to_writer(&f64::NAN, &mut buf).unwrap_err();
2169        assert_eq!(err.kind(), std::io::ErrorKind::InvalidData);
2170    }
2171
2172    #[test]
2173    fn pretty_empty_object_compact() {
2174        use super::to_string_pretty;
2175        let m: std::collections::BTreeMap<String, i32> = std::collections::BTreeMap::new();
2176        assert_eq!(to_string_pretty(&m).unwrap(), "{}");
2177    }
2178
2179    #[test]
2180    fn pretty_empty_array_compact() {
2181        use super::to_string_pretty;
2182        let v: Vec<i32> = Vec::new();
2183        assert_eq!(to_string_pretty(&v).unwrap(), "[]");
2184    }
2185
2186    #[test]
2187    fn pretty_array_indents_two_spaces() {
2188        use super::to_string_pretty;
2189        let v = vec![1_i32, 2, 3];
2190        assert_eq!(to_string_pretty(&v).unwrap(), "[\n  1,\n  2,\n  3\n]");
2191    }
2192
2193    #[test]
2194    fn pretty_object_keys_have_one_space_after_colon() {
2195        use super::to_string_pretty;
2196        let m: std::collections::BTreeMap<&str, i32> = [("a", 1), ("b", 2)].into_iter().collect();
2197        assert_eq!(
2198            to_string_pretty(&m).unwrap(),
2199            "{\n  \"a\": 1,\n  \"b\": 2\n}",
2200        );
2201    }
2202
2203    #[test]
2204    fn pretty_nested_indents_proportionally() {
2205        use super::to_string_pretty;
2206        let v: Vec<Vec<i32>> = vec![vec![1, 2], vec![3]];
2207        assert_eq!(
2208            to_string_pretty(&v).unwrap(),
2209            "[\n  [\n    1,\n    2\n  ],\n  [\n    3\n  ]\n]",
2210        );
2211    }
2212
2213    #[cfg(feature = "indexmap")]
2214    #[test]
2215    fn indexmap_preserves_insertion_order_on_parse() {
2216        use crate::parse_str;
2217        // Distinct, non-alphabetical order so a hash-bucket walk
2218        // would visibly reshuffle. IndexMap must yield the keys in
2219        // the order they appeared in the JSON.
2220        let json = r#"{"zebra":1,"alpha":2,"mango":3}"#;
2221        let m: indexmap::IndexMap<String, i32> = parse_str(json).unwrap();
2222        let keys: Vec<&str> = m.keys().map(String::as_str).collect();
2223        assert_eq!(keys, ["zebra", "alpha", "mango"]);
2224        assert_eq!(m["alpha"], 2);
2225    }
2226
2227    #[cfg(feature = "indexmap")]
2228    #[test]
2229    fn indexmap_round_trips_preserving_order() {
2230        use super::to_string;
2231        use crate::parse_str;
2232        let mut m = indexmap::IndexMap::<String, i32>::new();
2233        m.insert("z".into(), 1);
2234        m.insert("a".into(), 2);
2235        m.insert("m".into(), 3);
2236        let s = to_string(&m).unwrap();
2237        // Wire shape should preserve declaration order.
2238        assert_eq!(s, r#"{"z":1,"a":2,"m":3}"#);
2239        // Round-trip back into IndexMap must keep that order.
2240        let back: indexmap::IndexMap<String, i32> = parse_str(&s).unwrap();
2241        assert_eq!(
2242            back.keys().map(String::as_str).collect::<Vec<_>>(),
2243            ["z", "a", "m"],
2244        );
2245    }
2246
2247    #[cfg(feature = "indexmap")]
2248    #[test]
2249    fn indexmap_rejects_duplicate_keys() {
2250        use crate::parse_str;
2251        let r: Result<indexmap::IndexMap<String, i32>, _> = parse_str(r#"{"a":1,"a":2}"#);
2252        assert_eq!(r.unwrap_err().kind, crate::ErrorKind::DuplicateKey);
2253    }
2254
2255    #[cfg(feature = "indexmap")]
2256    #[test]
2257    fn indexset_round_trips() {
2258        use super::to_string;
2259        use crate::parse_str;
2260        let mut s = indexmap::IndexSet::<i32>::new();
2261        s.insert(3);
2262        s.insert(1);
2263        s.insert(2);
2264        let json = to_string(&s).unwrap();
2265        assert_eq!(json, "[3,1,2]");
2266        let back: indexmap::IndexSet<i32> = parse_str(&json).unwrap();
2267        assert_eq!(back.iter().copied().collect::<Vec<_>>(), [3, 1, 2]);
2268    }
2269
2270    #[test]
2271    fn pretty_round_trips_through_compact_parse() {
2272        use super::{parse_str, to_string_pretty};
2273        let m: std::collections::BTreeMap<String, Vec<i32>> = [
2274            (String::from("a"), vec![1, 2]),
2275            (String::from("b"), vec![3]),
2276        ]
2277        .into_iter()
2278        .collect();
2279        let pretty = to_string_pretty(&m).unwrap();
2280        // Whitespace is irrelevant to the parser; the pretty form must
2281        // re-parse to the same map.
2282        let back: std::collections::BTreeMap<String, Vec<i32>> = parse_str(&pretty).unwrap();
2283        assert_eq!(back, m);
2284    }
2285
2286    #[cfg(feature = "std")]
2287    #[test]
2288    fn io_writer_propagates_underlying_error() {
2289        use super::to_writer;
2290        // A writer that always errors: assert the io::Error reaches us
2291        // unmolested instead of getting flattened to a generic kind.
2292        struct FailingWriter;
2293        impl std::io::Write for FailingWriter {
2294            fn write(&mut self, _: &[u8]) -> std::io::Result<usize> {
2295                Err(std::io::Error::other("disk full"))
2296            }
2297            fn flush(&mut self) -> std::io::Result<()> {
2298                Ok(())
2299            }
2300        }
2301        let mut w = FailingWriter;
2302        let err = to_writer(&"hi", &mut w).unwrap_err();
2303        assert_eq!(err.to_string(), "disk full");
2304    }
2305}
2306
2307/// Combined derive tests. Each type gets both `FromJson` and `ToJson`
2308/// from one `#[derive(...)]`.
2309#[cfg(all(test, feature = "std"))]
2310mod json_macro_tests {
2311    use super::{parse_str, to_string};
2312    use crate::{FromJson, ToJson};
2313
2314    #[derive(Debug, PartialEq, FromJson, ToJson)]
2315    struct Plain {
2316        id: u32,
2317        name: String,
2318    }
2319
2320    #[test]
2321    fn plain_struct_round_trips() {
2322        let v = Plain {
2323            id: 7,
2324            name: String::from("alice"),
2325        };
2326        let s = to_string(&v).unwrap();
2327        assert_eq!(s, r#"{"id":7,"name":"alice"}"#);
2328        let back: Plain = parse_str(&s).unwrap();
2329        assert_eq!(back, v);
2330    }
2331
2332    #[derive(Debug, PartialEq, FromJson, ToJson)]
2333    struct Borrowed<'input> {
2334        tag: &'input str,
2335        count: u32,
2336    }
2337
2338    #[test]
2339    fn struct_with_lifetime_round_trips() {
2340        let json = r#"{"tag":"hi","count":3}"#;
2341        let v: Borrowed<'_> = parse_str(json).unwrap();
2342        assert_eq!(
2343            v,
2344            Borrowed {
2345                tag: "hi",
2346                count: 3
2347            }
2348        );
2349        assert_eq!(to_string(&v).unwrap(), json);
2350    }
2351
2352    #[derive(Debug, PartialEq, FromJson, ToJson)]
2353    struct Decorated {
2354        #[bourne(rename = "user-id")]
2355        user_id: u32,
2356        #[bourne(skip)]
2357        cached: u32,
2358        #[bourne(skip_if_none)]
2359        note: Option<String>,
2360        value: u32,
2361    }
2362
2363    #[test]
2364    fn rename_skip_skip_if_none() {
2365        let v = Decorated {
2366            user_id: 1,
2367            cached: 99,
2368            note: None,
2369            value: 42,
2370        };
2371        let s = to_string(&v).unwrap();
2372        assert_eq!(s, r#"{"user-id":1,"value":42}"#);
2373        let back: Decorated = parse_str(&s).unwrap();
2374        assert_eq!(back.user_id, 1);
2375        assert_eq!(back.value, 42);
2376    }
2377
2378    #[derive(Debug, PartialEq, FromJson, ToJson)]
2379    struct UserId(u64);
2380
2381    #[test]
2382    fn newtype_round_trips() {
2383        let v = UserId(42);
2384        let s = to_string(&v).unwrap();
2385        assert_eq!(s, "42");
2386        let back: UserId = parse_str(&s).unwrap();
2387        assert_eq!(back, v);
2388    }
2389
2390    #[derive(Debug, PartialEq, FromJson, ToJson)]
2391    struct Pair(i32, i32);
2392
2393    #[test]
2394    fn tuple_struct_round_trips() {
2395        let v = Pair(3, -7);
2396        let s = to_string(&v).unwrap();
2397        assert_eq!(s, "[3,-7]");
2398        let back: Pair = parse_str(&s).unwrap();
2399        assert_eq!(back, v);
2400    }
2401
2402    #[derive(Debug, PartialEq, FromJson, ToJson)]
2403    enum Shape {
2404        Circle,
2405        Wrapper(u32),
2406        Pair(u32, String),
2407        Box {
2408            w: u32,
2409            h: u32,
2410        },
2411        #[bourne(rename = "tri")]
2412        Triangle,
2413    }
2414
2415    #[test]
2416    fn externally_tagged_enum_round_trips() {
2417        let cases: Vec<(Shape, &str)> = vec![
2418            (Shape::Circle, r#""Circle""#),
2419            (Shape::Wrapper(7), r#"{"Wrapper":7}"#),
2420            (Shape::Pair(1, String::from("x")), r#"{"Pair":[1,"x"]}"#),
2421            (Shape::Box { w: 10, h: 20 }, r#"{"Box":{"w":10,"h":20}}"#),
2422            (Shape::Triangle, r#""tri""#),
2423        ];
2424        for (val, expected) in cases {
2425            let s = to_string(&val).unwrap();
2426            assert_eq!(s, expected);
2427            let back: Shape = parse_str(&s).unwrap();
2428            assert_eq!(back, val);
2429        }
2430    }
2431
2432    #[derive(Debug, PartialEq, FromJson, ToJson)]
2433    #[bourne(tag = "type")]
2434    enum Event {
2435        Heartbeat,
2436        #[bourne(rename = "click")]
2437        Click {
2438            x: u32,
2439            y: u32,
2440        },
2441    }
2442
2443    #[test]
2444    fn internally_tagged_enum_round_trips() {
2445        let hb = Event::Heartbeat;
2446        let s = to_string(&hb).unwrap();
2447        assert_eq!(s, r#"{"type":"Heartbeat"}"#);
2448        let back: Event = parse_str(&s).unwrap();
2449        assert_eq!(back, hb);
2450
2451        let click = Event::Click { x: 1, y: 2 };
2452        let s = to_string(&click).unwrap();
2453        assert_eq!(s, r#"{"type":"click","x":1,"y":2}"#);
2454        let back: Event = parse_str(&s).unwrap();
2455        assert_eq!(back, click);
2456    }
2457
2458    #[derive(Debug, PartialEq, FromJson, ToJson)]
2459    #[bourne(tag = "t", content = "c")]
2460    enum Msg {
2461        Ping,
2462        Echo(String),
2463        Pair(u32, u32),
2464        Body { text: String },
2465    }
2466
2467    #[test]
2468    fn adjacently_tagged_enum_round_trips() {
2469        let cases: Vec<(Msg, &str)> = vec![
2470            (Msg::Ping, r#"{"t":"Ping"}"#),
2471            (Msg::Echo(String::from("hi")), r#"{"t":"Echo","c":"hi"}"#),
2472            (Msg::Pair(1, 2), r#"{"t":"Pair","c":[1,2]}"#),
2473            (
2474                Msg::Body {
2475                    text: String::from("ok"),
2476                },
2477                r#"{"t":"Body","c":{"text":"ok"}}"#,
2478            ),
2479        ];
2480        for (val, expected) in cases {
2481            let s = to_string(&val).unwrap();
2482            assert_eq!(s, expected);
2483            let back: Msg = parse_str(&s).unwrap();
2484            assert_eq!(back, val);
2485        }
2486    }
2487
2488    #[derive(Debug, PartialEq, FromJson, ToJson)]
2489    #[bourne(untagged)]
2490    enum Mixed {
2491        Nothing,
2492        One(u32),
2493        Two(u32, u32),
2494        Body { name: String },
2495    }
2496
2497    #[derive(Debug, PartialEq, Eq, FromJson, ToJson)]
2498    pub struct PubFields {
2499        pub id: u32,
2500        pub(crate) name: String,
2501        value: u32,
2502    }
2503
2504    #[test]
2505    fn pub_fields_round_trip() {
2506        let v = PubFields {
2507            id: 1,
2508            name: String::from("hi"),
2509            value: 2,
2510        };
2511        let s = to_string(&v).unwrap();
2512        assert_eq!(s, r#"{"id":1,"name":"hi","value":2}"#);
2513        let back: PubFields = parse_str(&s).unwrap();
2514        assert_eq!(back, v);
2515    }
2516
2517    #[test]
2518    fn untagged_enum_serializes() {
2519        assert_eq!(to_string(&Mixed::Nothing).unwrap(), "null");
2520        assert_eq!(to_string(&Mixed::One(42)).unwrap(), "42");
2521        assert_eq!(to_string(&Mixed::Two(1, 2)).unwrap(), "[1,2]");
2522        assert_eq!(
2523            to_string(&Mixed::Body {
2524                name: String::from("x")
2525            })
2526            .unwrap(),
2527            r#"{"name":"x"}"#,
2528        );
2529    }
2530}
2531
2532/// Unsafe-boundary tests for the public surface — pinned at the safety
2533/// contracts of the `_unchecked` Vec-tail writers in `ByteSink` and the
2534/// `from_utf8_unchecked` site in `decode_escapes`. These are designed to
2535/// run under miri (CI: `MIRIFLAGS=-Zmiri-disable-isolation
2536/// RUSTFLAGS=--cfg bourne_no_simd cargo +nightly miri test -p json-bourne --lib`).
2537/// Each one targets a specific invariant; if a future refactor breaks the
2538/// caller-side capacity reservation or the UTF-8 boundary, miri here trips
2539/// on the precise unsafe before any user code does.
2540#[cfg(all(test, feature = "std"))]
2541mod unsafe_boundary_tests {
2542    use super::*;
2543    use alloc::string::String;
2544    use alloc::vec::Vec;
2545
2546    /// Slice-of-floats ser at the exact-capacity boundary. The slice
2547    /// writer's `reserve_hint` computes
2548    ///   `2 + n * (MAX_SERIALIZED_LEN + 1) = 2 + n * 33`.
2549    /// We pre-reserve exactly that, so the per-element
2550    /// `write_float_f64_taint` (which assumes ≥ 32 bytes headroom)
2551    /// runs against the tightest legal Vec capacity.
2552    #[test]
2553    fn bytesink_slice_floats_exact_capacity() {
2554        for n in [0usize, 1, 2, 3, 7, 16, 17, 32, 33] {
2555            #[allow(clippy::cast_precision_loss)]
2556            let data: Vec<f64> = (0..n).map(|i| i as f64 + 0.5).collect();
2557            let mut out: Vec<u8> = Vec::with_capacity(2 + n * 33);
2558            let mut sink = ByteSink::new(&mut out);
2559            (data.as_slice()).write_json(&mut sink).expect("ser");
2560            let s = core::str::from_utf8(&out).expect("ASCII");
2561            // Sanity: parse back as Vec<f64>.
2562            let back: Vec<f64> = parse_str(s).expect("parse back");
2563            assert_eq!(back.len(), n, "n={n}");
2564            #[allow(clippy::cast_precision_loss, clippy::float_cmp)]
2565            for (i, &v) in back.iter().enumerate() {
2566                assert_eq!(v, i as f64 + 0.5, "n={n} i={i}");
2567            }
2568        }
2569    }
2570
2571    /// Same as above but for `Vec<f32>`. f32 widens to f64 in the writer
2572    /// but uses the same taint path, so the capacity boundary is identical.
2573    #[test]
2574    fn bytesink_slice_f32_exact_capacity() {
2575        for n in [0usize, 1, 2, 16, 17] {
2576            #[allow(clippy::cast_precision_loss)]
2577            let data: Vec<f32> = (0..n).map(|i| i as f32 + 0.25).collect();
2578            let mut out: Vec<u8> = Vec::with_capacity(2 + n * 33);
2579            let mut sink = ByteSink::new(&mut out);
2580            (data.as_slice()).write_json(&mut sink).expect("ser");
2581            let s = core::str::from_utf8(&out).expect("ASCII");
2582            let back: Vec<f32> = parse_str(s).expect("parse back");
2583            assert_eq!(back.len(), n);
2584            #[allow(clippy::cast_precision_loss, clippy::float_cmp)]
2585            for (i, &v) in back.iter().enumerate() {
2586                assert_eq!(v, i as f32 + 0.25, "n={n} i={i}");
2587            }
2588        }
2589    }
2590
2591    /// Slice of f64 containing non-finite values — exercises the taint
2592    /// accumulator path and verifies the slice writer surfaces a
2593    /// `NonFiniteFloat` error rather than emitting garbage bytes.
2594    #[test]
2595    fn bytesink_slice_nonfinite_taint_path() {
2596        // The non-finite path writes substitute bytes into the Vec before
2597        // reporting the error. Miri ensures those substitute writes stay
2598        // within the reserved capacity.
2599        for (name, bad_idx, bad_val) in [
2600            ("inf at 0", 0usize, f64::INFINITY),
2601            ("nan at end", 4usize, f64::NAN),
2602            ("neg_inf middle", 2usize, f64::NEG_INFINITY),
2603        ] {
2604            let mut data: Vec<f64> = (0..5).map(f64::from).collect();
2605            data[bad_idx] = bad_val;
2606            let r = to_string(data.as_slice());
2607            assert!(r.is_err(), "{name}: expected non-finite error");
2608        }
2609    }
2610
2611    /// Slice of i64 — exercises `write_byte_unchecked` for both `[`/`,`/`]`
2612    /// at exact capacity. Integers don't use the float taint path but they
2613    /// do use `write_array_reserved`'s `write_byte_unchecked` for delimiters.
2614    #[test]
2615    fn bytesink_slice_ints_exact_capacity() {
2616        // MAX_SERIALIZED_LEN for i64 = 20 ("-9223372036854775808"), so
2617        // hint = 2 + n * 21.
2618        for n in [0usize, 1, 5, 16, 17] {
2619            #[allow(clippy::cast_possible_wrap)]
2620            let n_i64 = n as i64;
2621            #[allow(clippy::cast_possible_wrap)]
2622            let data: Vec<i64> = (0..n).map(|i| (i as i64) - n_i64 / 2).collect();
2623            let mut out: Vec<u8> = Vec::with_capacity(2 + n * 21);
2624            let mut sink = ByteSink::new(&mut out);
2625            (data.as_slice()).write_json(&mut sink).expect("ser");
2626            let s = core::str::from_utf8(&out).expect("ASCII");
2627            let back: Vec<i64> = parse_str(s).expect("parse back");
2628            assert_eq!(back, data, "n={n}");
2629        }
2630    }
2631
2632    /// `decode_escapes`' literal-byte run uses `from_utf8_unchecked` on
2633    /// the stretch between escapes. The lexer's UTF-8 invariant must hold
2634    /// across multi-byte sequences. Test escapes interleaved with 2/3/4-byte
2635    /// UTF-8 chars so the literal chunk passed to the unsafe spans
2636    /// multibyte data.
2637    #[test]
2638    fn decode_escapes_multibyte_in_literal_chunk() {
2639        // 2-byte (é = c3 a9), 3-byte (€ = e2 82 ac), 4-byte (𝄞 = f0 9d 84 9e)
2640        // chars surrounding `\n` escapes. The literal chunks bracket the
2641        // escape and must contain valid UTF-8.
2642        let cases: &[(&str, &str)] = &[
2643            (r#""café\nfin""#, "café\nfin"),
2644            (r#""price: 5€\tea""#, "price: 5€\tea"),
2645            (r#""note 𝄞\nplayed""#, "note 𝄞\nplayed"),
2646            // Many escapes between multibyte stretches.
2647            (r#""éé\nçç\tüü""#, "éé\nçç\tüü"),
2648            // Escape at start / end with multibyte in middle.
2649            (r#""\n𝄞café\t""#, "\n𝄞café\t"),
2650            // Long literal run before single escape (exercises the SIMD
2651            // scan tail).
2652            (
2653                r#""aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaéé\n""#,
2654                "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaéé\n",
2655            ),
2656        ];
2657        for (json, expected) in cases {
2658            let s: String = parse_str(json).expect("parse");
2659            assert_eq!(s, *expected, "case {json}");
2660        }
2661    }
2662
2663    /// Empty slice / array — `write_array_reserved`'s `split_first` is None,
2664    /// no `write_byte_unchecked` for elements. Just `[` and `]`.
2665    #[test]
2666    fn bytesink_empty_slice_writes_brackets_only() {
2667        let empty: Vec<i64> = Vec::new();
2668        let s = to_string(empty.as_slice()).expect("ser");
2669        assert_eq!(s, "[]");
2670
2671        let empty: Vec<f64> = Vec::new();
2672        let s = to_string(empty.as_slice()).expect("ser");
2673        assert_eq!(s, "[]");
2674    }
2675
2676    /// Single-element slice — `write_array_reserved`'s `split_first` returns
2677    /// `(first, [])` so we skip the inner loop. Verify the byte boundary.
2678    #[test]
2679    fn bytesink_single_element_slice() {
2680        let one = [42_i64];
2681        assert_eq!(to_string(one.as_slice()).unwrap(), "[42]");
2682        let one = [1.5_f64];
2683        assert_eq!(to_string(one.as_slice()).unwrap(), "[1.5]");
2684        let one = [1.5_f32];
2685        assert_eq!(to_string(one.as_slice()).unwrap(), "[1.5]");
2686    }
2687
2688    /// Empty string — `decode_escapes` is called with `raw=&[]`. The
2689    /// `from_utf8_unchecked(&raw[start..i])` slice is empty, which is
2690    /// a degenerate edge case that must not OOB.
2691    #[test]
2692    fn decode_escapes_empty_string() {
2693        let s: String = parse_str(r#""""#).expect("parse");
2694        assert_eq!(s, "");
2695    }
2696
2697    /// Pure-ASCII without any escape — the literal-byte run covers the
2698    /// whole input and `find_backslash` returns None. The unsafe slice
2699    /// is `&raw[0..raw.len()]`.
2700    #[test]
2701    fn decode_escapes_no_escapes_pure_ascii() {
2702        // Use `Vec<String>` to force the owned-decode path even though
2703        // the input has no escapes (the `&str` impl would borrow).
2704        let v: Vec<String> = parse_str(r#"["hello world","no escapes here"]"#).expect("parse");
2705        assert_eq!(
2706            v,
2707            [String::from("hello world"), String::from("no escapes here")]
2708        );
2709    }
2710
2711    /// Decode an escape immediately at the start — the first literal-byte
2712    /// run is empty, so `from_utf8_unchecked(&raw[0..0])` is called.
2713    #[test]
2714    fn decode_escapes_escape_at_start() {
2715        let v: Vec<String> = parse_str(r#"["\nhello","\tworld"]"#).expect("parse");
2716        assert_eq!(v, [String::from("\nhello"), String::from("\tworld")]);
2717    }
2718
2719    /// Decode an escape immediately at the end — after the escape there is
2720    /// no literal-byte run.
2721    #[test]
2722    fn decode_escapes_escape_at_end() {
2723        let v: Vec<String> = parse_str(r#"["hello\n","world\t"]"#).expect("parse");
2724        assert_eq!(v, [String::from("hello\n"), String::from("world\t")]);
2725    }
2726
2727    /// Back-to-back escapes — multiple zero-length literal chunks.
2728    #[test]
2729    fn decode_escapes_consecutive_escapes() {
2730        let v: Vec<String> = parse_str(r#"["\n\t\r\\","\"\"\""]"#).expect("parse");
2731        assert_eq!(v, [String::from("\n\t\r\\"), String::from(r#"""""#)]);
2732    }
2733
2734    // -----------------------------------------------------------------
2735    // Vec<T>::vec_from_lex fast-path coverage. The default
2736    // `FromJson::vec_from_lex` impl drives `from_lex` per element; the
2737    // primitive types (`&str`, integers via macros, `Duration`) override
2738    // it to skip the per-element Event detour. These overrides have
2739    // independent code paths from the default and need their own tests.
2740    // -----------------------------------------------------------------
2741
2742    /// `Vec<&str>::vec_from_lex` — borrowed strings, fused fast path.
2743    #[test]
2744    fn vec_borrowed_str_fast_path() {
2745        // Empty array — early-return branch.
2746        let v: Vec<&str> = parse_str("[]").expect("empty");
2747        assert_eq!(v, Vec::<&str>::new());
2748
2749        // Single element — first push only, no while loop.
2750        let v: Vec<&str> = parse_str(r#"["only"]"#).expect("single");
2751        assert_eq!(v, ["only"]);
2752
2753        // Many elements — exercises the while-loop body.
2754        let v: Vec<&str> = parse_str(r#"["a","b","c","d","e"]"#).expect("many");
2755        assert_eq!(v, ["a", "b", "c", "d", "e"]);
2756
2757        // Reject escape-bearing string (the borrowed path requires no escapes).
2758        let r: Result<Vec<&str>, _> = parse_str(r#"["plain","esc\nbad"]"#);
2759        assert!(r.is_err(), "borrowed path must reject escape");
2760    }
2761
2762    /// `Vec<Duration>::vec_from_lex` — fused, with non-finite / negative rejection.
2763    #[cfg(feature = "std")]
2764    #[test]
2765    fn vec_duration_fast_path() {
2766        use std::time::Duration;
2767
2768        // Empty — early return.
2769        let v: Vec<Duration> = parse_str("[]").expect("empty");
2770        assert!(v.is_empty());
2771
2772        // Single — first push.
2773        let v: Vec<Duration> = parse_str("[1.5]").expect("single");
2774        assert_eq!(v, [Duration::from_secs_f64(1.5)]);
2775
2776        // Many — loop body.
2777        let v: Vec<Duration> = parse_str("[0.0,1.0,1.5,2.25,100.125]").expect("many");
2778        assert_eq!(v.len(), 5);
2779        assert_eq!(v[0], Duration::ZERO);
2780        assert_eq!(v[4], Duration::from_secs_f64(100.125));
2781
2782        // Negative — rejected.
2783        let r: Result<Vec<Duration>, _> = parse_str("[1.0,-2.0]");
2784        assert!(r.is_err());
2785    }
2786
2787    /// `Vec<i64>::vec_from_lex` — exercises the macro-generated override
2788    /// in `de.rs::impl_int!`, including the bounds check on the empty branch.
2789    #[test]
2790    fn vec_i64_fast_path() {
2791        let v: Vec<i64> = parse_str("[]").expect("empty");
2792        assert!(v.is_empty());
2793
2794        let v: Vec<i64> = parse_str("[42]").expect("single");
2795        assert_eq!(v, [42]);
2796
2797        let v: Vec<i64> =
2798            parse_str("[1,-1,9223372036854775807,-9223372036854775808]").expect("many");
2799        assert_eq!(v, [1, -1, i64::MAX, i64::MIN]);
2800
2801        // Out-of-range for u32 should error.
2802        let r: Result<Vec<u32>, _> = parse_str("[1,99999999999]");
2803        assert!(r.is_err(), "u32 should overflow");
2804    }
2805
2806    /// `Vec<u128>::vec_from_lex` — wide-int macro path.
2807    #[test]
2808    fn vec_u128_fast_path() {
2809        let v: Vec<u128> = parse_str("[]").expect("empty");
2810        assert!(v.is_empty());
2811
2812        let v: Vec<u128> = parse_str("[0,170141183460469231731687303715884105727]").expect("many");
2813        assert_eq!(v, [0u128, i128::MAX as u128]);
2814    }
2815
2816    // -----------------------------------------------------------------
2817    // `decode_escapes` — full branch coverage. The match arms for `\b`,
2818    // `\f`, `\/`, and the various surrogate-pair error paths weren't
2819    // exercised by the existing tests.
2820    // -----------------------------------------------------------------
2821
2822    #[test]
2823    fn decode_all_simple_escapes() {
2824        // Each backslash escape variant — covers every match arm in
2825        // `decode_escapes`.
2826        let cases: &[(&str, &str)] = &[
2827            (r#""\b""#, "\u{0008}"), // backspace
2828            (r#""\f""#, "\u{000C}"), // form feed
2829            (r#""\/""#, "/"),        // solidus
2830            (r#""\\""#, "\\"),       // backslash
2831            (r#""\"""#, "\""),       // quote
2832            (r#""\n""#, "\n"),
2833            (r#""\r""#, "\r"),
2834            (r#""\t""#, "\t"),
2835        ];
2836        for &(json, expected) in cases {
2837            let s: String = parse_str(json).expect(json);
2838            assert_eq!(s, expected, "case {json}");
2839        }
2840    }
2841
2842    #[test]
2843    fn decode_unknown_escape_errors() {
2844        // `\x` is not a recognized escape — last match arm.
2845        let r: Result<String, _> = parse_str(r#""\x""#);
2846        assert!(r.is_err(), "unknown escape should error");
2847
2848        // Backslash at end-of-input — `i >= raw.len()` branch.
2849        let r: Result<String, _> = parse_str("\"\\\"");
2850        assert!(r.is_err(), "lone trailing backslash should error");
2851    }
2852
2853    #[test]
2854    fn decode_unicode_escape_short_input_errors() {
2855        // `\u` followed by < 4 hex digits — `i + 5 > raw.len()` branch.
2856        let r: Result<String, _> = parse_str(r#""\u00""#);
2857        assert!(r.is_err(), "short \\u should error");
2858        let r: Result<String, _> = parse_str(r#""\u""#);
2859        assert!(r.is_err());
2860    }
2861
2862    #[test]
2863    fn decode_unicode_lone_low_surrogate_errors() {
2864        // `\uDC00` standalone is a lone low surrogate — second
2865        // `0xDC00..=0xDFFF` branch.
2866        let r: Result<String, _> = parse_str(r#""\uDC00""#);
2867        assert!(r.is_err(), "lone low surrogate should error");
2868    }
2869
2870    #[test]
2871    fn decode_unicode_high_surrogate_then_invalid_low_errors() {
2872        // High surrogate \uD800 followed by another `\u` escape whose
2873        // codepoint is OUTSIDE the low-surrogate range — exercises the
2874        // explicit range-check arm (raw[i+1]==`\\` AND raw[i+2]==`u` but
2875        // the parsed low_value isn't a low surrogate).
2876        let json = "\"\\uD800\\u0041\""; // high then 'A' as A
2877        let r: Result<String, _> = parse_str(json);
2878        assert!(
2879            r.is_err(),
2880            "high surrogate then non-low-surrogate \\u must error"
2881        );
2882    }
2883
2884    /// High surrogate followed by `\uXXXX` where XXXX is a valid low
2885    /// surrogate, but the four hex digits are at end-of-input. Covers
2886    /// the `i + 7 > raw.len()` short-buffer guard.
2887    #[test]
2888    fn decode_unicode_high_surrogate_then_short_second_escape_errors() {
2889        let json = r#""\uD800\u""#;
2890        let r: Result<String, _> = parse_str(json);
2891        assert!(r.is_err(), "high surrogate then truncated \\u must error");
2892    }
2893
2894    /// Invalid hex inside the second \u of a surrogate pair.
2895    #[test]
2896    fn decode_unicode_high_surrogate_then_invalid_hex_errors() {
2897        let json = "\"\\uD800\\uZZZZ\"";
2898        let r: Result<String, _> = parse_str(json);
2899        assert!(r.is_err(), "high surrogate then bad hex must error");
2900    }
2901
2902    #[test]
2903    fn decode_unicode_high_surrogate_then_non_u_escape_errors() {
2904        // `\uD800` followed by `\n` (not a `\u` escape).
2905        let r: Result<String, _> = parse_str(r#""\uD800\n""#);
2906        assert!(
2907            r.is_err(),
2908            "high surrogate not followed by \\u should error"
2909        );
2910
2911        // `\uD800` followed by non-backslash byte (EOF or literal).
2912        let r: Result<String, _> = parse_str(r#""\uD800A""#);
2913        assert!(
2914            r.is_err(),
2915            "high surrogate not followed by escape should error"
2916        );
2917    }
2918
2919    #[test]
2920    fn decode_unicode_surrogate_pair_round_trips() {
2921        // Valid surrogate pair for U+1F600 (GRINNING FACE): high=D83D
2922        // low=DE00. Encoded as `\u` escapes (not the literal emoji) so
2923        // decode_escapes' surrogate arm runs — literal multi-byte UTF-8
2924        // goes through the lexer's `consume_utf8_multibyte` instead.
2925        let json = "\"\\uD83D\\uDE00\"";
2926        let s: String = parse_str(json).expect("parse");
2927        assert_eq!(s, "\u{1F600}");
2928
2929        // Surrogate pair adjacent to literal ASCII / other escapes.
2930        let json = "\"a\\uD83D\\uDE00b\\uD83D\\uDE01c\"";
2931        let s: String = parse_str(json).expect("parse");
2932        assert_eq!(s, "a\u{1F600}b\u{1F601}c");
2933    }
2934
2935    #[test]
2936    fn decode_unicode_bmp_escape_round_trips() {
2937        // Standard BMP unicode escape — non-surrogate path. U+00E9 ('é')
2938        // encoded as `é` so decode_escapes' \u arm runs (a literal
2939        // "é" goes through consume_utf8_multibyte instead).
2940        let json = "\"\\u00E9\"";
2941        let s: String = parse_str(json).expect("parse");
2942        assert_eq!(s, "é");
2943
2944        // BMP escapes at various code points.
2945        let json = "\"A\\u00A3\\u20AC\""; // A, £, €
2946        let s: String = parse_str(json).expect("parse");
2947        assert_eq!(s, "A£€");
2948    }
2949
2950    #[test]
2951    fn decode_invalid_hex_in_u_escape_errors() {
2952        // `\u00ZX` — invalid hex.
2953        let r: Result<String, _> = parse_str(r#""\u00ZX""#);
2954        assert!(r.is_err(), "invalid hex should error");
2955    }
2956
2957    // -----------------------------------------------------------------
2958    // `to_decimal_uncentred` — the float path for powers of 2 (mantissa
2959    // == 1 << 52). Hit when the IEEE 754 mantissa lands exactly on the
2960    // uncentred boundary. These values are rare in random samples, so
2961    // need explicit tests.
2962    // -----------------------------------------------------------------
2963
2964    /// Powers of 2 hit the uncentred decompose path. Each gets a
2965    /// shortest-roundtrip rendering through `to_decimal_uncentred`.
2966    #[test]
2967    fn to_decimal_uncentred_powers_of_two_round_trip() {
2968        // f64 values where mantissa == 1 << 52 and exponent != EXPONENT_MIN:
2969        // these are 2.0, 4.0, 8.0, 16.0, ..., up to 2^1023.
2970        let cases = [
2971            2.0_f64,
2972            4.0,
2973            8.0,
2974            16.0,
2975            32.0,
2976            64.0,
2977            128.0,
2978            256.0,
2979            512.0,
2980            1024.0,
2981            2.0_f64.powi(20),
2982            2.0_f64.powi(50),
2983            2.0_f64.powi(100),
2984            2.0_f64.powi(500),
2985            2.0_f64.powi(1023), // largest finite power of 2
2986            // Negative powers of 2 — half exponent.
2987            2.0_f64.powi(-1), // 0.5
2988            2.0_f64.powi(-2), // 0.25
2989            2.0_f64.powi(-10),
2990            2.0_f64.powi(-50),
2991            2.0_f64.powi(-100),
2992            2.0_f64.powi(-1000),
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    /// Negative powers of 2 — sign path through `to_decimal_uncentred`.
3005    #[test]
3006    fn to_decimal_uncentred_negative_powers_round_trip() {
3007        for k in [-30, -10, -1, 1, 10, 30, 100, 500] {
3008            let v = -(2.0_f64.powi(k));
3009            let s = to_string(&v).expect("ser");
3010            let back: f64 = parse_str(&s).expect("parse back");
3011            #[allow(clippy::float_cmp)]
3012            {
3013                assert_eq!(back, v, "round-trip for {v:e}: emitted {s:?}");
3014            }
3015        }
3016    }
3017
3018    /// Subnormal f64 — minimum positive denormal, uses uncentred path
3019    /// at a different boundary.
3020    #[test]
3021    fn to_decimal_uncentred_subnormals_round_trip() {
3022        let cases = [
3023            5e-324_f64,        // smallest subnormal
3024            f64::MIN_POSITIVE, // smallest normal
3025            -f64::MIN_POSITIVE,
3026            f64::MAX,
3027            -f64::MAX,
3028        ];
3029        for &v in &cases {
3030            let s = to_string(&v).expect("ser");
3031            let back: f64 = parse_str(&s).expect("parse back");
3032            #[allow(clippy::float_cmp)]
3033            {
3034                assert_eq!(back, v, "round-trip for {v:e}: emitted {s:?}");
3035            }
3036        }
3037    }
3038
3039    // -----------------------------------------------------------------
3040    // Direct-sink coverage. Every public sink type (StringSink,
3041    // PrettyStringSink, ByteSink unchecked methods) needs at least one
3042    // call so the CRAP gate's zero-coverage trigger doesn't fire.
3043    // -----------------------------------------------------------------
3044
3045    /// `StringSink` — the `&mut String` sink. Exercises `write_byte`,
3046    /// `write_str_raw`, and `write_float_f64` directly.
3047    #[test]
3048    fn string_sink_writes_directly() {
3049        let mut out = String::new();
3050        let mut sink = StringSink::new(&mut out);
3051        // write_byte and write_str_raw via the trait
3052        sink.write_byte(b'[').unwrap();
3053        sink.write_str_raw("1").unwrap();
3054        sink.write_byte(b',').unwrap();
3055        // write_float_f64 (StringSink override)
3056        sink.write_float_f64(2.5).unwrap();
3057        sink.write_byte(b']').unwrap();
3058        assert_eq!(out, "[1,2.5]");
3059
3060        // Non-finite rejection through the StringSink path.
3061        let mut out = String::new();
3062        let mut sink = StringSink::new(&mut out);
3063        assert!(sink.write_float_f64(f64::NAN).is_err());
3064    }
3065
3066    /// `PrettyStringSink::with_indent` — custom indent variant.
3067    /// Existing pretty tests only cover the default-indent constructor.
3068    #[test]
3069    fn pretty_sink_with_indent_uses_custom_indent() {
3070        let mut out = String::new();
3071        let mut sink = PrettyStringSink::with_indent(&mut out, "\t");
3072        let data = (1_i32, 2_i32, 3_i32);
3073        data.write_json(&mut sink).unwrap();
3074        // Tab indent → contains a `\n\t` sequence.
3075        assert!(out.contains("\n\t"), "got: {out:?}");
3076    }
3077
3078    /// `PrettyStringSink::write_float_f64` — covers the pretty-sink
3079    /// float arm (existing tests use the simple-byte and string paths).
3080    #[test]
3081    fn pretty_sink_handles_floats() {
3082        let v: alloc::vec::Vec<f64> = alloc::vec![1.5, 2.5, 3.0];
3083        let s = to_string_pretty(v.as_slice()).unwrap();
3084        assert!(s.contains("1.5"));
3085        assert!(s.contains("3.0"));
3086    }
3087
3088    /// `ByteSink::write_float_f64_unchecked` — non-finite triggers the
3089    /// outlined `cold_nonfinite_byte_sink` error. Exercises the cold path.
3090    #[test]
3091    fn bytesink_unchecked_float_nonfinite_returns_error() {
3092        let mut out: Vec<u8> = Vec::with_capacity(64);
3093        let mut sink = ByteSink::new(&mut out);
3094        // SAFETY: ample reserved capacity (≥32 bytes).
3095        #[allow(unsafe_code)]
3096        let r = unsafe { sink.write_float_f64_unchecked(f64::INFINITY) };
3097        assert!(r.is_err(), "non-finite must error through cold arm");
3098    }
3099
3100    /// `ByteSink::write_float_f64_unchecked_finite` — direct call with
3101    /// a finite value. The slice fast path calls this internally.
3102    #[test]
3103    fn bytesink_unchecked_finite_writes_value() {
3104        let mut out: Vec<u8> = Vec::with_capacity(64);
3105        let mut sink = ByteSink::new(&mut out);
3106        // Pick a finite value that round-trips exactly through the formatter
3107        // and `f64::parse`. 2.5 is exact in binary; avoiding 3.14 also dodges
3108        // clippy's approx_constant warning about PI.
3109        let value = 2.5_f64;
3110        // SAFETY: reserved 64 bytes, value is finite.
3111        #[allow(unsafe_code)]
3112        unsafe {
3113            sink.write_float_f64_unchecked_finite(value).unwrap();
3114        }
3115        let s = core::str::from_utf8(&out).unwrap();
3116        let parsed: f64 = s.parse().unwrap();
3117        // Bit-pattern compare: write→parse must round-trip exactly.
3118        assert_eq!(parsed.to_bits(), value.to_bits());
3119    }
3120
3121    /// `ToJson for Path` (std-only) — covers `Path::write_json`.
3122    #[cfg(feature = "std")]
3123    #[test]
3124    fn path_serializes_via_to_string() {
3125        use std::path::Path;
3126        let p = Path::new("/tmp/file.txt");
3127        let s = to_string(p).unwrap();
3128        assert_eq!(s, "\"/tmp/file.txt\"");
3129    }
3130
3131    /// `MapKeyOut for Cow<'_, str>` — used when a HashMap/BTreeMap
3132    /// key type is `Cow<'_, str>`. Exercises `Cow::as_str`.
3133    #[test]
3134    fn map_with_cow_str_keys_serializes() {
3135        use alloc::borrow::Cow;
3136        use alloc::collections::BTreeMap;
3137        let mut m: BTreeMap<Cow<'_, str>, i32> = BTreeMap::new();
3138        m.insert(Cow::Borrowed("a"), 1);
3139        m.insert(Cow::Owned(String::from("b")), 2);
3140        let s = to_string(&m).unwrap();
3141        assert!(s.contains(r#""a":1"#));
3142        assert!(s.contains(r#""b":2"#));
3143    }
3144
3145    /// `f64::pre_validate_slice` — the post-taint stub returns Ok(()).
3146    /// Direct call to ensure the function is exercised.
3147    #[test]
3148    fn f64_pre_validate_slice_is_noop() {
3149        let slice: &[f64] = &[1.0, 2.0, f64::INFINITY];
3150        // The fn body just returns Ok; calling it through the trait
3151        // exercises both the dispatch and the body.
3152        let r = <f64 as ToJson>::pre_validate_slice(slice);
3153        assert!(r.is_ok());
3154    }
3155
3156    /// `crate::ser::float::format_f64_write` and `reject_non_finite`
3157    /// are exercised by `StringSink::write_float_f64` (via the `float::`
3158    /// module). Existing tests cover that, but cover them explicitly
3159    /// for the non-finite branch through the public `to_fmt` entry
3160    /// point which uses `FmtWriteSink` (a different code path).
3161    #[test]
3162    fn to_fmt_writes_finite_and_rejects_nonfinite() {
3163        let mut s = String::new();
3164        to_fmt(&1.5_f64, &mut s).unwrap();
3165        assert_eq!(s, "1.5");
3166
3167        let mut s = String::new();
3168        assert!(to_fmt(&f64::INFINITY, &mut s).is_err());
3169    }
3170
3171    /// `fmt_write_error` is invoked when the underlying `core::fmt::Write`
3172    /// impl returns an error. Construct a writer that always fails and
3173    /// verify `to_fmt` propagates a typed `Error` (rather than the
3174    /// detail-less `fmt::Error`).
3175    #[test]
3176    fn to_fmt_propagates_underlying_write_failure() {
3177        use core::fmt;
3178
3179        /// Writer that errors on every call — drives `fmt_write_error`.
3180        struct AlwaysFail;
3181        impl fmt::Write for AlwaysFail {
3182            fn write_str(&mut self, _s: &str) -> fmt::Result {
3183                Err(fmt::Error)
3184            }
3185        }
3186
3187        // Any non-empty serializable value flushes at least one byte
3188        // through `write_str` / `write_char`, which the writer rejects.
3189        let mut sink = AlwaysFail;
3190        let r = to_fmt(&42_i64, &mut sink);
3191        assert!(r.is_err(), "expected propagated error from failing writer");
3192
3193        // Also exercise the float path through FmtWriteSink, which uses
3194        // a separate `fmt_write_error()` call site.
3195        let mut sink = AlwaysFail;
3196        let r = to_fmt(&1.5_f64, &mut sink);
3197        assert!(r.is_err());
3198    }
3199}