Skip to main content

oci_zero/
json.rs

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}