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