Skip to main content

one_brackets/
parser.rs

1//! Streaming reader of the bracket format from any [`Read`].
2//!
3//! Follows the reader of the platform (8.3.27): whitespace is anything ICU `u_isspace` accepts,
4//! an atom runs up to the next `,` or `}`, and a quoted string unescapes doubled quotes and
5//! UTF-16 code units `"\xxxx`.
6
7use std::fs::File;
8use std::io::{self, BufRead, BufReader, Read};
9use std::path::Path;
10use std::str::FromStr;
11
12use crate::node::is_space;
13use crate::text::push_code_point;
14use crate::{Node, ParseError, Position, Text, Value};
15
16type Result<T> = std::result::Result<T, ParseError>;
17
18impl Node {
19    /// Parses a single top-level value from any source.
20    ///
21    /// Text is UTF-8; a leading BOM and surrounding whitespace are allowed. Reads through a buffer without loading the whole input into memory.
22    pub fn from_reader<R: Read>(reader: R) -> Result<Node> {
23        parse(BufReader::new(reader))
24    }
25}
26
27impl TryFrom<&str> for Node {
28    type Error = ParseError;
29
30    fn try_from(text: &str) -> Result<Node> {
31        parse(text.as_bytes())
32    }
33}
34
35impl TryFrom<&[u8]> for Node {
36    type Error = ParseError;
37
38    fn try_from(bytes: &[u8]) -> Result<Node> {
39        parse(bytes)
40    }
41}
42
43impl TryFrom<&Path> for Node {
44    type Error = ParseError;
45
46    fn try_from(path: &Path) -> Result<Node> {
47        let file = File::open(path).map_err(|source| ParseError::Open {
48            path: path.to_owned(),
49            source,
50        })?;
51        Node::from_reader(file)
52    }
53}
54
55impl FromStr for Node {
56    type Err = ParseError;
57
58    fn from_str(text: &str) -> Result<Node> {
59        parse(text.as_bytes())
60    }
61}
62
63fn parse<R: BufRead>(reader: R) -> Result<Node> {
64    let mut parser = Parser::new(reader);
65    parser.input.skip_bom()?;
66    parser.input.skip_ws()?;
67    if parser.input.peek()?.is_none() {
68        return Err(ParseError::EmptyInput);
69    }
70    let node = parser.value()?;
71    parser.input.skip_ws()?;
72    let at = parser.input.pos;
73    match parser.input.peek()? {
74        None => Ok(node),
75        Some(b'}') => Err(ParseError::UnmatchedClose { at }),
76        Some(_) => Err(ParseError::TrailingData { at }),
77    }
78}
79
80/// Comma-separated top-level values read one at a time, e.g. records of an event log `.lgp`.
81///
82/// The input is streamed: only the current value is kept in memory. Text lines before
83/// the values (a file header) can be read with [`read_line`](Values::read_line);
84/// positions in errors and nodes still count from the start of the input.
85/// A trailing comma is allowed: a log being written ends with one.
86/// The iterator stops after the first error.
87///
88/// ```
89/// use one_brackets::Values;
90///
91/// # fn main() -> Result<(), one_brackets::ParseError> {
92/// let mut values = Values::new("HEADER\r\n{1,a},\r\n{2,b},".as_bytes());
93/// assert_eq!(values.read_line()?.as_deref(), Some("HEADER"));
94/// let records = values.collect::<Result<Vec<_>, _>>()?;
95/// assert_eq!(records.len(), 2);
96/// assert_eq!(records[1].position().unwrap().line, 3);
97/// # Ok(())
98/// # }
99/// ```
100pub struct Values<R> {
101    parser: Parser<BufReader<R>>,
102    state: State,
103}
104
105#[derive(Clone, Copy, PartialEq, Eq)]
106enum State {
107    /// The BOM has not been checked yet.
108    Fresh,
109    BeforeValue,
110    AfterValue,
111    Done,
112}
113
114impl<R: Read> Values<R> {
115    pub fn new(reader: R) -> Self {
116        Values {
117            parser: Parser::new(BufReader::new(reader)),
118            state: State::Fresh,
119        }
120    }
121
122    /// Reads one text line before the values; `None` at the end of input.
123    ///
124    /// The line ending (`\n` or `\r\n`) is not included.
125    pub fn read_line(&mut self) -> Result<Option<String>> {
126        self.start()?;
127        let input = &mut self.parser.input;
128        let at = input.pos;
129        if input.peek()?.is_none() {
130            return Ok(None);
131        }
132        let mut bytes = Vec::new();
133        if input.scan(|b| b == b'\n', Some(&mut bytes))?.is_some() {
134            input.bump()?;
135        }
136        if bytes.last() == Some(&b'\r') {
137            bytes.pop();
138        }
139        String::from_utf8(bytes)
140            .map(Some)
141            .map_err(|_| ParseError::InvalidUtf8 {
142                context: "line",
143                at,
144            })
145    }
146
147    /// Position of the next unread byte.
148    pub fn position(&self) -> Position {
149        self.parser.input.pos
150    }
151
152    fn start(&mut self) -> Result<()> {
153        if self.state == State::Fresh {
154            self.parser.input.skip_bom()?;
155            self.state = State::BeforeValue;
156        }
157        Ok(())
158    }
159
160    fn read_value(&mut self) -> Result<Option<Node>> {
161        self.start()?;
162        let input = &mut self.parser.input;
163        input.skip_ws()?;
164        if self.state == State::AfterValue {
165            let at = input.pos;
166            match input.peek()? {
167                None => return Ok(None),
168                Some(b',') => {
169                    input.bump()?;
170                    input.skip_ws()?;
171                }
172                Some(b'}') => return Err(ParseError::UnmatchedClose { at }),
173                Some(_) => {
174                    return Err(ParseError::UnexpectedChar {
175                        found: self.parser.char_at_cursor()?,
176                        at,
177                    });
178                }
179            }
180        }
181        if self.parser.input.peek()?.is_none() {
182            return Ok(None);
183        }
184        let node = self.parser.value()?;
185        self.state = State::AfterValue;
186        Ok(Some(node))
187    }
188}
189
190impl<R: Read> Iterator for Values<R> {
191    type Item = Result<Node>;
192
193    fn next(&mut self) -> Option<Result<Node>> {
194        if self.state == State::Done {
195            return None;
196        }
197        let result = self.read_value();
198        if !matches!(result, Ok(Some(_))) {
199            self.state = State::Done;
200        }
201        result.transpose()
202    }
203}
204
205impl<R: Read> std::iter::FusedIterator for Values<R> {}
206
207/// `fill_buf` that retries on `Interrupted`.
208fn fill<R: BufRead>(reader: &mut R) -> io::Result<&[u8]> {
209    loop {
210        match reader.fill_buf() {
211            // Reborrowed to end the loop's borrow: a known limitation of the borrow checker.
212            Ok(_) => break,
213            Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
214            Err(e) => return Err(e),
215        }
216    }
217    reader.fill_buf()
218}
219
220/// Byte cursor over UTF-8 input that tracks the position.
221struct Input<R> {
222    inner: R,
223    /// Bytes taken from `inner` to look ahead.
224    pending: Vec<u8>,
225    pos: Position,
226}
227
228impl<R: BufRead> Input<R> {
229    fn new(inner: R) -> Self {
230        Self {
231            inner,
232            pending: Vec::new(),
233            pos: Position::START,
234        }
235    }
236
237    fn skip_bom(&mut self) -> Result<()> {
238        const BOM: [u8; 3] = [0xEF, 0xBB, 0xBF];
239        for (i, expected) in BOM.into_iter().enumerate() {
240            if self.peek_ahead(i)? != Some(expected) {
241                return Ok(());
242            }
243        }
244        self.pending.clear();
245        self.pos.offset = BOM.len() as u64;
246        Ok(())
247    }
248
249    fn peek_inner(&mut self) -> Result<Option<u8>> {
250        Ok(fill(&mut self.inner)?.first().copied())
251    }
252
253    fn peek(&mut self) -> Result<Option<u8>> {
254        match self.pending.first() {
255            Some(&b) => Ok(Some(b)),
256            None => self.peek_inner(),
257        }
258    }
259
260    /// Byte `n` positions after the cursor.
261    fn peek_ahead(&mut self, n: usize) -> Result<Option<u8>> {
262        while self.pending.len() <= n {
263            let Some(b) = self.peek_inner()? else {
264                return Ok(None);
265            };
266            self.inner.consume(1);
267            self.pending.push(b);
268        }
269        Ok(Some(self.pending[n]))
270    }
271
272    fn bump(&mut self) -> Result<Option<u8>> {
273        let byte = if self.pending.is_empty() {
274            let byte = self.peek_inner()?;
275            if byte.is_some() {
276                self.inner.consume(1);
277            }
278            byte
279        } else {
280            Some(self.pending.remove(0))
281        };
282        if let Some(b) = byte {
283            advance_byte(&mut self.pos, b);
284        }
285        Ok(byte)
286    }
287
288    /// Skips whitespace as ICU `u_isspace` defines it.
289    fn skip_ws(&mut self) -> Result<()> {
290        loop {
291            match self.space_len()? {
292                0 => return Ok(()),
293                1 => {
294                    self.scan(|b| !is_ascii_space(b), None)?;
295                }
296                len => {
297                    for _ in 0..len {
298                        self.bump()?;
299                    }
300                }
301            }
302        }
303    }
304
305    /// Length in bytes of the whitespace character under the cursor; 0 if there is none.
306    fn space_len(&mut self) -> Result<usize> {
307        let Some(first) = self.peek()? else {
308            return Ok(0);
309        };
310        let len = match first {
311            b if is_ascii_space(b) => 1,
312            // U+0085 and U+00A0.
313            0xC2 => match self.peek_ahead(1)? {
314                Some(0x85 | 0xA0) => 2,
315                _ => 0,
316            },
317            // The others are U+1680..U+3000.
318            0xE1..=0xE3 => {
319                let (Some(b1), Some(b2)) = (self.peek_ahead(1)?, self.peek_ahead(2)?) else {
320                    return Ok(0);
321                };
322                let c = u32::from(first & 0x0F) << 12
323                    | u32::from(b1 & 0x3F) << 6
324                    | u32::from(b2 & 0x3F);
325                let valid = b1 & 0xC0 == 0x80 && b2 & 0xC0 == 0x80;
326                if valid && char::from_u32(c).is_some_and(is_space) {
327                    3
328                } else {
329                    0
330                }
331            }
332            _ => 0,
333        };
334        Ok(len)
335    }
336
337    /// Moves over bytes until `stop` matches, a whole buffer at a time, copying them to `out`.
338    ///
339    /// Returns the stop byte, which stays unread, or `None` at the end of input.
340    fn scan(
341        &mut self,
342        stop: impl Fn(u8) -> bool,
343        mut out: Option<&mut Vec<u8>>,
344    ) -> Result<Option<u8>> {
345        // Bytes taken to look ahead: at most a few, one by one.
346        while let Some(&b) = self.pending.first() {
347            if stop(b) {
348                return Ok(Some(b));
349            }
350            self.bump()?;
351            if let Some(out) = out.as_deref_mut() {
352                out.push(b);
353            }
354        }
355        loop {
356            let buf = fill(&mut self.inner)?;
357            if buf.is_empty() {
358                return Ok(None);
359            }
360            let (len, found) = match buf.iter().position(|&b| stop(b)) {
361                Some(i) => (i, Some(buf[i])),
362                None => (buf.len(), None),
363            };
364            let chunk = &buf[..len];
365            if let Some(out) = out.as_deref_mut() {
366                out.extend_from_slice(chunk);
367            }
368            advance(&mut self.pos, chunk);
369            self.inner.consume(len);
370            if found.is_some() {
371                return Ok(found);
372            }
373        }
374    }
375
376    /// Reads one character, which may be an unpaired surrogate; `None` at the end of input.
377    fn next_code_point(&mut self) -> Result<Option<u32>> {
378        let Some(first) = self.bump()? else {
379            return Ok(None);
380        };
381        let (len, mut c) = match first {
382            0..=0x7F => return Ok(Some(u32::from(first))),
383            0xC0..=0xDF => (2, u32::from(first & 0x1F)),
384            0xE0..=0xEF => (3, u32::from(first & 0x0F)),
385            0xF0..=0xF7 => (4, u32::from(first & 0x07)),
386            _ => return Ok(Some(u32::from(char::REPLACEMENT_CHARACTER))),
387        };
388        for _ in 1..len {
389            match self.peek()? {
390                Some(b) if b & 0xC0 == 0x80 => {
391                    self.bump()?;
392                    c = c << 6 | u32::from(b & 0x3F);
393                }
394                _ => return Ok(Some(u32::from(char::REPLACEMENT_CHARACTER))),
395            }
396        }
397        Ok(Some(c))
398    }
399}
400
401fn is_ascii_space(b: u8) -> bool {
402    matches!(b, b'\t'..=b'\r' | 0x1C..=b' ')
403}
404
405struct Parser<R> {
406    input: Input<R>,
407    /// Items of all open lists, innermost last; reused between values.
408    ///
409    /// A closed list takes its items out into a `Vec` of the exact size,
410    /// so lists do not grow by reallocation.
411    items: Vec<Node>,
412}
413
414impl<R: BufRead> Parser<R> {
415    fn new(inner: R) -> Self {
416        Self {
417            input: Input::new(inner),
418            items: Vec::new(),
419        }
420    }
421
422    fn value(&mut self) -> Result<Node> {
423        match self.input.peek()? {
424            Some(b'{') => self.list(),
425            _ => self.scalar(),
426        }
427    }
428
429    /// Lists are parsed without recursion: forms can be deeply nested.
430    fn list(&mut self) -> Result<Node> {
431        // (position of `{`, index of its first item in `self.items`)
432        let mut stack: Vec<(Position, usize)> = Vec::new();
433        // Left over from a value that failed to parse.
434        self.items.clear();
435        loop {
436            // The cursor is always on `{` here.
437            let opened = self.input.pos;
438            self.input.bump()?;
439            self.input.skip_ws()?;
440            stack.push((opened, self.items.len()));
441            match self.input.peek()? {
442                // Empty list `{}`; the shared closing code follows.
443                Some(b'}') => {}
444                Some(b'{') => continue,
445                _ => {
446                    let item = self.scalar()?;
447                    self.items.push(item);
448                }
449            }
450            // Separators and closing brackets until a new nested list starts.
451            loop {
452                self.input.skip_ws()?;
453                let at = self.input.pos;
454                match self.input.peek()? {
455                    Some(b',') => {
456                        self.input.bump()?;
457                        self.input.skip_ws()?;
458                        if self.input.peek()? == Some(b'{') {
459                            break;
460                        }
461                        // Before `}` this is an empty atom: `{a,}` has two items, as the
462                        // platform writes an empty value.
463                        let item = self.scalar()?;
464                        self.items.push(item);
465                    }
466                    Some(b'}') => {
467                        self.input.bump()?;
468                        let (opened, first) = stack.pop().expect("list stack is not empty");
469                        let items = self.items.drain(first..).collect();
470                        let done = Node::parsed(Value::List(items), opened);
471                        if stack.is_empty() {
472                            return Ok(done);
473                        }
474                        self.items.push(done);
475                    }
476                    // Only after a string or a list: an atom runs up to `,` or `}`.
477                    Some(_) => {
478                        return Err(ParseError::UnexpectedChar {
479                            found: self.char_at_cursor()?,
480                            at,
481                        });
482                    }
483                    None => {
484                        let (opened, _) = stack.last().expect("list stack is not empty");
485                        return Err(ParseError::UnclosedList {
486                            opened: *opened,
487                            at,
488                        });
489                    }
490                }
491            }
492        }
493    }
494
495    fn scalar(&mut self) -> Result<Node> {
496        if self.input.peek()? == Some(b'"') {
497            self.string()
498        } else {
499            self.atom()
500        }
501    }
502
503    /// `"text"`: `""` is a quote, `"\xxxx` a UTF-16 code unit in hex, which the platform
504    /// writes for each surrogate. The text goes on after an escape without a new quote:
505    /// `"a"\d83d"\de00b"` is `a😀b`.
506    fn string(&mut self) -> Result<Node> {
507        let start = self.input.pos;
508        self.input.bump()?;
509        let mut bytes = Vec::new();
510        loop {
511            let part = bytes.len();
512            if self.input.scan(|b| b == b'"', Some(&mut bytes))?.is_none() {
513                return Err(ParseError::UnclosedString { start });
514            }
515            if std::str::from_utf8(&bytes[part..]).is_err() {
516                return Err(ParseError::InvalidUtf8 {
517                    context: "quoted string",
518                    at: start,
519                });
520            }
521            self.input.bump()?;
522            match self.input.peek()? {
523                Some(b'"') => {
524                    self.input.bump()?;
525                    bytes.push(b'"');
526                }
527                Some(b'\\') => {
528                    let at = self.input.pos;
529                    self.input.bump()?;
530                    let mut unit = 0;
531                    for _ in 0..4 {
532                        let digit = self.input.next_code_point()?.and_then(hex_digit);
533                        let Some(digit) = digit else {
534                            return Err(ParseError::InvalidEscape { at });
535                        };
536                        unit = unit << 4 | digit;
537                    }
538                    push_code_point(&mut bytes, unit);
539                }
540                _ => break,
541            }
542        }
543        Ok(Node::parsed(Value::Str(Text::from_wtf8(bytes)), start))
544    }
545
546    /// Everything up to `,`, `}` or the end of input, whitespace and line breaks included.
547    fn atom(&mut self) -> Result<Node> {
548        let start = self.input.pos;
549        let mut bytes = Vec::new();
550        self.input
551            .scan(|b| matches!(b, b',' | b'}'), Some(&mut bytes))?;
552        if std::str::from_utf8(&bytes).is_err() {
553            return Err(ParseError::InvalidUtf8 {
554                context: "value",
555                at: start,
556            });
557        }
558        Ok(Node::parsed(Value::Atom(Text::from_wtf8(bytes)), start))
559    }
560
561    /// Character under the cursor (for error messages); may span several bytes.
562    fn char_at_cursor(&mut self) -> Result<char> {
563        let c = self.input.next_code_point()?;
564        Ok(c.and_then(char::from_u32)
565            .unwrap_or(char::REPLACEMENT_CHARACTER))
566    }
567}
568
569/// A hex digit of an escape, as the platform reads it: ASCII and fullwidth `0-9`, `A-F`
570/// and `a-f` in any case, and any decimal digit of the Basic Multilingual Plane (`٣` is 3).
571fn hex_digit(c: u32) -> Option<u32> {
572    match c {
573        0x30..=0x39 => Some(c - 0x30),
574        0x41..=0x46 => Some(c - 0x41 + 10),
575        0x61..=0x66 => Some(c - 0x61 + 10),
576        0xFF21..=0xFF26 => Some(c - 0xFF21 + 10),
577        0xFF41..=0xFF46 => Some(c - 0xFF41 + 10),
578        _ => DIGIT_ZEROS
579            .iter()
580            .find(|&&zero| (zero..zero + 10).contains(&c))
581            .map(|zero| c - zero),
582    }
583}
584
585/// Zeros of the decimal digits (category Nd) of the Basic Multilingual Plane in Unicode 6.0,
586/// the version of ICU 4.6 the platform uses. Each zero is followed by the other nine digits.
587const DIGIT_ZEROS: [u32; 34] = [
588    0x0660, 0x06F0, 0x07C0, 0x0966, 0x09E6, 0x0A66, 0x0AE6, 0x0B66, 0x0BE6, 0x0C66, 0x0CE6, 0x0D66,
589    0x0E50, 0x0ED0, 0x0F20, 0x1040, 0x1090, 0x17E0, 0x1810, 0x1946, 0x19D0, 0x1A80, 0x1A90, 0x1B50,
590    0x1BB0, 0x1C40, 0x1C50, 0xA620, 0xA8D0, 0xA900, 0xA9D0, 0xAA50, 0xABF0, 0xFF10,
591];
592
593/// Moves the position over one byte already read.
594fn advance_byte(pos: &mut Position, byte: u8) {
595    pos.offset += 1;
596    if byte == b'\n' {
597        pos.line += 1;
598        pos.column = 1;
599    } else if byte & 0xC0 != 0x80 {
600        // UTF-8 continuation bytes do not advance the column.
601        pos.column += 1;
602    }
603}
604
605/// Moves the position over bytes already read.
606fn advance(pos: &mut Position, bytes: &[u8]) {
607    // UTF-8 continuation bytes do not advance the column.
608    let chars = |bytes: &[u8]| bytes.iter().filter(|&&b| b & 0xC0 != 0x80).count() as u64;
609    pos.offset += bytes.len() as u64;
610    match bytes.iter().rposition(|&b| b == b'\n') {
611        Some(last) => {
612            pos.line += bytes.iter().filter(|&&b| b == b'\n').count() as u64;
613            pos.column = 1 + chars(&bytes[last + 1..]);
614        }
615        None => pos.column += chars(bytes),
616    }
617}
618#[cfg(test)]
619mod tests {
620    use super::*;
621
622    fn atom(s: &str) -> Node {
623        Node::atom(s)
624    }
625
626    /// Source that yields one byte per `read` call.
627    struct ByteByByte<'a>(&'a [u8]);
628
629    impl Read for ByteByByte<'_> {
630        fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
631            match (self.0.split_first(), buf.first_mut()) {
632                (Some((&b, rest)), Some(slot)) => {
633                    *slot = b;
634                    self.0 = rest;
635                    Ok(1)
636                }
637                _ => Ok(0),
638            }
639        }
640    }
641
642    #[test]
643    fn parses_values() {
644        let node: Node = "\u{feff}{1,{\"a \"\"b\"\"\",x},{},}".parse().unwrap();
645        assert_eq!(
646            node,
647            Node::list([
648                atom("1"),
649                Node::list([Node::string("a \"b\""), atom("x")]),
650                Node::list([]),
651                atom(""),
652            ])
653        );
654    }
655
656    #[test]
657    fn keeps_base64_with_line_breaks() {
658        let node = Node::try_from("{1,\r\n{#base64:QUJD\r\r\nREVG\r\r\n},2}").unwrap();
659        assert_eq!(
660            node.get_path(&[1, 0]).and_then(Node::as_str),
661            Some("#base64:QUJD\r\r\nREVG\r\r\n")
662        );
663        assert_eq!(node[2].as_i64(), Some(2));
664    }
665
666    #[test]
667    fn atoms_run_to_comma_or_brace() {
668        let node = Node::try_from("{1 ,  2\r\n}").unwrap();
669        assert_eq!(node, Node::list([atom("1 "), atom("2\r\n")]));
670        assert_eq!(node[1].as_atom_trimmed(), Some("2"));
671        assert_eq!(node[1].as_i64(), Some(2));
672        assert_eq!(node.to_string(), "{1 ,2\r\n}");
673
674        let node = Node::try_from("{a{b,{x\"y\",1}}").unwrap();
675        assert_eq!(
676            node,
677            Node::list([atom("a{b"), Node::list([atom("x\"y\""), atom("1")])])
678        );
679        // Leading whitespace is skipped, Unicode whitespace included.
680        let node = Node::try_from("{\u{3000}\"a\"\u{a0},\u{2028}{}\u{85}, b}").unwrap();
681        assert_eq!(
682            node,
683            Node::list([Node::string("a"), Node::list([]), atom("b")])
684        );
685        assert_eq!(
686            Node::from_reader(ByteByByte("{\u{2003}x\u{2003}}".as_bytes())).unwrap(),
687            Node::list([atom("x\u{2003}")])
688        );
689    }
690
691    #[test]
692    fn empty_items() {
693        let items = |text: &str| Node::try_from(text).unwrap().into_list().unwrap();
694        assert_eq!(items("{}"), []);
695        assert_eq!(items("{ \r\n}"), []);
696        // The platform writes an empty value as nothing: `{a,}` has two items.
697        assert_eq!(items("{,}"), [atom(""), atom("")]);
698        assert_eq!(items("{a,}"), [atom("a"), atom("")]);
699        assert_eq!(items("{,a}"), [atom(""), atom("a")]);
700        assert_eq!(items("{,,}").len(), 3);
701    }
702
703    #[test]
704    fn unescapes_utf16_code_units() {
705        let node = Node::try_from(r#"{"a"\d83d"\de00b",""""\D800"""}"#).unwrap();
706        assert_eq!(node[0].as_quoted(), Some("a😀b"));
707        assert_eq!(node[0].to_string(), r#""a"\d83d"\de00b""#);
708        // An escape follows a quote: a string that starts with one starts with `""`.
709        let lone = node[1].as_text().unwrap();
710        let units = [u16::from(b'"'), 0xD800, u16::from(b'"')];
711        assert_eq!(lone.encode_utf16().collect::<Vec<_>>(), units);
712        assert_eq!(node[1].as_str(), None);
713        assert_eq!(node[1].to_string(), r#"""""\d800""""#);
714        // After a doubled quote the text goes on: `"\` there is not an escape.
715        assert_eq!(
716            Node::try_from(r#""""\d800""#).unwrap().as_quoted(),
717            Some(r#""\d800"#)
718        );
719        let node = Node::string(Text::from_utf16(&units[1..]));
720        assert_eq!(node.to_string(), r#"""\d800""""#);
721        assert_eq!(Node::try_from(node.to_string().as_str()).unwrap(), node);
722
723        // Fullwidth letters and any decimal digits are hex digits too.
724        let node = Node::try_from(r#"""\004f"\٠٠٤١""#).unwrap();
725        assert_eq!(node.as_quoted(), Some("OA"));
726    }
727
728    #[test]
729    fn reads_only_utf8() {
730        // A UTF-16 BOM is not UTF-8.
731        let err = Node::try_from(&[0xFF, 0xFE, b'{', 0, b'}', 0][..]).unwrap_err();
732        assert!(matches!(err, ParseError::InvalidUtf8 { .. }), "{err}");
733        // An unpaired surrogate written as UTF-8 bytes is not valid UTF-8 either.
734        let err = Node::try_from(&b"{\"\xED\xA0\x80\"}"[..]).unwrap_err();
735        assert!(matches!(err, ParseError::InvalidUtf8 { .. }), "{err}");
736    }
737
738    #[test]
739    fn deep_nesting_without_recursion() {
740        let depth = 5_000;
741        let text = format!("{}{}", "{".repeat(depth), "}".repeat(depth));
742        assert!(Node::try_from(text.as_str()).is_ok());
743    }
744
745    #[test]
746    fn reads_byte_by_byte() {
747        let text = "\u{feff}{1,\r\n{\"ру\"\"с\",#base64:AB\r\r\nC},Ж,\"\"}";
748        let expected = Node::try_from(text).unwrap();
749        let node = Node::from_reader(ByteByByte(text.as_bytes())).unwrap();
750        assert_eq!(node, expected);
751    }
752
753    #[test]
754    fn partial_bom_is_part_of_value() {
755        // U+F000 starts with byte 0xEF like a BOM: the bytes read are pushed back.
756        let node = Node::try_from("\u{f000}").unwrap();
757        assert_eq!(node, atom("\u{f000}"));
758    }
759
760    #[test]
761    fn propagates_io_errors() {
762        struct Failing;
763        impl Read for Failing {
764            fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
765                Err(io::Error::other("disk is on fire"))
766            }
767        }
768        let err = Node::from_reader(Failing).unwrap_err();
769        assert!(matches!(err, ParseError::Io(_)));
770        insta::assert_snapshot!(err, @"I/O error while reading input: disk is on fire");
771    }
772
773    #[test]
774    fn rejects_broken_input() {
775        assert!(Node::try_from("{1,2").is_err());
776        assert!(Node::try_from("{\"abc}").is_err());
777        assert!(Node::try_from("{1}}").is_err());
778    }
779
780    #[test]
781    fn reads_value_sequences() {
782        let text = "\u{feff}HEAD\r\n\r\n{1},\r\n{2,\"x\"},\r\nz,\r\n";
783        let mut values = Values::new(ByteByByte(text.as_bytes()));
784        assert_eq!(values.read_line().unwrap().as_deref(), Some("HEAD"));
785        assert_eq!(values.read_line().unwrap().as_deref(), Some(""));
786        let nodes: Vec<Node> = values.map(Result::unwrap).collect();
787        assert_eq!(
788            nodes,
789            [
790                Node::list([atom("1")]),
791                Node::list([atom("2"), Node::string("x")]),
792                atom("z")
793            ]
794        );
795        assert_eq!(nodes[1].position().unwrap().line, 4);
796
797        assert_eq!(Values::new("".as_bytes()).count(), 0);
798        let mut values = Values::new("{1}\r\n{2}".as_bytes());
799        assert!(values.next().unwrap().is_ok());
800        insta::assert_snapshot!(values.next().unwrap().unwrap_err(), @"unexpected character '{' at line 2, column 1: expected ',' or '}'");
801        assert!(values.next().is_none());
802        let err = Values::new("{1},{2".as_bytes())
803            .nth(1)
804            .unwrap()
805            .unwrap_err();
806        insta::assert_snapshot!(err, @"unexpected end of input at line 1, column 7: list opened at line 1, column 5 is not closed");
807    }
808
809    #[test]
810    fn reports_positions() {
811        let err = Node::try_from("{1,\r\n{2,\r\n  3").unwrap_err();
812        insta::assert_snapshot!(err, @"unexpected end of input at line 3, column 4: list opened at line 2, column 1 is not closed");
813        let err = Node::try_from("{\"ёж\" x}").unwrap_err();
814        insta::assert_snapshot!(err, @"unexpected character 'x' at line 1, column 7: expected ',' or '}'");
815        assert_eq!(err.position().map(|p| p.offset), Some(8));
816    }
817}