1use core::{char, fmt, str};
2
3const MAX_DEPTH: usize = 64;
4
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6pub struct JsonString<'a> {
7 encoded: &'a str,
8 escaped: bool,
9}
10
11impl<'a> JsonString<'a> {
12 pub const fn encoded(&self) -> &'a str {
13 self.encoded
14 }
15
16 pub fn as_str(&self) -> Option<&'a str> {
17 (!self.escaped).then_some(self.encoded)
18 }
19
20 pub fn decode_into<'buffer>(
21 &self,
22 buffer: &'buffer mut [u8],
23 ) -> Result<&'buffer str, JsonError> {
24 if !self.escaped {
25 let destination = buffer
26 .get_mut(..self.encoded.len())
27 .ok_or(JsonError::BufferTooSmall)?;
28 destination.copy_from_slice(self.encoded.as_bytes());
29 return str::from_utf8(destination).map_err(|_| JsonError::InvalidUtf8);
30 }
31
32 let source = self.encoded.as_bytes();
33 let mut input = 0;
34 let mut output = 0;
35 while input < source.len() {
36 if source[input] != b'\\' {
37 let start = input;
38 while input < source.len() && source[input] != b'\\' {
39 input += 1;
40 }
41 copy_decoded(buffer, &mut output, &source[start..input])?;
42 continue;
43 }
44 let (character, next) = escaped_character(source, input)?;
45 input = next;
46 let mut encoded = [0; 4];
47 copy_decoded(
48 buffer,
49 &mut output,
50 character.encode_utf8(&mut encoded).as_bytes(),
51 )?;
52 }
53 str::from_utf8(&buffer[..output]).map_err(|_| JsonError::InvalidUtf8)
54 }
55
56 pub(crate) fn decoded_eq_ascii(&self, expected: &str) -> bool {
57 if !expected.is_ascii() {
58 return false;
59 }
60 if !self.escaped {
61 return self.encoded == expected;
62 }
63 let source = self.encoded.as_bytes();
64 let expected = expected.as_bytes();
65 let mut input = 0;
66 let mut output = 0;
67 while input < source.len() {
68 if source[input] == b'\\' {
69 let Ok((character, next)) = escaped_character(source, input) else {
70 return false;
71 };
72 if !character.is_ascii() || expected.get(output).copied() != Some(character as u8) {
73 return false;
74 }
75 input = next;
76 output += 1;
77 } else {
78 if expected.get(output).copied() != Some(source[input]) {
79 return false;
80 }
81 input += 1;
82 output += 1;
83 }
84 }
85 output == expected.len()
86 }
87}
88
89#[derive(Clone, Copy, Debug, Eq, PartialEq)]
90pub enum JsonError {
91 InvalidUtf8,
92 InvalidSyntax,
93 NestingTooDeep,
94 InvalidEscape,
95 InvalidNumber,
96 WrongType,
97 MissingField(&'static str),
98 DuplicateField(&'static str),
99 BufferTooSmall,
100}
101
102impl fmt::Display for JsonError {
103 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
104 match self {
105 Self::InvalidUtf8 => formatter.write_str("JSON is not UTF-8"),
106 Self::InvalidSyntax => formatter.write_str("invalid JSON syntax"),
107 Self::NestingTooDeep => formatter.write_str("JSON nesting is too deep"),
108 Self::InvalidEscape => formatter.write_str("invalid JSON string escape"),
109 Self::InvalidNumber => formatter.write_str("invalid JSON number"),
110 Self::WrongType => formatter.write_str("unexpected JSON value type"),
111 Self::MissingField(field) => write!(formatter, "missing JSON field {field}"),
112 Self::DuplicateField(field) => write!(formatter, "duplicate JSON field {field}"),
113 Self::BufferTooSmall => formatter.write_str("JSON string output buffer is too small"),
114 }
115 }
116}
117
118#[derive(Clone, Copy, Debug, Eq, PartialEq)]
119pub(crate) struct Value<'a> {
120 bytes: &'a [u8],
121}
122
123impl<'a> Value<'a> {
124 pub(crate) fn parse_document(bytes: &'a [u8]) -> Result<Self, JsonError> {
125 str::from_utf8(bytes).map_err(|_| JsonError::InvalidUtf8)?;
126 let start = whitespace(bytes, 0);
127 let end = parse_value(bytes, start, 0)?;
128 if whitespace(bytes, end) != bytes.len() {
129 return Err(JsonError::InvalidSyntax);
130 }
131 Ok(Self {
132 bytes: &bytes[start..end],
133 })
134 }
135
136 pub(crate) fn object(self) -> Result<Object<'a>, JsonError> {
137 if self.bytes.first() != Some(&b'{') {
138 return Err(JsonError::WrongType);
139 }
140 Ok(Object { bytes: self.bytes })
141 }
142
143 pub(crate) fn array(self) -> Result<Array<'a>, JsonError> {
144 if self.bytes.first() != Some(&b'[') {
145 return Err(JsonError::WrongType);
146 }
147 Ok(Array { bytes: self.bytes })
148 }
149
150 pub(crate) fn string(self) -> Result<JsonString<'a>, JsonError> {
151 if self.bytes.first() != Some(&b'"') || self.bytes.last() != Some(&b'"') {
152 return Err(JsonError::WrongType);
153 }
154 let encoded = str::from_utf8(&self.bytes[1..self.bytes.len() - 1])
155 .map_err(|_| JsonError::InvalidUtf8)?;
156 Ok(JsonString {
157 encoded,
158 escaped: encoded.as_bytes().contains(&b'\\'),
159 })
160 }
161
162 pub(crate) fn u64(self) -> Result<u64, JsonError> {
163 let value = str::from_utf8(self.bytes).map_err(|_| JsonError::InvalidUtf8)?;
164 if value.is_empty() || !value.bytes().all(|byte| byte.is_ascii_digit()) {
165 return Err(JsonError::WrongType);
166 }
167 value.parse().map_err(|_| JsonError::InvalidNumber)
168 }
169
170 pub(crate) fn is_null(self) -> bool {
171 self.bytes == b"null"
172 }
173}
174
175#[derive(Clone, Copy, Debug, Eq, PartialEq)]
176pub(crate) struct Object<'a> {
177 bytes: &'a [u8],
178}
179
180impl<'a> Object<'a> {
181 pub(crate) fn get(self, name: &'static str) -> Result<Option<Value<'a>>, JsonError> {
182 let mut found = None;
183 for member in self.iter() {
184 let (key, value) = member?;
185 if key.decoded_eq_ascii(name) {
186 if found.is_some() {
187 return Err(JsonError::DuplicateField(name));
188 }
189 found = Some(value);
190 }
191 }
192 Ok(found)
193 }
194
195 pub(crate) fn required(self, name: &'static str) -> Result<Value<'a>, JsonError> {
196 self.get(name)?.ok_or(JsonError::MissingField(name))
197 }
198
199 pub(crate) fn iter(self) -> ObjectIter<'a> {
200 ObjectIter {
201 bytes: self.bytes,
202 position: 1,
203 first: true,
204 finished: false,
205 }
206 }
207}
208
209pub(crate) struct ObjectIter<'a> {
210 bytes: &'a [u8],
211 position: usize,
212 first: bool,
213 finished: bool,
214}
215
216impl<'a> Iterator for ObjectIter<'a> {
217 type Item = Result<(JsonString<'a>, Value<'a>), JsonError>;
218
219 fn next(&mut self) -> Option<Self::Item> {
220 if self.finished {
221 return None;
222 }
223 let result = self.next_inner();
224 if result.is_err() {
225 self.finished = true;
226 }
227 result.transpose()
228 }
229}
230
231impl<'a> ObjectIter<'a> {
232 fn next_inner(&mut self) -> Result<Option<(JsonString<'a>, Value<'a>)>, JsonError> {
233 self.position = whitespace(self.bytes, self.position);
234 if self.bytes.get(self.position) == Some(&b'}') {
235 self.finished = true;
236 return Ok(None);
237 }
238 if !self.first {
239 if self.bytes.get(self.position) != Some(&b',') {
240 return Err(JsonError::InvalidSyntax);
241 }
242 self.position = whitespace(self.bytes, self.position + 1);
243 }
244 self.first = false;
245 if self.bytes.get(self.position) != Some(&b'"') {
246 return Err(JsonError::InvalidSyntax);
247 }
248 let key_end = parse_string(self.bytes, self.position)?;
249 let key = Value {
250 bytes: &self.bytes[self.position..key_end],
251 }
252 .string()?;
253 self.position = whitespace(self.bytes, key_end);
254 if self.bytes.get(self.position) != Some(&b':') {
255 return Err(JsonError::InvalidSyntax);
256 }
257 let start = whitespace(self.bytes, self.position + 1);
258 let end = parse_value(self.bytes, start, 0)?;
259 self.position = end;
260 Ok(Some((
261 key,
262 Value {
263 bytes: &self.bytes[start..end],
264 },
265 )))
266 }
267}
268
269#[derive(Clone, Copy, Debug, Eq, PartialEq)]
270pub(crate) struct Array<'a> {
271 bytes: &'a [u8],
272}
273
274impl<'a> Array<'a> {
275 pub(crate) fn iter(self) -> ArrayIter<'a> {
276 ArrayIter {
277 bytes: self.bytes,
278 position: 1,
279 first: true,
280 finished: false,
281 }
282 }
283}
284
285pub(crate) struct ArrayIter<'a> {
286 bytes: &'a [u8],
287 position: usize,
288 first: bool,
289 finished: bool,
290}
291
292impl<'a> Iterator for ArrayIter<'a> {
293 type Item = Result<Value<'a>, JsonError>;
294
295 fn next(&mut self) -> Option<Self::Item> {
296 if self.finished {
297 return None;
298 }
299 let result = self.next_inner();
300 if result.is_err() {
301 self.finished = true;
302 }
303 result.transpose()
304 }
305}
306
307impl<'a> ArrayIter<'a> {
308 fn next_inner(&mut self) -> Result<Option<Value<'a>>, JsonError> {
309 self.position = whitespace(self.bytes, self.position);
310 if self.bytes.get(self.position) == Some(&b']') {
311 self.finished = true;
312 return Ok(None);
313 }
314 if !self.first {
315 if self.bytes.get(self.position) != Some(&b',') {
316 return Err(JsonError::InvalidSyntax);
317 }
318 self.position = whitespace(self.bytes, self.position + 1);
319 }
320 self.first = false;
321 let start = self.position;
322 let end = parse_value(self.bytes, start, 0)?;
323 self.position = end;
324 Ok(Some(Value {
325 bytes: &self.bytes[start..end],
326 }))
327 }
328}
329
330fn parse_value(bytes: &[u8], position: usize, depth: usize) -> Result<usize, JsonError> {
331 if depth > MAX_DEPTH {
332 return Err(JsonError::NestingTooDeep);
333 }
334 match bytes.get(position).copied() {
335 Some(b'"') => parse_string(bytes, position),
336 Some(b'{') => parse_object(bytes, position, depth + 1),
337 Some(b'[') => parse_array(bytes, position, depth + 1),
338 Some(b't') => literal(bytes, position, b"true"),
339 Some(b'f') => literal(bytes, position, b"false"),
340 Some(b'n') => literal(bytes, position, b"null"),
341 Some(b'-' | b'0'..=b'9') => parse_number(bytes, position),
342 _ => Err(JsonError::InvalidSyntax),
343 }
344}
345
346fn parse_object(bytes: &[u8], mut position: usize, depth: usize) -> Result<usize, JsonError> {
347 position = whitespace(bytes, position + 1);
348 if bytes.get(position) == Some(&b'}') {
349 return Ok(position + 1);
350 }
351 loop {
352 if bytes.get(position) != Some(&b'"') {
353 return Err(JsonError::InvalidSyntax);
354 }
355 position = whitespace(bytes, parse_string(bytes, position)?);
356 if bytes.get(position) != Some(&b':') {
357 return Err(JsonError::InvalidSyntax);
358 }
359 position = whitespace(
360 bytes,
361 parse_value(bytes, whitespace(bytes, position + 1), depth)?,
362 );
363 match bytes.get(position) {
364 Some(b'}') => return Ok(position + 1),
365 Some(b',') => position = whitespace(bytes, position + 1),
366 _ => return Err(JsonError::InvalidSyntax),
367 }
368 }
369}
370
371fn parse_array(bytes: &[u8], mut position: usize, depth: usize) -> Result<usize, JsonError> {
372 position = whitespace(bytes, position + 1);
373 if bytes.get(position) == Some(&b']') {
374 return Ok(position + 1);
375 }
376 loop {
377 position = whitespace(bytes, parse_value(bytes, position, depth)?);
378 match bytes.get(position) {
379 Some(b']') => return Ok(position + 1),
380 Some(b',') => position = whitespace(bytes, position + 1),
381 _ => return Err(JsonError::InvalidSyntax),
382 }
383 }
384}
385
386fn parse_string(bytes: &[u8], position: usize) -> Result<usize, JsonError> {
387 let mut index = position + 1;
388 while let Some(byte) = bytes.get(index).copied() {
389 match byte {
390 b'"' => return Ok(index + 1),
391 b'\\' => {
392 let (_, next) = escaped_character(bytes, index)?;
393 index = next;
394 }
395 0..=0x1f => return Err(JsonError::InvalidSyntax),
396 _ => index += 1,
397 }
398 }
399 Err(JsonError::InvalidSyntax)
400}
401
402fn escaped_character(bytes: &[u8], slash: usize) -> Result<(char, usize), JsonError> {
403 match bytes.get(slash + 1).copied() {
404 Some(b'"') => Ok(('"', slash + 2)),
405 Some(b'\\') => Ok(('\\', slash + 2)),
406 Some(b'/') => Ok(('/', slash + 2)),
407 Some(b'b') => Ok(('\u{0008}', slash + 2)),
408 Some(b'f') => Ok(('\u{000c}', slash + 2)),
409 Some(b'n') => Ok(('\n', slash + 2)),
410 Some(b'r') => Ok(('\r', slash + 2)),
411 Some(b't') => Ok(('\t', slash + 2)),
412 Some(b'u') => {
413 let first = unicode_escape(bytes, slash + 2)?;
414 let next = slash + 6;
415 let scalar = if (0xd800..=0xdbff).contains(&first) {
416 if bytes.get(next..next + 2) != Some(b"\\u") {
417 return Err(JsonError::InvalidEscape);
418 }
419 let second = unicode_escape(bytes, next + 2)?;
420 if !(0xdc00..=0xdfff).contains(&second) {
421 return Err(JsonError::InvalidEscape);
422 }
423 let high = u32::from(first - 0xd800);
424 let low = u32::from(second - 0xdc00);
425 (0x10000 + (high << 10) + low, next + 6)
426 } else if (0xdc00..=0xdfff).contains(&first) {
427 return Err(JsonError::InvalidEscape);
428 } else {
429 (u32::from(first), next)
430 };
431 let character = char::from_u32(scalar.0).ok_or(JsonError::InvalidEscape)?;
432 Ok((character, scalar.1))
433 }
434 _ => Err(JsonError::InvalidEscape),
435 }
436}
437
438fn unicode_escape(bytes: &[u8], start: usize) -> Result<u16, JsonError> {
439 let digits = bytes
440 .get(start..start + 4)
441 .ok_or(JsonError::InvalidEscape)?;
442 let mut value = 0u16;
443 for digit in digits {
444 value = value
445 .checked_mul(16)
446 .and_then(|value| hex(*digit).map(|digit| value + u16::from(digit)))
447 .ok_or(JsonError::InvalidEscape)?;
448 }
449 Ok(value)
450}
451
452fn hex(byte: u8) -> Option<u8> {
453 match byte {
454 b'0'..=b'9' => Some(byte - b'0'),
455 b'a'..=b'f' => Some(byte - b'a' + 10),
456 b'A'..=b'F' => Some(byte - b'A' + 10),
457 _ => None,
458 }
459}
460
461fn parse_number(bytes: &[u8], mut position: usize) -> Result<usize, JsonError> {
462 if bytes.get(position) == Some(&b'-') {
463 position += 1;
464 }
465 match bytes.get(position) {
466 Some(b'0') => position += 1,
467 Some(b'1'..=b'9') => {
468 position += 1;
469 while matches!(bytes.get(position), Some(b'0'..=b'9')) {
470 position += 1;
471 }
472 }
473 _ => return Err(JsonError::InvalidNumber),
474 }
475 if bytes.get(position) == Some(&b'.') {
476 position += 1;
477 let start = position;
478 while matches!(bytes.get(position), Some(b'0'..=b'9')) {
479 position += 1;
480 }
481 if position == start {
482 return Err(JsonError::InvalidNumber);
483 }
484 }
485 if matches!(bytes.get(position), Some(b'e' | b'E')) {
486 position += 1;
487 if matches!(bytes.get(position), Some(b'+' | b'-')) {
488 position += 1;
489 }
490 let start = position;
491 while matches!(bytes.get(position), Some(b'0'..=b'9')) {
492 position += 1;
493 }
494 if position == start {
495 return Err(JsonError::InvalidNumber);
496 }
497 }
498 Ok(position)
499}
500
501fn literal(bytes: &[u8], position: usize, literal: &[u8]) -> Result<usize, JsonError> {
502 if bytes.get(position..position + literal.len()) == Some(literal) {
503 Ok(position + literal.len())
504 } else {
505 Err(JsonError::InvalidSyntax)
506 }
507}
508
509fn whitespace(bytes: &[u8], mut position: usize) -> usize {
510 while matches!(bytes.get(position), Some(b' ' | b'\n' | b'\r' | b'\t')) {
511 position += 1;
512 }
513 position
514}
515
516fn copy_decoded(buffer: &mut [u8], output: &mut usize, bytes: &[u8]) -> Result<(), JsonError> {
517 let end = output
518 .checked_add(bytes.len())
519 .ok_or(JsonError::BufferTooSmall)?;
520 let destination = buffer
521 .get_mut(*output..end)
522 .ok_or(JsonError::BufferTooSmall)?;
523 destination.copy_from_slice(bytes);
524 *output = end;
525 Ok(())
526}
527
528#[cfg(test)]
529mod tests {
530 use std::{format, string::ToString};
531
532 use super::{parse_value, JsonError, Value, MAX_DEPTH};
533
534 #[test]
535 fn validates_and_iterates_nested_json() {
536 let value = Value::parse_document(br#" {"a":[1,{"b":true}],"skip":null} "#).unwrap();
537 let object = value.object().unwrap();
538 let array = object.required("a").unwrap().array().unwrap();
539 assert_eq!(array.iter().count(), 2);
540 assert!(object.get("missing").unwrap().is_none());
541 }
542
543 #[test]
544 fn decodes_strings_and_rejects_bad_surrogates() {
545 let value = Value::parse_document(br#""hello\n\u263a\ud83d\ude00""#).unwrap();
546 let string = value.string().unwrap();
547 let mut output = [0; 32];
548 assert_eq!(string.decode_into(&mut output).unwrap(), "hello\n☺😀");
549 assert_eq!(
550 Value::parse_document(br#""\ud83d!""#),
551 Err(JsonError::InvalidEscape)
552 );
553 }
554
555 #[test]
556 fn rejects_trailing_and_duplicate_requested_fields() {
557 assert!(Value::parse_document(b"{} x").is_err());
558 let object = Value::parse_document(br#"{"a":1,"\u0061":2}"#)
559 .unwrap()
560 .object()
561 .unwrap();
562 assert_eq!(object.get("a"), Err(JsonError::DuplicateField("a")));
563 }
564
565 #[test]
566 fn decodes_every_simple_escape_and_uppercase_hex() {
567 let value = Value::parse_document(br#""\"\\\/\b\f\n\r\t\u004A\u00AF""#).unwrap();
568 let string = value.string().unwrap();
569 let mut output = [0; 32];
570 assert_eq!(
571 string.decode_into(&mut output).unwrap().as_bytes(),
572 b"\"\\/\x08\x0c\n\r\tJ\xc2\xaf"
573 );
574
575 let positioned_backslash = Value::parse_document(br#""a\\b""#)
576 .unwrap()
577 .string()
578 .unwrap();
579 assert_eq!(
580 positioned_backslash.decode_into(&mut output).unwrap(),
581 "a\\b"
582 );
583 }
584
585 #[test]
586 fn exposes_unescaped_strings_and_checks_decoded_ascii_exactly() {
587 let plain = Value::parse_document(br#""plain""#)
588 .unwrap()
589 .string()
590 .unwrap();
591 assert_eq!(plain.encoded(), "plain");
592 assert_eq!(plain.as_str(), Some("plain"));
593 assert!(plain.decoded_eq_ascii("plain"));
594 assert!(!plain.decoded_eq_ascii("other"));
595 assert!(!plain.decoded_eq_ascii("£"));
596
597 let escaped = Value::parse_document(br#""\u0062""#)
598 .unwrap()
599 .string()
600 .unwrap();
601 assert_eq!(escaped.as_str(), None);
602 assert!(escaped.decoded_eq_ascii("b"));
603 assert!(!escaped.decoded_eq_ascii("a"));
604 }
605
606 #[test]
607 fn rejects_raw_control_characters_and_small_decode_buffers() {
608 assert_eq!(
609 Value::parse_document(b"\"line\nfeed\""),
610 Err(JsonError::InvalidSyntax)
611 );
612
613 let plain = Value::parse_document(br#""hello""#)
614 .unwrap()
615 .string()
616 .unwrap();
617 assert_eq!(
618 plain.decode_into(&mut [0; 4]),
619 Err(JsonError::BufferTooSmall)
620 );
621 let escaped = Value::parse_document(br#""\u263a""#)
622 .unwrap()
623 .string()
624 .unwrap();
625 assert_eq!(
626 escaped.decode_into(&mut [0; 2]),
627 Err(JsonError::BufferTooSmall)
628 );
629 }
630
631 #[test]
632 fn accepts_empty_containers_and_false_literal() {
633 assert_eq!(
634 Value::parse_document(b"{}")
635 .unwrap()
636 .object()
637 .unwrap()
638 .iter()
639 .count(),
640 0
641 );
642 assert_eq!(
643 Value::parse_document(b"[]")
644 .unwrap()
645 .array()
646 .unwrap()
647 .iter()
648 .count(),
649 0
650 );
651 assert!(Value::parse_document(b"false").is_ok());
652 }
653
654 #[test]
655 fn enforces_nesting_at_the_exact_limit() {
656 assert_eq!(parse_value(b"null", 0, MAX_DEPTH), Ok(4));
657 assert_eq!(
658 parse_value(b"null", 0, MAX_DEPTH + 1),
659 Err(JsonError::NestingTooDeep)
660 );
661 assert_eq!(
662 parse_value(b"null", 0, MAX_DEPTH + 2),
663 Err(JsonError::NestingTooDeep)
664 );
665
666 let arrays_at_limit = format!("{}null{}", "[".repeat(MAX_DEPTH), "]".repeat(MAX_DEPTH));
667 assert!(Value::parse_document(arrays_at_limit.as_bytes()).is_ok());
668 let arrays_too_deep = format!(
669 "{}null{}",
670 "[".repeat(MAX_DEPTH + 1),
671 "]".repeat(MAX_DEPTH + 1)
672 );
673 assert_eq!(
674 Value::parse_document(arrays_too_deep.as_bytes()),
675 Err(JsonError::NestingTooDeep)
676 );
677
678 let objects_at_limit = format!(
679 "{}null{}",
680 "{\"a\":".repeat(MAX_DEPTH),
681 "}".repeat(MAX_DEPTH)
682 );
683 assert!(Value::parse_document(objects_at_limit.as_bytes()).is_ok());
684 let objects_too_deep = format!(
685 "{}null{}",
686 "{\"a\":".repeat(MAX_DEPTH + 1),
687 "}".repeat(MAX_DEPTH + 1)
688 );
689 assert_eq!(
690 Value::parse_document(objects_too_deep.as_bytes()),
691 Err(JsonError::NestingTooDeep)
692 );
693 }
694
695 #[test]
696 fn validates_each_json_number_form() {
697 for number in ["0", "-1", "10", "1.25", "1e2", "1E+2", "1e-2"] {
698 assert!(
699 Value::parse_document(number.as_bytes()).is_ok(),
700 "number={number:?}"
701 );
702 }
703 for number in ["-", "01", "1.", "1e", "1e+", "1e-"] {
704 assert!(
705 Value::parse_document(number.as_bytes()).is_err(),
706 "number={number:?}"
707 );
708 }
709
710 assert_eq!(Value { bytes: b"" }.u64(), Err(JsonError::WrongType));
711 assert_eq!(Value { bytes: b"12x" }.u64(), Err(JsonError::WrongType));
712 assert_eq!(Value { bytes: b"42" }.u64(), Ok(42));
713 assert_eq!(
714 Value {
715 bytes: b"18446744073709551616"
716 }
717 .u64(),
718 Err(JsonError::InvalidNumber)
719 );
720 }
721
722 #[test]
723 fn string_conversion_checks_both_quotes() {
724 assert_eq!(
725 Value { bytes: b"plain\"" }.string(),
726 Err(JsonError::WrongType)
727 );
728 assert_eq!(
729 Value { bytes: b"\"plain" }.string(),
730 Err(JsonError::WrongType)
731 );
732 }
733
734 #[test]
735 fn formats_every_json_error() {
736 let cases = [
737 (JsonError::InvalidUtf8, "JSON is not UTF-8"),
738 (JsonError::InvalidSyntax, "invalid JSON syntax"),
739 (JsonError::NestingTooDeep, "JSON nesting is too deep"),
740 (JsonError::InvalidEscape, "invalid JSON string escape"),
741 (JsonError::InvalidNumber, "invalid JSON number"),
742 (JsonError::WrongType, "unexpected JSON value type"),
743 (JsonError::MissingField("name"), "missing JSON field name"),
744 (
745 JsonError::DuplicateField("name"),
746 "duplicate JSON field name",
747 ),
748 (
749 JsonError::BufferTooSmall,
750 "JSON string output buffer is too small",
751 ),
752 ];
753 for (error, expected) in cases {
754 assert_eq!(error.to_string(), expected);
755 }
756 }
757}