1 use std::str::Chars;
2 
3 /// Peekable iterator over a char sequence.
4 ///
5 /// Next characters can be peeked via `nth_char` method,
6 /// and position can be shifted forward via `bump` method.
7 pub(crate) struct Cursor<'a> {
8     initial_len: usize,
9     chars: Chars<'a>,
10     #[cfg(debug_assertions)]
11     prev: char,
12 }
13 
14 pub(crate) const EOF_CHAR: char = '\0';
15 
16 impl<'a> Cursor<'a> {
new(input: &'a str) -> Cursor<'a>17     pub(crate) fn new(input: &'a str) -> Cursor<'a> {
18         Cursor {
19             initial_len: input.len(),
20             chars: input.chars(),
21             #[cfg(debug_assertions)]
22             prev: EOF_CHAR,
23         }
24     }
25 
26     /// Returns the last eaten symbol (or `'\0'` in release builds).
27     /// (For debug assertions only.)
prev(&self) -> char28     pub(crate) fn prev(&self) -> char {
29         #[cfg(debug_assertions)]
30         {
31             self.prev
32         }
33 
34         #[cfg(not(debug_assertions))]
35         {
36             EOF_CHAR
37         }
38     }
39 
40     /// Returns nth character relative to the current cursor position.
41     /// If requested position doesn't exist, `EOF_CHAR` is returned.
42     /// However, getting `EOF_CHAR` doesn't always mean actual end of file,
43     /// it should be checked with `is_eof` method.
nth_char(&self, n: usize) -> char44     fn nth_char(&self, n: usize) -> char {
45         self.chars().nth(n).unwrap_or(EOF_CHAR)
46     }
47 
48     /// Peeks the next symbol from the input stream without consuming it.
first(&self) -> char49     pub(crate) fn first(&self) -> char {
50         self.nth_char(0)
51     }
52 
53     /// Peeks the second symbol from the input stream without consuming it.
second(&self) -> char54     pub(crate) fn second(&self) -> char {
55         self.nth_char(1)
56     }
57 
58     /// Checks if there is nothing more to consume.
is_eof(&self) -> bool59     pub(crate) fn is_eof(&self) -> bool {
60         self.chars.as_str().is_empty()
61     }
62 
63     /// Returns amount of already consumed symbols.
len_consumed(&self) -> usize64     pub(crate) fn len_consumed(&self) -> usize {
65         self.initial_len - self.chars.as_str().len()
66     }
67 
68     /// Returns a `Chars` iterator over the remaining characters.
chars(&self) -> Chars<'a>69     fn chars(&self) -> Chars<'a> {
70         self.chars.clone()
71     }
72 
73     /// Moves to the next character.
bump(&mut self) -> Option<char>74     pub(crate) fn bump(&mut self) -> Option<char> {
75         let c = self.chars.next()?;
76 
77         #[cfg(debug_assertions)]
78         {
79             self.prev = c;
80         }
81 
82         Some(c)
83     }
84 }
85