1use std::fs::File;
4use std::io::{self, BufRead, BufReader, Read};
5use std::path::Path;
6use std::str::FromStr;
7
8use crate::{Node, ParseError, Position, Value};
9
10type Result<T> = std::result::Result<T, ParseError>;
11
12const BOM: [u8; 3] = [0xEF, 0xBB, 0xBF];
13const BASE64: &[u8] = b"#base64:";
14
15impl Node {
16 pub fn from_reader<R: Read>(reader: R) -> Result<Node> {
21 parse(BufReader::new(reader))
22 }
23}
24
25impl TryFrom<&str> for Node {
26 type Error = ParseError;
27
28 fn try_from(text: &str) -> Result<Node> {
29 parse(text.as_bytes())
30 }
31}
32
33impl TryFrom<String> for Node {
34 type Error = ParseError;
35
36 fn try_from(text: String) -> Result<Node> {
37 parse(text.as_bytes())
38 }
39}
40
41impl TryFrom<&[u8]> for Node {
42 type Error = ParseError;
43
44 fn try_from(bytes: &[u8]) -> Result<Node> {
45 parse(bytes)
46 }
47}
48
49impl TryFrom<Vec<u8>> for Node {
50 type Error = ParseError;
51
52 fn try_from(bytes: Vec<u8>) -> Result<Node> {
53 parse(bytes.as_slice())
54 }
55}
56
57impl TryFrom<File> for Node {
58 type Error = ParseError;
59
60 fn try_from(file: File) -> Result<Node> {
61 Node::from_reader(file)
62 }
63}
64
65impl TryFrom<&Path> for Node {
66 type Error = ParseError;
67
68 fn try_from(path: &Path) -> Result<Node> {
69 let file = File::open(path).map_err(|source| ParseError::Open {
70 path: path.to_owned(),
71 source,
72 })?;
73 Node::from_reader(file)
74 }
75}
76
77impl FromStr for Node {
78 type Err = ParseError;
79
80 fn from_str(text: &str) -> Result<Node> {
81 parse(text.as_bytes())
82 }
83}
84
85fn parse<R: BufRead>(reader: R) -> Result<Node> {
86 let mut parser = Parser {
87 input: Input::new(reader),
88 };
89 parser.input.skip_bom()?;
90 parser.input.skip_ws()?;
91 if parser.input.peek()?.is_none() {
92 return Err(ParseError::EmptyInput);
93 }
94 let node = parser.value()?;
95 parser.input.skip_ws()?;
96 let at = parser.input.pos;
97 match parser.input.peek()? {
98 None => Ok(node),
99 Some(b'}') => Err(ParseError::UnmatchedClose { at }),
100 Some(_) => Err(ParseError::TrailingData { at }),
101 }
102}
103
104pub struct Values<R> {
125 parser: Parser<BufReader<R>>,
126 state: State,
127}
128
129#[derive(Clone, Copy, PartialEq, Eq)]
130enum State {
131 Fresh,
133 BeforeValue,
134 AfterValue,
135 Done,
136}
137
138impl<R: Read> Values<R> {
139 pub fn new(reader: R) -> Self {
140 Values {
141 parser: Parser {
142 input: Input::new(BufReader::new(reader)),
143 },
144 state: State::Fresh,
145 }
146 }
147
148 pub fn read_line(&mut self) -> Result<Option<String>> {
152 self.start()?;
153 let input = &mut self.parser.input;
154 let at = input.pos;
155 if input.peek()?.is_none() {
156 return Ok(None);
157 }
158 let mut bytes = Vec::new();
159 if input.scan(|b| b == b'\n', Some(&mut bytes))?.is_some() {
160 input.bump()?;
161 }
162 if bytes.last() == Some(&b'\r') {
163 bytes.pop();
164 }
165 String::from_utf8(bytes)
166 .map(Some)
167 .map_err(|_| ParseError::InvalidUtf8 {
168 context: "line",
169 at,
170 })
171 }
172
173 pub fn position(&self) -> Position {
175 self.parser.input.pos
176 }
177
178 fn start(&mut self) -> Result<()> {
179 if self.state == State::Fresh {
180 self.parser.input.skip_bom()?;
181 self.state = State::BeforeValue;
182 }
183 Ok(())
184 }
185
186 fn read_value(&mut self) -> Result<Option<Node>> {
187 self.start()?;
188 let input = &mut self.parser.input;
189 input.skip_ws()?;
190 if self.state == State::AfterValue {
191 let at = input.pos;
192 match input.peek()? {
193 None => return Ok(None),
194 Some(b',') => {
195 input.bump()?;
196 input.skip_ws()?;
197 }
198 Some(b'}') => return Err(ParseError::UnmatchedClose { at }),
199 Some(_) => {
200 return Err(ParseError::UnexpectedChar {
201 found: self.parser.char_at_cursor()?,
202 at,
203 });
204 }
205 }
206 }
207 if self.parser.input.peek()?.is_none() {
208 return Ok(None);
209 }
210 let node = self.parser.value()?;
211 self.state = State::AfterValue;
212 Ok(Some(node))
213 }
214}
215
216impl<R: Read> Iterator for Values<R> {
217 type Item = Result<Node>;
218
219 fn next(&mut self) -> Option<Result<Node>> {
220 if self.state == State::Done {
221 return None;
222 }
223 let result = self.read_value();
224 if !matches!(result, Ok(Some(_))) {
225 self.state = State::Done;
226 }
227 result.transpose()
228 }
229}
230
231impl<R: Read> std::iter::FusedIterator for Values<R> {}
232
233struct Input<R> {
235 inner: R,
236 pending: Vec<u8>,
238 pos: Position,
239}
240
241impl<R: BufRead> Input<R> {
242 fn new(inner: R) -> Self {
243 Self {
244 inner,
245 pending: Vec::new(),
246 pos: Position::START,
247 }
248 }
249
250 fn peek_inner(&mut self) -> Result<Option<u8>> {
251 loop {
252 match self.inner.fill_buf() {
253 Ok(buf) => return Ok(buf.first().copied()),
254 Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
255 Err(e) => return Err(e.into()),
256 }
257 }
258 }
259
260 fn peek(&mut self) -> Result<Option<u8>> {
261 match self.pending.first() {
262 Some(&b) => Ok(Some(b)),
263 None => self.peek_inner(),
264 }
265 }
266
267 fn bump(&mut self) -> Result<Option<u8>> {
268 let byte = if self.pending.is_empty() {
269 let byte = self.peek_inner()?;
270 if byte.is_some() {
271 self.inner.consume(1);
272 }
273 byte
274 } else {
275 Some(self.pending.remove(0))
276 };
277 if let Some(b) = byte {
278 advance(&mut self.pos, &[b]);
279 }
280 Ok(byte)
281 }
282
283 fn skip_bom(&mut self) -> Result<()> {
284 let mut seen = Vec::new();
285 for expected in BOM {
286 match self.peek_inner()? {
287 Some(b) if b == expected => {
288 self.inner.consume(1);
289 seen.push(b);
290 }
291 _ => break,
292 }
293 }
294 if seen.len() == BOM.len() {
295 self.pos.offset += BOM.len() as u64;
296 } else {
297 self.pending = seen;
298 }
299 Ok(())
300 }
301
302 fn skip_ws(&mut self) -> Result<()> {
303 self.scan(|b| !b.is_ascii_whitespace(), None)?;
304 Ok(())
305 }
306
307 fn scan(
311 &mut self,
312 stop: impl Fn(u8) -> bool,
313 mut out: Option<&mut Vec<u8>>,
314 ) -> Result<Option<u8>> {
315 while let Some(&b) = self.pending.first() {
317 if stop(b) {
318 return Ok(Some(b));
319 }
320 self.bump()?;
321 if let Some(out) = out.as_deref_mut() {
322 out.push(b);
323 }
324 }
325 loop {
326 let buf = loop {
327 match self.inner.fill_buf() {
328 Ok(buf) => break buf,
329 Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
330 Err(e) => return Err(e.into()),
331 }
332 };
333 if buf.is_empty() {
334 return Ok(None);
335 }
336 let (len, found) = match buf.iter().position(|&b| stop(b)) {
337 Some(i) => (i, Some(buf[i])),
338 None => (buf.len(), None),
339 };
340 let chunk = &buf[..len];
341 if let Some(out) = out.as_deref_mut() {
342 out.extend_from_slice(chunk);
343 }
344 advance(&mut self.pos, chunk);
345 self.inner.consume(len);
346 if found.is_some() {
347 return Ok(found);
348 }
349 }
350 }
351}
352
353struct Parser<R> {
354 input: Input<R>,
355}
356
357impl<R: BufRead> Parser<R> {
358 fn value(&mut self) -> Result<Node> {
359 match self.input.peek()? {
360 Some(b'{') => self.list(),
361 _ => self.scalar(),
362 }
363 }
364
365 fn list(&mut self) -> Result<Node> {
367 let mut stack: Vec<(Position, Vec<Node>)> = Vec::new();
369 loop {
370 let opened = self.input.pos;
372 self.input.bump()?;
373 self.input.skip_ws()?;
374 stack.push((opened, Vec::new()));
375 match self.input.peek()? {
376 Some(b'}') => {}
378 Some(b'{') => continue,
379 _ => {
380 let item = self.scalar()?;
381 push(&mut stack, item);
382 }
383 }
384 loop {
386 self.input.skip_ws()?;
387 let at = self.input.pos;
388 match self.input.peek()? {
389 Some(b',') => {
390 self.input.bump()?;
391 self.input.skip_ws()?;
392 if self.input.peek()? == Some(b'{') {
393 break;
394 }
395 let item = self.scalar()?;
396 push(&mut stack, item);
397 }
398 Some(b'}') => {
399 self.input.bump()?;
400 let (opened, items) = stack.pop().expect("list stack is not empty");
401 let done = Node::parsed(Value::List(items), opened);
402 match stack.last_mut() {
403 Some((_, parent)) => parent.push(done),
404 None => return Ok(done),
405 }
406 }
407 Some(_) => {
408 return Err(ParseError::UnexpectedChar {
409 found: self.char_at_cursor()?,
410 at,
411 });
412 }
413 None => {
414 let (opened, _) = stack.last().expect("list stack is not empty");
415 return Err(ParseError::UnclosedList {
416 opened: *opened,
417 at,
418 });
419 }
420 }
421 }
422 }
423 }
424
425 fn scalar(&mut self) -> Result<Node> {
426 if self.input.peek()? == Some(b'"') {
427 self.string()
428 } else {
429 self.atom()
430 }
431 }
432
433 fn string(&mut self) -> Result<Node> {
434 let start = self.input.pos;
435 self.input.bump()?;
436 let mut bytes = Vec::new();
437 loop {
438 if self.input.scan(|b| b == b'"', Some(&mut bytes))?.is_none() {
439 return Err(ParseError::UnclosedString { start });
440 }
441 self.input.bump()?;
442 if self.input.peek()? != Some(b'"') {
444 break;
445 }
446 self.input.bump()?;
447 bytes.push(b'"');
448 }
449 let value = String::from_utf8(bytes).map_err(|_| ParseError::InvalidUtf8 {
450 context: "quoted string",
451 at: start,
452 })?;
453 Ok(Node::parsed(Value::Str(value), start))
454 }
455
456 fn atom(&mut self) -> Result<Node> {
457 let start = self.input.pos;
458 let mut bytes = Vec::new();
459 let delimiter = |b: u8| matches!(b, b',' | b'{' | b'}');
460 let stop = self.input.scan(
461 |b| delimiter(b) || b.is_ascii_whitespace(),
462 Some(&mut bytes),
463 )?;
464 if stop.is_some_and(|b| b.is_ascii_whitespace()) && bytes.starts_with(BASE64) {
466 self.input.scan(delimiter, Some(&mut bytes))?;
467 }
468 let value = String::from_utf8(bytes).map_err(|_| ParseError::InvalidUtf8 {
469 context: "value",
470 at: start,
471 })?;
472 Ok(Node::parsed(Value::Atom(value), start))
473 }
474
475 fn char_at_cursor(&mut self) -> Result<char> {
477 let mut bytes = Vec::with_capacity(4);
478 while let Some(b) = self.input.peek()? {
479 if !bytes.is_empty() && b & 0xC0 != 0x80 || bytes.len() == 4 {
480 break;
481 }
482 bytes.push(b);
483 self.input.bump()?;
484 }
485 Ok(std::str::from_utf8(&bytes)
486 .ok()
487 .and_then(|s| s.chars().next())
488 .unwrap_or(char::REPLACEMENT_CHARACTER))
489 }
490}
491
492fn advance(pos: &mut Position, bytes: &[u8]) {
494 let chars = |bytes: &[u8]| bytes.iter().filter(|&&b| b & 0xC0 != 0x80).count() as u64;
496 pos.offset += bytes.len() as u64;
497 match bytes.iter().rposition(|&b| b == b'\n') {
498 Some(last) => {
499 pos.line += bytes.iter().filter(|&&b| b == b'\n').count() as u64;
500 pos.column = 1 + chars(&bytes[last + 1..]);
501 }
502 None => pos.column += chars(bytes),
503 }
504}
505
506fn push(stack: &mut [(Position, Vec<Node>)], item: Node) {
507 stack
508 .last_mut()
509 .expect("list stack is not empty")
510 .1
511 .push(item);
512}
513
514#[cfg(test)]
515mod tests {
516 use super::*;
517
518 fn atom(s: &str) -> Node {
519 Node::atom(s)
520 }
521
522 struct ByteByByte<'a>(&'a [u8]);
524
525 impl Read for ByteByByte<'_> {
526 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
527 match (self.0.split_first(), buf.first_mut()) {
528 (Some((&b, rest)), Some(slot)) => {
529 *slot = b;
530 self.0 = rest;
531 Ok(1)
532 }
533 _ => Ok(0),
534 }
535 }
536 }
537
538 #[test]
539 fn parses_values() {
540 let node: Node = "\u{feff}{1,{\"a \"\"b\"\"\",x},{},}".parse().unwrap();
541 assert_eq!(
542 node,
543 Node::list([
544 atom("1"),
545 Node::list([Node::string("a \"b\""), atom("x")]),
546 Node::list([]),
547 atom(""),
548 ])
549 );
550 }
551
552 #[test]
553 fn keeps_base64_with_line_breaks() {
554 let node = Node::try_from("{1,\r\n{#base64:QUJD\r\r\nREVG\r\r\n},2}").unwrap();
555 assert_eq!(
556 node.get_path(&[1, 0]).and_then(Node::as_str),
557 Some("#base64:QUJD\r\r\nREVG\r\r\n")
558 );
559 assert_eq!(node[2].as_i64(), Some(2));
560 }
561
562 #[test]
563 fn deep_nesting_without_recursion() {
564 let depth = 5_000;
565 let text = format!("{}{}", "{".repeat(depth), "}".repeat(depth));
566 assert!(Node::try_from(text).is_ok());
567 }
568
569 #[test]
570 fn reads_byte_by_byte() {
571 let text = "\u{feff}{1,\r\n{\"ру\"\"с\",#base64:AB\r\r\nC},Ж,\"\"}";
572 let expected = Node::try_from(text).unwrap();
573 let node = Node::from_reader(ByteByByte(text.as_bytes())).unwrap();
574 assert_eq!(node, expected);
575 }
576
577 #[test]
578 fn partial_bom_is_part_of_value() {
579 let node = Node::try_from("\u{f000}").unwrap();
581 assert_eq!(node, atom("\u{f000}"));
582 }
583
584 #[test]
585 fn propagates_io_errors() {
586 struct Failing;
587 impl Read for Failing {
588 fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
589 Err(io::Error::other("disk is on fire"))
590 }
591 }
592 let err = Node::from_reader(Failing).unwrap_err();
593 assert!(matches!(err, ParseError::Io(_)));
594 insta::assert_snapshot!(err, @"I/O error while reading input: disk is on fire");
595 }
596
597 #[test]
598 fn rejects_broken_input() {
599 assert!(Node::try_from("{1,2").is_err());
600 assert!(Node::try_from("{\"abc}").is_err());
601 assert!(Node::try_from("{1}}").is_err());
602 }
603
604 #[test]
605 fn reads_value_sequences() {
606 let text = "\u{feff}HEAD\r\n\r\n{1},\r\n{2,\"x\"},\r\nz,\r\n";
607 let mut values = Values::new(ByteByByte(text.as_bytes()));
608 assert_eq!(values.read_line().unwrap().as_deref(), Some("HEAD"));
609 assert_eq!(values.read_line().unwrap().as_deref(), Some(""));
610 let nodes: Vec<Node> = values.map(Result::unwrap).collect();
611 assert_eq!(
612 nodes,
613 [
614 Node::list([atom("1")]),
615 Node::list([atom("2"), Node::string("x")]),
616 atom("z")
617 ]
618 );
619 assert_eq!(nodes[1].position().unwrap().line, 4);
620
621 assert_eq!(Values::new("".as_bytes()).count(), 0);
622 let mut values = Values::new("{1}\r\n{2}".as_bytes());
623 assert!(values.next().unwrap().is_ok());
624 insta::assert_snapshot!(values.next().unwrap().unwrap_err(), @"unexpected character '{' at line 2, column 1: expected ',' or '}'");
625 assert!(values.next().is_none());
626 let err = Values::new("{1},{2".as_bytes())
627 .nth(1)
628 .unwrap()
629 .unwrap_err();
630 insta::assert_snapshot!(err, @"unexpected end of input at line 1, column 7: list opened at line 1, column 5 is not closed");
631 }
632
633 #[test]
634 fn reports_positions() {
635 let err = Node::try_from("{1,\r\n{2,\r\n 3").unwrap_err();
636 insta::assert_snapshot!(err, @"unexpected end of input at line 3, column 4: list opened at line 2, column 1 is not closed");
637 let err = Node::try_from("{\"ёж\" x}").unwrap_err();
638 insta::assert_snapshot!(err, @"unexpected character 'x' at line 1, column 7: expected ',' or '}'");
639 assert_eq!(err.position().map(|p| p.offset), Some(8));
640 }
641}