1use super::{LoadError, LoadResult, ReadExt};
2use std::io::BufRead;
3
4const EOL: u8 = 0xA;
5
6pub(crate) fn parse_header<R: BufRead>(mut reader: R) -> LoadResult<(usize, usize, R)> {
7 loop {
9 let mut next_is_eol = || reader.read_byte().map(|b| b == EOL);
10 if next_is_eol()? && next_is_eol()? {
11 break;
12 }
13 }
14
15 DimParser::new(reader)?.parse()
16}
17
18struct DimParser<R> {
19 reader: R,
20 byte: u8,
21}
22
23impl<R: BufRead> DimParser<R> {
24 fn new(mut reader: R) -> LoadResult<Self> {
25 let byte = reader.read_byte()?;
26 Ok(Self { reader, byte })
27 }
28
29 fn parse(mut self) -> LoadResult<(usize, usize, R)> {
30 self.eat_spaces()?;
31 let y = self.expect_y()?;
32 self.expect_spaces()?;
33 let x = self.expect_x()?;
34 self.eat_spaces()?;
35 self.expect_eol()?;
36 Ok((x, y, self.reader))
37 }
38
39 fn eat_spaces(&mut self) -> LoadResult<bool> {
40 let mut ate_any = false;
41 while self.byte == b' ' {
42 ate_any = true;
43 self.eat()?;
44 }
45 Ok(ate_any)
46 }
47
48 fn expect_spaces(&mut self) -> LoadResult {
49 match self.eat_spaces()? {
50 true => Ok(()),
51 false => Err(LoadError::Header),
52 }
53 }
54
55 fn eat(&mut self) -> LoadResult<u8> {
56 self.byte = self.reader.read_byte()?;
57 Ok(self.byte)
58 }
59
60 fn expect<B: AsRef<[u8]>>(&mut self, bytes: B) -> LoadResult {
61 for &byte in bytes.as_ref() {
62 if self.byte == byte {
63 self.eat()?;
64 } else {
65 return Err(LoadError::Header);
66 }
67 }
68 Ok(())
69 }
70
71 fn expect_y(&mut self) -> LoadResult<usize> {
72 self.expect(b"-Y")?;
73 self.expect_spaces()?;
74 self.expect_usize()
75 }
76
77 fn expect_x(&mut self) -> LoadResult<usize> {
78 self.expect(b"+X")?;
79 self.expect_spaces()?;
80 self.expect_usize()
81 }
82
83 fn expect_usize(&mut self) -> LoadResult<usize> {
84 let mut value: usize = 0;
85 if !self.byte.is_ascii_digit() {
86 return Err(LoadError::Header);
87 }
88 loop {
89 value = value
90 .checked_mul(10)
91 .ok_or(LoadError::Header)?
92 .checked_add((self.byte - b'0') as usize)
93 .ok_or(LoadError::Header)?;
94 if !self.eat()?.is_ascii_digit() {
95 return Ok(value);
96 }
97 }
98 }
99
100 fn expect_eol(&mut self) -> LoadResult {
101 match self.byte {
102 EOL => Ok(()),
103 _ => Err(LoadError::Header),
104 }
105 }
106}