1 | use crate::io; |
2 | |
3 | pub struct LineColIterator<I> { |
4 | iter: I, |
5 | |
6 | /// Index of the current line. Characters in the first line of the input |
7 | /// (before the first newline character) are in line 1. |
8 | line: usize, |
9 | |
10 | /// Index of the current column. The first character in the input and any |
11 | /// characters immediately following a newline character are in column 1. |
12 | /// The column is 0 immediately after a newline character has been read. |
13 | col: usize, |
14 | |
15 | /// Byte offset of the start of the current line. This is the sum of lengths |
16 | /// of all previous lines. Keeping track of things this way allows efficient |
17 | /// computation of the current line, column, and byte offset while only |
18 | /// updating one of the counters in `next()` in the common case. |
19 | start_of_line: usize, |
20 | } |
21 | |
22 | impl<I> LineColIterator<I> |
23 | where |
24 | I: Iterator<Item = io::Result<u8>>, |
25 | { |
26 | pub fn new(iter: I) -> LineColIterator<I> { |
27 | LineColIterator { |
28 | iter, |
29 | line: 1, |
30 | col: 0, |
31 | start_of_line: 0, |
32 | } |
33 | } |
34 | |
35 | pub fn line(&self) -> usize { |
36 | self.line |
37 | } |
38 | |
39 | pub fn col(&self) -> usize { |
40 | self.col |
41 | } |
42 | |
43 | pub fn byte_offset(&self) -> usize { |
44 | self.start_of_line + self.col |
45 | } |
46 | } |
47 | |
48 | impl<I> Iterator for LineColIterator<I> |
49 | where |
50 | I: Iterator<Item = io::Result<u8>>, |
51 | { |
52 | type Item = io::Result<u8>; |
53 | |
54 | fn next(&mut self) -> Option<io::Result<u8>> { |
55 | match self.iter.next() { |
56 | None => None, |
57 | Some(Ok(b' \n' )) => { |
58 | self.start_of_line += self.col + 1; |
59 | self.line += 1; |
60 | self.col = 0; |
61 | Some(Ok(b' \n' )) |
62 | } |
63 | Some(Ok(c)) => { |
64 | self.col += 1; |
65 | Some(Ok(c)) |
66 | } |
67 | Some(Err(e)) => Some(Err(e)), |
68 | } |
69 | } |
70 | } |
71 | |