1#[cfg(feature = "regex")]
2use regex::Regex;
3
4pub struct MultiLineStream<'a> {
9 pub source: &'a str,
10 position: usize,
11}
12
13impl<'a> MultiLineStream<'a> {
14 pub fn new(source: &'a str, position: usize) -> MultiLineStream<'a> {
15 MultiLineStream { source, position }
16 }
17
18 pub fn eos(&self) -> bool {
19 self.source.len() <= self.position
20 }
21
22 pub fn pos(&self) -> usize {
23 self.position
24 }
25
26 pub fn go_back(&mut self, n: usize) {
27 self.position -= n;
28 }
29
30 pub fn advance(&mut self, n: usize) {
31 self.position += n;
32 }
33
34 pub fn go_to_end(&mut self) {
35 self.position = self.source.len();
36 }
37
38 pub fn peek_char(&self, n: isize) -> Option<u8> {
39 let index = if n >= 0 {
40 self.position + n as usize
41 } else {
42 self.position - (-n) as usize
43 };
44 Some(self.source.bytes().nth(index)?)
45 }
46
47 pub fn advance_if_char(&mut self, ch: u8) -> bool {
48 if let Some(char) = self.source.bytes().nth(self.position) {
49 if char == ch {
50 self.position += 1;
51 return true;
52 }
53 }
54 false
55 }
56
57 pub fn advance_if_chars(&mut self, ch: &str) -> bool {
58 if self.position + ch.len() > self.source.len() {
59 return false;
60 }
61
62 if !self
63 .source
64 .get(self.position..self.position + ch.len())
65 .is_some_and(|v| v == ch)
66 {
67 return false;
68 }
69
70 self.advance(ch.len());
71 true
72 }
73
74 #[cfg(feature = "regex")]
75 pub fn advance_if_regexp(&mut self, regexp: &Regex) -> Option<&'a str> {
76 let haystack = &self.source[self.position..];
77 let captures = regexp.captures(haystack)?;
78 let m = captures.get(0).unwrap();
79 self.position += m.end();
80 Some(m.as_str())
81 }
82
83 #[cfg(feature = "regex")]
84 pub fn advance_until_regexp(&mut self, regexp: &Regex) -> Option<&'a str> {
85 let haystack = &self.source[self.position..];
86 if let Some(captures) = regexp.captures(haystack) {
87 let m = captures.get(0).unwrap();
88 self.position += m.start();
89 Some(m.as_str())
90 } else {
91 self.go_to_end();
92 None
93 }
94 }
95
96 pub fn advance_until_char(&mut self, ch: u8) -> bool {
97 while self.position < self.source.len() {
98 if self.source.bytes().nth(self.position) == Some(ch) {
99 return true;
100 }
101 self.advance(1);
102 }
103 false
104 }
105
106 pub fn advance_until_chars(&mut self, ch: &str) -> bool {
107 while self.position + ch.len() <= self.source.len() {
108 if self
109 .source
110 .get(self.position..self.position + ch.len())
111 .is_some_and(|v| v == ch)
112 {
113 return true;
114 }
115 self.advance(1);
116 }
117 self.go_to_end();
118 false
119 }
120
121 pub fn skip_whitespace(&mut self) -> bool {
122 let n = self.advance_while_char(|ch| vec![b' ', b'\t', b'\n', 12, b'\r'].contains(&ch));
123 n > 0
124 }
125
126 pub fn advance_while_char<F>(&mut self, condition: F) -> usize
127 where
128 F: Fn(u8) -> bool,
129 {
130 let pos_now = self.position;
131 while self.position < self.source.len()
132 && condition(self.source.bytes().nth(self.position).unwrap())
133 {
134 self.advance(1);
135 }
136 self.position - pos_now
137 }
138}