Skip to main content

reliakit_json/
lib.rs

1//! Strict, bounded, and deterministic JSON for reliability-sensitive Rust.
2//!
3//! `reliakit-json` is built for systems that process **untrusted** JSON or need
4//! **predictable** output: it parses a strict subset of [RFC 8259], rejects
5//! duplicate object keys, enforces explicit [resource limits](JsonLimits),
6//! preserves number precision, reports errors with location and path, and
7//! serializes deterministically. It has no external dependencies, forbids
8//! unsafe code, and supports `no_std` (with `alloc`).
9//!
10//! It deliberately does **not** provide derive macros, schema validation,
11//! JSON5, comments, trailing commas, lenient parsing, or SIMD throughput.
12//!
13//! # Example
14//!
15//! ```
16//! use reliakit_json::{parse_str, to_compact_string};
17//!
18//! let value = parse_str(r#"{"name":"reliakit","ok":true}"#).unwrap();
19//! assert_eq!(value.as_object().unwrap().get("name").unwrap().as_str(), Some("reliakit"));
20//!
21//! // Serialization is deterministic and preserves member order.
22//! assert_eq!(to_compact_string(&value), r#"{"name":"reliakit","ok":true}"#);
23//!
24//! // Strict by default: duplicate keys are rejected, not silently resolved.
25//! assert!(parse_str(r#"{"a":1,"a":2}"#).is_err());
26//! ```
27//!
28//! # Limits
29//!
30//! [`parse`] applies conservative [`JsonLimits`] by default. Use
31//! [`parse_with_limits`] to choose a profile or tune individual limits:
32//!
33//! ```
34//! use reliakit_json::{parse_with_limits, JsonLimits};
35//!
36//! let limits = JsonLimits::conservative().with_max_depth(8);
37//! assert!(parse_with_limits(b"[[[[[[[[[[1]]]]]]]]]]", limits).is_err());
38//! ```
39//!
40//! [RFC 8259]: https://www.rfc-editor.org/rfc/rfc8259
41
42#![cfg_attr(not(feature = "std"), no_std)]
43#![forbid(unsafe_code)]
44#![warn(missing_docs)]
45
46extern crate alloc;
47
48mod error;
49mod limits;
50mod number;
51mod parse;
52mod value;
53mod write;
54
55pub use error::{
56    JsonError, JsonErrorKind, JsonLimitKind, JsonNumberError, JsonPath, JsonPathSegment,
57};
58pub use limits::JsonLimits;
59pub use number::JsonNumber;
60pub use parse::{parse, parse_str, parse_with_limits};
61pub use value::{JsonMember, JsonObject, JsonValue};
62pub use write::{to_compact_string, to_compact_vec};
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67    use alloc::string::{String, ToString};
68
69    fn parse_ok(input: &str) -> JsonValue {
70        parse_str(input).expect("should parse")
71    }
72
73    fn kind(input: &str) -> JsonErrorKind {
74        parse_str(input).expect_err("should fail").kind().clone()
75    }
76
77    // ---- scalars ----------------------------------------------------------
78
79    #[test]
80    fn parses_scalars() {
81        assert_eq!(parse_ok("null"), JsonValue::Null);
82        assert_eq!(parse_ok("true"), JsonValue::Bool(true));
83        assert_eq!(parse_ok("false"), JsonValue::Bool(false));
84        assert_eq!(parse_ok("\"hi\"").as_str(), Some("hi"));
85        assert_eq!(parse_ok("42").as_number().unwrap().to_i64().unwrap(), 42);
86    }
87
88    #[test]
89    fn whitespace_is_allowed_around_values() {
90        assert_eq!(parse_ok("  \t\r\n 7 \n").as_number().unwrap().as_str(), "7");
91    }
92
93    #[test]
94    fn only_json_whitespace_is_accepted() {
95        // A vertical tab (U+000B) is not JSON whitespace.
96        assert_eq!(kind("\u{0B}1"), JsonErrorKind::UnexpectedByte);
97    }
98
99    // ---- structure --------------------------------------------------------
100
101    #[test]
102    fn parses_object_and_array() {
103        let value = parse_ok(r#"{"a":[1,2,3],"b":{"c":null}}"#);
104        let obj = value.as_object().unwrap();
105        assert_eq!(obj.len(), 2);
106        assert_eq!(obj.get("a").unwrap().as_array().unwrap().len(), 3);
107        assert!(obj
108            .get("b")
109            .unwrap()
110            .as_object()
111            .unwrap()
112            .get("c")
113            .unwrap()
114            .is_null());
115    }
116
117    #[test]
118    fn empty_containers() {
119        assert_eq!(parse_ok("[]").as_array().unwrap().len(), 0);
120        assert_eq!(parse_ok("{}").as_object().unwrap().len(), 0);
121    }
122
123    // ---- required rejections ---------------------------------------------
124
125    #[test]
126    fn rejects_trailing_data() {
127        assert_eq!(kind("1 2"), JsonErrorKind::TrailingData);
128        assert_eq!(kind("{} x"), JsonErrorKind::TrailingData);
129    }
130
131    #[test]
132    fn rejects_comments_and_trailing_commas() {
133        assert_eq!(kind("1 // c"), JsonErrorKind::TrailingData);
134        assert_eq!(kind("[1,]"), JsonErrorKind::UnexpectedByte);
135        assert_eq!(kind(r#"{"a":1,}"#), JsonErrorKind::UnexpectedByte);
136    }
137
138    #[test]
139    fn rejects_bad_numbers() {
140        for bad in ["01", "1.", "-", "1e", "1e+", "00", "1.2.3"] {
141            assert_eq!(kind(bad), JsonErrorKind::InvalidNumber, "input {bad:?}");
142        }
143        // Also rejected, with their own correct kinds (no valid value starts
144        // with '.' or '+'; "0x1" parses "0" then chokes on the trailing "x1").
145        assert_eq!(kind(".5"), JsonErrorKind::UnexpectedByte);
146        assert_eq!(kind("+1"), JsonErrorKind::UnexpectedByte);
147        assert_eq!(kind("0x1"), JsonErrorKind::TrailingData);
148    }
149
150    #[test]
151    fn rejects_nan_and_infinity() {
152        assert_eq!(kind("NaN"), JsonErrorKind::UnexpectedByte);
153        assert_eq!(kind("Infinity"), JsonErrorKind::UnexpectedByte);
154        assert_eq!(kind("-Infinity"), JsonErrorKind::InvalidNumber);
155    }
156
157    #[test]
158    fn rejects_unescaped_control_and_bad_escapes() {
159        assert_eq!(kind("\"\u{01}\""), JsonErrorKind::UnescapedControlCharacter);
160        assert_eq!(kind(r#""\x""#), JsonErrorKind::InvalidEscape);
161        assert_eq!(kind(r#""\u00""#), JsonErrorKind::InvalidUnicodeEscape);
162    }
163
164    #[test]
165    fn rejects_lone_surrogates() {
166        assert_eq!(kind(r#""\uD800""#), JsonErrorKind::LoneSurrogate);
167        assert_eq!(kind(r#""\uDC00""#), JsonErrorKind::LoneSurrogate);
168        assert_eq!(kind(r#""\uD800a""#), JsonErrorKind::LoneSurrogate);
169    }
170
171    #[test]
172    fn accepts_valid_surrogate_pair() {
173        assert_eq!(parse_ok(r#""𝄞""#).as_str(), Some("\u{1D11E}"));
174    }
175
176    #[test]
177    fn rejects_invalid_utf8_and_bom() {
178        assert_eq!(
179            parse(&[0xff]).unwrap_err().kind().clone(),
180            JsonErrorKind::InvalidUtf8
181        );
182        assert_eq!(
183            parse(&[0xEF, 0xBB, 0xBF, b'1']).unwrap_err().kind().clone(),
184            JsonErrorKind::InvalidUtf8
185        );
186    }
187
188    // ---- string semantics -------------------------------------------------
189
190    #[test]
191    fn escape_and_literal_decode_equally() {
192        assert_eq!(parse_ok(r#""a""#), parse_ok(r#""a""#));
193    }
194
195    #[test]
196    fn decodes_named_escapes() {
197        assert_eq!(
198            parse_ok(r#""\n\t\r\b\f\"\\\/""#).as_str(),
199            Some("\n\t\r\u{08}\u{0C}\"\\/")
200        );
201    }
202
203    // ---- duplicate keys ---------------------------------------------------
204
205    #[test]
206    fn rejects_duplicate_keys() {
207        assert_eq!(kind(r#"{"a":1,"a":2}"#), JsonErrorKind::DuplicateKey);
208    }
209
210    #[test]
211    fn duplicate_detection_is_after_escape_decoding() {
212        assert_eq!(
213            kind(r#"{"role":"user","role":"admin"}"#),
214            JsonErrorKind::DuplicateKey
215        );
216    }
217
218    // ---- limits -----------------------------------------------------------
219
220    #[test]
221    fn enforces_depth_limit() {
222        let limits = JsonLimits::new().with_max_depth(3);
223        assert!(parse_with_limits(b"[[[1]]]", limits).is_ok());
224        assert_eq!(
225            parse_with_limits(b"[[[[1]]]]", limits)
226                .unwrap_err()
227                .kind()
228                .clone(),
229            JsonErrorKind::LimitExceeded(JsonLimitKind::Depth)
230        );
231    }
232
233    #[test]
234    fn enforces_count_limits() {
235        let limits = JsonLimits::new();
236        let limits = JsonLimits {
237            max_array_items: 2,
238            max_object_members: 2,
239            max_total_nodes: 100,
240            ..limits
241        };
242        assert_eq!(
243            parse_with_limits(b"[1,2,3]", limits)
244                .unwrap_err()
245                .kind()
246                .clone(),
247            JsonErrorKind::LimitExceeded(JsonLimitKind::ArrayItems)
248        );
249        assert_eq!(
250            parse_with_limits(br#"{"a":1,"b":2,"c":3}"#, limits)
251                .unwrap_err()
252                .kind()
253                .clone(),
254            JsonErrorKind::LimitExceeded(JsonLimitKind::ObjectMembers)
255        );
256    }
257
258    #[test]
259    fn enforces_total_nodes_and_input_bytes() {
260        let nodes = JsonLimits::new().with_max_total_nodes(2);
261        assert_eq!(
262            parse_with_limits(b"[1,2]", nodes)
263                .unwrap_err()
264                .kind()
265                .clone(),
266            JsonErrorKind::LimitExceeded(JsonLimitKind::TotalNodes)
267        );
268        let bytes = JsonLimits::new().with_max_input_bytes(2);
269        assert_eq!(
270            parse_with_limits(b"[1]", bytes).unwrap_err().kind().clone(),
271            JsonErrorKind::LimitExceeded(JsonLimitKind::InputBytes)
272        );
273    }
274
275    #[test]
276    fn enforces_string_and_number_byte_limits() {
277        let s = JsonLimits {
278            max_string_bytes: 3,
279            ..JsonLimits::new()
280        };
281        assert_eq!(
282            parse_with_limits(br#""abcd""#, s)
283                .unwrap_err()
284                .kind()
285                .clone(),
286            JsonErrorKind::LimitExceeded(JsonLimitKind::StringBytes)
287        );
288        let n = JsonLimits {
289            max_number_bytes: 2,
290            ..JsonLimits::new()
291        };
292        assert_eq!(
293            parse_with_limits(b"12345", n).unwrap_err().kind().clone(),
294            JsonErrorKind::LimitExceeded(JsonLimitKind::NumberBytes)
295        );
296    }
297
298    // ---- numbers ----------------------------------------------------------
299
300    #[test]
301    fn number_conversions() {
302        assert_eq!(parse_ok("-7").as_number().unwrap().to_i64().unwrap(), -7);
303        assert_eq!(parse_ok("7").as_number().unwrap().to_u64().unwrap(), 7);
304        assert!((parse_ok("1.5").as_number().unwrap().to_f64().unwrap() - 1.5).abs() < 1e-12);
305        assert_eq!(
306            parse_ok("1.5").as_number().unwrap().to_i64(),
307            Err(JsonNumberError::NotAnInteger)
308        );
309        assert_eq!(
310            parse_ok("99999999999999999999999")
311                .as_number()
312                .unwrap()
313                .to_i64(),
314            Err(JsonNumberError::OutOfRange)
315        );
316        assert_eq!(
317            parse_ok("1e400").as_number().unwrap().to_f64(),
318            Err(JsonNumberError::NotFinite)
319        );
320    }
321
322    #[test]
323    fn number_preserves_representation() {
324        assert_eq!(parse_ok("1.0").as_number().unwrap().as_str(), "1.0");
325        assert_ne!(parse_ok("1.0"), parse_ok("1")); // structural equality
326    }
327
328    #[test]
329    fn json_number_from_f64() {
330        assert_eq!(JsonNumber::try_from_f64(1.5).unwrap().as_str(), "1.5");
331        assert_eq!(
332            JsonNumber::try_from_f64(f64::NAN),
333            Err(JsonNumberError::NotFinite)
334        );
335        assert_eq!(
336            JsonNumber::try_from_f64(f64::INFINITY),
337            Err(JsonNumberError::NotFinite)
338        );
339        assert_eq!(JsonNumber::new("01"), Err(JsonNumberError::InvalidNumber));
340    }
341
342    // ---- errors -----------------------------------------------------------
343
344    #[test]
345    fn error_reports_location_and_path() {
346        let err = parse_str("  @").unwrap_err();
347        assert_eq!(err.kind().clone(), JsonErrorKind::UnexpectedByte);
348        assert_eq!(err.offset(), 2);
349        assert_eq!(err.line(), 1);
350        assert_eq!(err.column(), 3);
351
352        let err = parse_str(r#"{"users":[{"name":1},{"name":}]}"#).unwrap_err();
353        let path = err.path().unwrap().to_string();
354        assert_eq!(path, "$.users[1].name");
355    }
356
357    // ---- serialization ----------------------------------------------------
358
359    #[test]
360    fn compact_roundtrip_and_golden_bytes() {
361        let value = parse_ok(r#"{"a":1,"b":true,"c":[null,"x"]}"#);
362        assert_eq!(
363            to_compact_vec(&value),
364            br#"{"a":1,"b":true,"c":[null,"x"]}"#
365        );
366        // Roundtrip: serialize, reparse, equal value.
367        let again = parse_str(&to_compact_string(&value)).unwrap();
368        assert_eq!(value, again);
369    }
370
371    #[test]
372    fn writer_escapes_control_and_special_characters() {
373        let mut object = JsonObject::new();
374        object.insert(
375            String::from("k"),
376            JsonValue::String(String::from("a\nb\"c\\\u{01}")),
377        );
378        let value = JsonValue::Object(object);
379        assert_eq!(to_compact_string(&value), r#"{"k":"a\nb\"c\\\u0001"}"#);
380    }
381
382    #[test]
383    fn object_insert_replaces_in_place() {
384        let mut object = JsonObject::new();
385        assert!(object
386            .insert(String::from("a"), JsonValue::Bool(true))
387            .is_none());
388        let old = object.insert(String::from("a"), JsonValue::Bool(false));
389        assert_eq!(old, Some(JsonValue::Bool(true)));
390        assert_eq!(object.len(), 1);
391    }
392
393    #[test]
394    fn deeply_nested_within_limits_does_not_overflow() {
395        // Build input nested to the default limit and confirm bounded handling.
396        let depth = 64;
397        let mut s = String::new();
398        for _ in 0..depth {
399            s.push('[');
400        }
401        s.push('1');
402        for _ in 0..depth {
403            s.push(']');
404        }
405        // Default max_depth is 64, so depth 64 is at the edge; depth 65 fails.
406        let _ = parse_str(&s); // must not panic regardless of accept/reject
407        assert!(parse_with_limits(s.as_bytes(), JsonLimits::new().with_max_depth(64)).is_ok());
408    }
409
410    #[test]
411    fn arbitrary_bytes_never_panic() {
412        // Smoke test: a spread of odd inputs must each return Ok or Err, never panic.
413        for input in [
414            &b""[..],
415            b"   ",
416            b"{",
417            b"[",
418            b"\"",
419            b"\"\\",
420            b"\"\\u",
421            b"tru",
422            b"-",
423            b"[,]",
424            b"{,}",
425            b"\xff\xfe",
426            b"[[[",
427            b"}}}",
428            b"\"\\uD800\"",
429            b"1e",
430            b"{\"a\"}",
431        ] {
432            let _ = parse(input);
433        }
434    }
435
436    #[test]
437    fn value_accessors_return_inner_or_none() {
438        let v = parse_ok(r#"{"b":true,"n":7,"s":"x","a":[1],"nil":null}"#);
439        let o = v.as_object().expect("object");
440        assert!(o.get("nil").unwrap().is_null());
441        assert_eq!(o.get("b").unwrap().as_bool(), Some(true));
442        assert_eq!(o.get("s").unwrap().as_str(), Some("x"));
443        assert_eq!(o.get("n").unwrap().as_number().unwrap().as_str(), "7");
444        assert_eq!(o.get("a").unwrap().as_array().unwrap().len(), 1);
445
446        // Wrong-variant accessors return None.
447        let b = JsonValue::Bool(true);
448        assert!(!b.is_null());
449        assert_eq!(b.as_str(), None);
450        assert_eq!(b.as_number(), None);
451        assert_eq!(b.as_array(), None);
452        assert!(b.as_object().is_none());
453        assert_eq!(JsonValue::Null.as_bool(), None);
454    }
455
456    #[test]
457    fn object_insert_get_iter_and_len() {
458        let mut obj = JsonObject::new();
459        assert!(obj.is_empty());
460        assert_eq!(obj.len(), 0);
461        assert!(!obj.contains_key("k"));
462
463        assert_eq!(obj.insert("k".to_string(), JsonValue::Bool(false)), None);
464        assert!(obj.contains_key("k"));
465        assert_eq!(obj.len(), 1);
466
467        // Insert with an existing key replaces in place and returns the old value.
468        let old = obj.insert("k".to_string(), JsonValue::Bool(true));
469        assert_eq!(old, Some(JsonValue::Bool(false)));
470        assert_eq!(obj.len(), 1);
471        assert_eq!(obj.get("k"), Some(&JsonValue::Bool(true)));
472        assert_eq!(obj.get("missing"), None);
473
474        obj.insert("k2".to_string(), JsonValue::Null);
475        let members: Vec<&str> = obj.iter().map(|m| m.key()).collect();
476        assert_eq!(members, ["k", "k2"]);
477        assert_eq!(obj.iter().next().unwrap().value(), &JsonValue::Bool(true));
478
479        assert_eq!(JsonObject::default().len(), 0);
480    }
481
482    #[test]
483    fn number_conversions_cover_each_error() {
484        let int = JsonNumber::new("42").unwrap();
485        assert!(int.is_integer());
486        assert_eq!(int.to_i64(), Ok(42));
487        assert_eq!(int.to_u64(), Ok(42));
488        assert_eq!(int.to_f64(), Ok(42.0));
489
490        let neg = JsonNumber::new("-1").unwrap();
491        assert_eq!(neg.to_u64(), Err(JsonNumberError::OutOfRange));
492
493        let frac = JsonNumber::new("1.5").unwrap();
494        assert!(!frac.is_integer());
495        assert_eq!(frac.to_i64(), Err(JsonNumberError::NotAnInteger));
496        assert_eq!(frac.to_u64(), Err(JsonNumberError::NotAnInteger));
497        assert_eq!(frac.to_f64(), Ok(1.5));
498
499        let huge = JsonNumber::new("99999999999999999999").unwrap();
500        assert_eq!(huge.to_i64(), Err(JsonNumberError::OutOfRange));
501
502        let overflow = JsonNumber::new("1e400").unwrap();
503        assert_eq!(overflow.to_f64(), Err(JsonNumberError::NotFinite));
504
505        assert_eq!(JsonNumber::new("+1"), Err(JsonNumberError::InvalidNumber));
506        assert_eq!(JsonNumber::try_from_f64(2.5).unwrap().to_f64(), Ok(2.5));
507        assert_eq!(
508            JsonNumber::try_from_f64(f64::NAN),
509            Err(JsonNumberError::NotFinite)
510        );
511        assert_eq!(
512            JsonNumber::try_from_f64(f64::INFINITY),
513            Err(JsonNumberError::NotFinite)
514        );
515    }
516
517    #[test]
518    fn limits_profiles_and_builders() {
519        assert_eq!(JsonLimits::default(), JsonLimits::new());
520        assert!(JsonLimits::conservative().max_input_bytes < JsonLimits::new().max_input_bytes);
521        assert!(JsonLimits::permissive().max_input_bytes > JsonLimits::new().max_input_bytes);
522
523        let tuned = JsonLimits::new()
524            .with_max_depth(8)
525            .with_max_input_bytes(1024)
526            .with_max_string_bytes(16)
527            .with_max_total_nodes(32);
528        assert_eq!(tuned.max_depth, 8);
529        assert_eq!(tuned.max_input_bytes, 1024);
530        assert_eq!(tuned.max_string_bytes, 16);
531        assert_eq!(tuned.max_total_nodes, 32);
532    }
533
534    #[test]
535    fn error_display_covers_each_kind() {
536        // One representative input per simple kind, then check Display text.
537        let cases: &[(&str, &str)] = &[
538            ("", "unexpected end of input"),
539            ("@", "unexpected byte"),
540            ("\"a\\xb\"", "invalid escape sequence"),
541            ("\"\\uZZZZ\"", "invalid unicode escape"),
542            ("\"\\uD800\"", "unpaired UTF-16 surrogate"),
543            ("01", "invalid number"),
544            ("{\"a\":1,\"a\":2}", "duplicate object key"),
545            ("true false", "trailing data after JSON value"),
546        ];
547        for (input, expected) in cases {
548            let err = parse_str(input).unwrap_err();
549            assert!(
550                err.to_string().contains(expected),
551                "input {input:?} -> {err} (expected to contain {expected:?})"
552            );
553        }
554
555        // A control character inside a string.
556        let ctrl = parse(b"\"\x01\"").unwrap_err();
557        assert!(ctrl.to_string().contains("unescaped control character"));
558
559        // Invalid UTF-8 input.
560        let utf8 = parse(b"\xff").unwrap_err();
561        assert!(utf8.to_string().contains("invalid UTF-8"));
562    }
563
564    #[test]
565    fn error_accessors_and_limit_display_with_path() {
566        let limits = JsonLimits::new().with_max_depth(1);
567        let err = parse_with_limits(b"[[1]]", limits).unwrap_err();
568        assert_eq!(
569            err.kind(),
570            &JsonErrorKind::LimitExceeded(JsonLimitKind::Depth)
571        );
572        assert!(err.offset() >= 1);
573        assert_eq!(err.line(), 1);
574        assert!(err.column() >= 1);
575        let shown = err.to_string();
576        assert!(shown.contains("limit exceeded: nesting depth"));
577        assert!(shown.contains("path: $"));
578        assert_eq!(JsonLimitKind::Depth.as_str(), "nesting depth");
579    }
580
581    #[test]
582    fn path_display_formats_keys_and_indices() {
583        let path = JsonPath::from_segments(vec![
584            JsonPathSegment::Key("users".to_string()),
585            JsonPathSegment::Index(3),
586            JsonPathSegment::Key("email".to_string()),
587        ]);
588        assert_eq!(path.to_string(), "$.users[3].email");
589        assert_eq!(path.segments().len(), 3);
590        assert_eq!(JsonPath::default().to_string(), "$");
591    }
592
593    #[test]
594    fn number_error_display_is_distinct() {
595        assert_eq!(
596            JsonNumberError::OutOfRange.to_string(),
597            "number out of range for target type"
598        );
599        assert_eq!(
600            JsonNumberError::NotAnInteger.to_string(),
601            "number is not an integer"
602        );
603        assert_eq!(
604            JsonNumberError::NotFinite.to_string(),
605            "number is not finite"
606        );
607        assert_eq!(
608            JsonNumberError::InvalidNumber.to_string(),
609            "not a valid JSON number"
610        );
611    }
612
613    #[test]
614    fn writer_serializes_all_branches() {
615        let mut obj = JsonObject::new();
616        obj.insert("off".to_string(), JsonValue::Bool(false));
617        obj.insert(
618            "esc".to_string(),
619            // Named escapes plus a control char that needs a hex nibble a-f.
620            JsonValue::String("\u{08}\u{0C}\n\r\t\u{1F}".to_string()),
621        );
622        obj.insert(
623            "arr".to_string(),
624            JsonValue::Array(vec![JsonValue::Null, JsonValue::Bool(true)]),
625        );
626        let value = JsonValue::Object(obj);
627
628        let s = to_compact_string(&value);
629        // Round-trips back to the same value (exercises every escape branch,
630        // including a control char whose hex escape uses an a-f nibble).
631        assert_eq!(parse_str(&s).unwrap(), value);
632        assert_eq!(to_compact_vec(&value), s.clone().into_bytes());
633        assert!(s.starts_with("{\"off\":false,"));
634        assert!(s.ends_with(",\"arr\":[null,true]}"));
635    }
636
637    #[cfg(feature = "std")]
638    #[test]
639    fn errors_implement_std_error() {
640        fn assert_error<E: std::error::Error>(_: &E) {}
641        let parse_err = parse_str("").unwrap_err();
642        assert_error(&parse_err);
643        let num_err = JsonNumber::new("1.5").unwrap().to_i64().unwrap_err();
644        assert_error(&num_err);
645    }
646}