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