pochoir_common/
stream_parser.rs1use std::{
2 ops::Range,
3 path::{Path, PathBuf},
4};
5
6use crate::{Error, Result, Spanned};
7
8#[derive(Debug, Clone)]
9pub struct StreamParser<'a> {
10 file_path: PathBuf,
11 value: &'a str,
12 index: usize,
13}
14
15impl<'a> StreamParser<'a> {
16 pub fn new<T: AsRef<Path>>(file_path: T, value: &'a str) -> Self {
17 Self {
18 file_path: file_path.as_ref().into(),
19 value,
20 index: 0,
21 }
22 }
23
24 pub fn file_path(&self) -> &Path {
26 &self.file_path
27 }
28
29 pub fn index(&self) -> usize {
31 self.index
32 }
33
34 pub fn set_index(&mut self, index: usize) {
36 self.index = index;
37 }
38
39 pub fn next(&self) -> Result<char> {
45 self.value.as_bytes().get(self.index).copied().map_or_else(
46 || {
47 Err(Spanned::new(Error::UnexpectedEoi)
48 .with_span(if self.value.is_empty() {
49 0..0
50 } else {
51 self.value.len() - 1..self.value.len()
52 })
53 .with_file_path(&self.file_path))
54 },
55 |v| Ok(v as char),
56 )
57 }
58
59 #[allow(clippy::missing_panics_doc)]
65 pub fn take_next(&mut self) -> Result<char> {
66 if self.index < self.value.len() {
67 self.index += 1;
68
69 Ok(self.value.as_bytes().get(self.index - 1).copied().unwrap() as char)
70 } else {
71 Err(Spanned::new(Error::UnexpectedEoi)
72 .with_span(if self.value.is_empty() {
73 0..0
74 } else {
75 self.value.len() - 1..self.value.len()
76 })
77 .with_file_path(&self.file_path))
78 }
79 }
80
81 pub fn peek(&self, n: usize) -> Result<&'a str> {
87 self.value.get(self.index..self.index + n).ok_or(
88 Spanned::new(Error::UnexpectedEoi)
89 .with_span(if self.value.is_empty() {
90 0..0
91 } else {
92 self.value.len() - 1..self.value.len()
93 })
94 .with_file_path(&self.file_path),
95 )
96 }
97
98 pub fn peek_exact(&self, val: &str) -> bool {
102 self.value.get(self.index..self.index + val.len()) == Some(val)
103 }
104
105 pub fn peek_early(&self, n: usize, e: usize) -> Result<&'a str> {
111 self.value.get(self.index + e..self.index + e + n).ok_or(
112 Spanned::new(Error::UnexpectedEoi)
113 .with_span(if self.value.is_empty() {
114 0..0
115 } else {
116 self.value.len() - 1..self.value.len()
117 })
118 .with_file_path(&self.file_path),
119 )
120 }
121
122 pub fn peek_early_exact(&self, val: &str, e: usize) -> bool {
126 self.value.get(self.index + e..self.index + e + val.len()) == Some(val)
127 }
128
129 pub fn take(&mut self, n: usize) -> Result<()> {
135 if self.index < self.value.len() {
136 self.index += n;
137 Ok(())
138 } else {
139 Err(Spanned::new(Error::UnexpectedEoi)
140 .with_span(if self.value.is_empty() {
141 0..0
142 } else {
143 self.value.len() - 1..self.value.len()
144 })
145 .with_file_path(&self.file_path))
146 }
147 }
148
149 #[allow(clippy::missing_panics_doc)]
158 pub fn take_exact(&mut self, val: &str) -> Result<()> {
159 if self.index + val.len() <= self.value.len() {
160 let span = self.index
161 ..self.index
162 + val.len()
163 + (0..4)
164 .find(|n| self.value.is_char_boundary(self.index + val.len() + n))
165 .expect("a unicode codepoint should at most be 4 bytes");
166 let value = &self.value[span.clone()];
167
168 if value == val {
169 self.index += val.len();
170 Ok(())
171 } else {
172 Err(Spanned::new(Error::UnexpectedInput {
173 expected: val.to_string(),
174 found: value.to_string(),
175 })
176 .with_span(span)
177 .with_file_path(&self.file_path))
178 }
179 } else {
180 Err(Spanned::new(Error::ExpectedFoundEoi {
181 expected: val.to_string(),
182 })
183 .with_span(if self.value.is_empty() {
184 0..0
185 } else {
186 self.value.len() - 1..self.value.len()
187 })
188 .with_file_path(&self.file_path))
189 }
190 }
191
192 pub fn trim(&mut self) {
194 self.take_while(|(_, ch)| char::is_whitespace(ch));
195 }
196
197 pub fn is_eoi(&self) -> bool {
198 self.index >= self.value.len()
199 }
200
201 pub fn get_range(&self, range: Range<usize>) -> Result<&'a str> {
207 self.value.get(range).ok_or_else(|| {
208 Spanned::new(Error::UnexpectedEoi)
209 .with_span(if self.value.is_empty() {
210 0..0
211 } else {
212 self.value.len() - 1..self.value.len()
213 })
214 .with_file_path(&self.file_path)
215 })
216 }
217
218 pub fn take_while<F: FnMut((usize, char)) -> bool>(&mut self, mut f: F) -> &'a str {
219 let old_index = self.index;
220 let new_index = &self.value[self.index..]
221 .char_indices()
222 .find_map(|(i, ch)| {
223 if f((self.index + i, ch)) {
224 None
225 } else {
226 Some(self.index + i)
227 }
228 })
229 .unwrap_or(self.value.len());
230
231 self.index = *new_index;
232 &self.value[old_index..*new_index]
233 }
234
235 pub fn take_while_peekable<F: FnMut(usize, char, Option<char>) -> bool>(
236 &mut self,
237 mut f: F,
238 ) -> &'a str {
239 let old_index = self.index;
240 let mut iter = self.value[self.index..].char_indices().peekable();
241
242 while let Some(i) = iter.next() {
243 if !f(i.0, i.1, iter.peek().map(|(_, ch)| *ch)) {
244 self.index += i.0;
245 return &self.value[old_index..self.index];
246 }
247 }
248
249 self.index = self.value.len();
250 &self.value[old_index..]
251 }
252
253 pub fn take_until(&mut self, val: &str) -> Result<&'a str> {
261 let old_index = self.index;
262 let mut new_index = old_index;
263
264 while let Some(value) = &self.value.get(new_index..new_index + val.len()) {
265 if *value == val {
266 self.index = new_index;
267 return Ok(&self.value[old_index..self.index]);
268 }
269
270 new_index += 1;
271 }
272
273 Err(Spanned::new(Error::ExpectedFoundEoi {
274 expected: val.to_string(),
275 })
276 .with_span(if self.value.is_empty() {
277 0..0
278 } else {
279 self.value.len() - 1..self.value.len()
280 })
281 .with_file_path(&self.file_path))
282 }
283
284 pub fn take_until_index(&mut self, val: &str) -> Result<usize> {
292 let old_index = self.index;
293 let mut new_index = old_index;
294
295 while let Some(value) = &self.value.get(new_index..new_index + val.len()) {
296 if *value == val {
297 self.index = new_index;
298 return Ok(self.index);
299 }
300
301 new_index += 1;
302 }
303
304 Err(Spanned::new(Error::ExpectedFoundEoi {
305 expected: val.to_string(),
306 })
307 .with_span(if self.value.is_empty() {
308 0..0
309 } else {
310 self.value.len() - 1..self.value.len()
311 })
312 .with_file_path(&self.file_path))
313 }
314
315 pub fn take_until_eoi(&mut self) -> &'a str {
316 let old_index = self.index;
317 self.index = self.value.len();
318 &self.value[old_index..]
319 }
320}