1use std::ops::Range;
2
3pub struct Reader<'a> {
4 source: &'a [u8],
5 position: usize,
6}
7
8impl<'a> Reader<'a> {
9 pub fn new(input: &'a str) -> Self {
10 Self {
11 source: input.as_bytes(),
12 position: 0,
13 }
14 }
15
16 pub fn from_bytes(input: &'a [u8]) -> Self {
17 Self {
18 source: input,
19 position: 0,
20 }
21 }
22
23 #[inline]
24 pub fn get_position(&self) -> usize {
25 self.position
26 }
27
28 #[inline]
29 pub fn slice(&self, range: Range<usize>) -> &'a str {
30 unsafe { std::str::from_utf8_unchecked(&self.source[range]) }
33 }
34
35 #[inline]
36 pub fn peek(&self) -> Option<u8> {
37 self.source.get(self.position).copied()
38 }
39
40 #[inline]
41 pub fn next_while_list(&mut self, characters: &[u8]) {
42 let len = self.source.len();
43 while self.position < len && characters.contains(&self.source[self.position]) {
44 self.position += 1;
45 }
46 }
47
48 #[inline]
49 pub fn next_while(&mut self, character: u8) {
50 let len = self.source.len();
51 while self.position < len && self.source[self.position] == character {
52 self.position += 1;
53 }
54 }
55
56 #[inline]
57 pub fn next_until_list(&mut self, characters: &[u8]) {
58 let len = self.source.len();
59 while self.position < len && !characters.contains(&self.source[self.position]) {
60 self.position += 1;
61 }
62 }
63
64 pub fn next_until(&mut self, character: u8) {
66 let len = self.source.len();
67 while self.position < len && self.source[self.position] != character {
68 self.position += 1;
69 }
70 }
71
72 pub fn skip(&mut self) {
73 if self.position < self.source.len() {
74 self.position += 1;
75 }
76 }
77
78 pub fn eof(&self) -> bool {
79 if self.position >= self.source.len() {
80 return true;
81 }
82
83 self.source[self.position..]
84 .iter()
85 .all(|b| b.is_ascii_whitespace())
86 }
87
88 pub fn match_ignore_case(&self, s: &str) -> bool {
89 if self.position + s.len() > self.source.len() {
90 return false;
91 }
92 let slice = &self.source[self.position..self.position + s.len()];
93 slice.eq_ignore_ascii_case(s.as_bytes())
94 }
95}
96
97impl<'a> Iterator for Reader<'a> {
98 type Item = u8;
99
100 #[inline]
101 fn next(&mut self) -> Option<Self::Item> {
102 if self.position < self.source.len() {
103 let b = self.source[self.position];
104 self.position += 1;
105 Some(b)
106 } else {
107 None
108 }
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 fn basic_iterator() {
118 let my_string = String::from("Hello World");
119 let mut reader = Reader::new(&my_string);
120
121 assert_eq!(reader.get_position(), 0);
122 assert_eq!(reader.next(), Some(b'H'));
123
124 assert_eq!(reader.get_position(), 1);
125 assert_eq!(reader.next(), Some(b'e'));
126
127 assert_eq!(reader.get_position(), 2);
128 assert_eq!(reader.next(), Some(b'l'));
129
130 assert_eq!(reader.get_position(), 3);
131 assert_eq!(reader.next(), Some(b'l'));
132
133 assert_eq!(reader.get_position(), 4);
134 assert_eq!(reader.next(), Some(b'o'));
135
136 assert_eq!(reader.get_position(), 5);
137 assert_eq!(reader.next(), Some(b' '));
138
139 assert_eq!(reader.get_position(), 6);
140 assert_eq!(reader.next(), Some(b'W'));
141
142 assert_eq!(reader.get_position(), 7);
143 assert_eq!(reader.next(), Some(b'o'));
144
145 assert_eq!(reader.get_position(), 8);
146 assert_eq!(reader.next(), Some(b'r'));
147
148 assert_eq!(reader.get_position(), 9);
149 assert_eq!(reader.next(), Some(b'l'));
150
151 assert_eq!(reader.get_position(), 10);
152 assert_eq!(reader.peek(), Some(b'd'));
153
154 assert_eq!(reader.get_position(), 10);
155 assert_eq!(reader.next(), Some(b'd'));
156
157 assert_eq!(reader.slice(0..5), "Hello");
158 }
159}