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