noa_parser/peek.rs
1//! Peekable types
2//!
3//! A `Peekable` is a type that can be used to peek at the current position of a
4//! `Scanner` without advancing the scanner.
5
6use crate::errors::ParseResult;
7use crate::matcher::MatchSize;
8use crate::recognizer::RecognizeSelf;
9use crate::scanner::Scanner;
10use std::marker::PhantomData;
11
12/// A successful peeking result.
13///
14/// A `Peeking` contains the start and end results of a successful peek, the
15/// length of the end slice, and a reference to the data that was peeked.
16#[derive(Debug, PartialEq)]
17pub struct Peeking<'a, T, S, E> {
18 /// The start of the match.
19 pub start: S,
20 /// The end of the match.
21 pub end: E,
22 /// The length of peeked slice.
23 pub end_slice: usize,
24 /// The data that was peeked.
25 pub data: &'a [T],
26}
27
28impl<'a, T, S, E> Peeking<'a, T, S, E>
29where
30 S: MatchSize,
31 E: MatchSize,
32{
33 /// Get a slice of the data that was peeked.
34 pub fn peeked_slice(&self) -> &'a [T] {
35 &self.data[self.start.size()..self.end_slice - self.end.size()]
36 }
37
38 /// Get the data that was peeked.
39 ///
40 /// Returns a reference to the underlying data that was peeked.
41 pub fn data(&self) -> &'a [T] {
42 self.data
43 }
44}
45
46/// The result of a peeking operation.
47///
48/// A `PeekResult` contains the result of attempting to match a `Peekable`
49/// against the current position of a `Scanner`. If the match succeeds, a
50/// `Found` is returned with the length of the end slice, the start of the
51/// match, and the end of the match. If the match fails, a `NotFound` is
52/// returned.
53#[derive(PartialEq, Debug)]
54pub enum PeekResult<S, E> {
55 /// The match was successful.
56 Found { end_slice: usize, start: S, end: E },
57 /// The match was unsuccessful.
58 NotFound,
59}
60
61/// A type that can be peeked at the current position of a `Scanner`.
62///
63/// A `Peekable` is a type that can be used to peek at the current position of a
64/// `Scanner`. Implementors of `Peekable` must provide a `peek` method that
65/// attempts to match the `Peekable` against the current position of the
66/// `Scanner`.
67///
68/// # Associated Types
69///
70/// * `S` - The type of the start of the match.
71/// * `E` - The type of the end of the match.
72///
73/// # Required Methods
74///
75/// * `peek` - Attempts to match the `Peekable` against the current position of
76/// the `Scanner`.
77pub trait Peekable<'a, T, S, E> {
78 /// Attempt to match the `Peekable` against the current position of the
79 /// `Scanner`.
80 ///
81 /// This method will temporarily advance the position of the `Scanner` to
82 /// find a match. If a match is found, the `Scanner` is rewound to the
83 /// original position and a `PeekResult` is returned. If no match is found,
84 /// the `Scanner` is rewound to the original position and an `Err` is
85 /// returned.
86 ///
87 /// # Arguments
88 ///
89 /// * `data` - The `Scanner` to use when matching.
90 ///
91 /// # Returns
92 ///
93 /// A `PeekResult` if the `Peekable` matches the current position of the
94 /// `Scanner`, or an `Err` otherwise.
95 fn peek(&self, data: &Scanner<'a, T>) -> ParseResult<PeekResult<S, E>>;
96}
97
98/// Attempt to match a `Peekable` against the current position of a `Scanner`.
99///
100/// This function will temporarily advance the position of the `Scanner` to find
101/// a match. If a match is found, the `Scanner` is rewound to the original
102/// position and a `Peeking` is returned. If no match is found, the `Scanner` is
103/// rewound to the original position and an `Err` is returned.
104///
105/// # Arguments
106///
107/// * `peekable` - The `Peekable` to attempt to match.
108/// * `scanner` - The `Scanner` to use when matching.
109///
110/// # Returns
111///
112/// A `Peeking` if the `Peekable` matches the current position of the `Scanner`,
113/// or an `Err` otherwise.
114pub fn peek<'a, T, S, E, P: Peekable<'a, T, S, E>>(
115 peekable: P,
116 scanner: &mut Scanner<'a, T>,
117) -> ParseResult<Option<Peeking<'a, T, S, E>>> {
118 let source_cursor = scanner.current_position();
119 match peekable.peek(scanner)? {
120 PeekResult::Found {
121 end_slice,
122 start,
123 end,
124 } => {
125 let data = &scanner.data()[source_cursor..source_cursor + end_slice];
126 Ok(Some(Peeking {
127 start,
128 end,
129 end_slice,
130 data,
131 }))
132 }
133 PeekResult::NotFound => {
134 scanner.jump_to(source_cursor);
135 Ok(None)
136 }
137 }
138}
139
140/// A `Peekable` that peeks until the given `element` is found in the
141/// `Scanner`.
142///
143/// This `Peekable` will temporarily advance the position of the `Scanner` to
144/// find a match. If a match is found, the `Scanner` is rewound to the original
145/// position and a `PeekResult` is returned. If no match is found, the `Scanner`
146/// is rewound to the original position and an `Err` is returned.
147pub struct Until<'a, T, V> {
148 element: V,
149 _marker: PhantomData<&'a T>,
150}
151
152impl<'a, T, V> Until<'a, T, V> {
153 pub fn new(element: V) -> Until<'a, T, V> {
154 Until {
155 element,
156 _marker: PhantomData,
157 }
158 }
159}
160
161impl<'a, T, V> Peekable<'a, T, V, V> for Until<'a, T, V>
162where
163 V: RecognizeSelf<'a, T, V> + Clone,
164{
165 /// Peek until the given `element` is found in the `Scanner`.
166 ///
167 /// This function will temporarily advance the position of the `Scanner` to
168 /// find a match. If a match is found, the `Scanner` is rewound to the
169 /// original position and a `PeekResult` is returned. If no match is found,
170 /// the `Scanner` is rewound to the original position and an `Err` is
171 /// returned.
172 ///
173 /// # Arguments
174 ///
175 /// * `data` - The `Scanner` to use when matching.
176 ///
177 /// # Returns
178 ///
179 /// A `PeekResult` if the `element` matches the current position of the
180 /// `Scanner`, or an `Err` otherwise.
181 fn peek(&self, data: &Scanner<'a, T>) -> ParseResult<PeekResult<V, V>> {
182 // create a temporary scanner to peek data
183 let mut scanner = Scanner::new(data.data());
184 while !scanner.is_empty() {
185 match self.element.clone().recognize_self(&mut scanner) {
186 Ok(Some(element)) => {
187 return Ok(PeekResult::Found {
188 end_slice: scanner.current_position() - self.element.size(),
189 start: element.clone(),
190 end: element.clone(),
191 });
192 }
193 Ok(None) => {
194 scanner.bump_by(1);
195 continue;
196 }
197 Err(_err) => {
198 scanner.bump_by(1);
199 continue;
200 }
201 }
202 }
203 Ok(PeekResult::NotFound)
204 }
205}
206
207/// A `Peekable` that peeks until the end of the `Scanner`.
208///
209/// This `Peekable` will temporarily advance the position of the `Scanner` to
210/// find a match. If a match is found, the `Scanner` is rewound to the original
211/// position and a `PeekResult` is returned. If no match is found, the `Scanner`
212/// is rewound to the original position and an `Err` is returned.
213#[derive(Default)]
214pub struct UntilEnd<T>(PhantomData<T>);
215
216impl<'a> Peekable<'a, u8, (), ()> for UntilEnd<u8> {
217 /// Peeks at the current position of the `Scanner` until it reaches the end
218 /// of the data.
219 ///
220 /// # Arguments
221 ///
222 /// * `data` - The `Scanner` to use when matching.
223 ///
224 /// # Returns
225 ///
226 /// A `PeekResult` where the `end_slice` is the current position of the
227 /// `Scanner`, and `start` and `end` are both `()`.
228 fn peek(&self, data: &Scanner<'a, u8>) -> ParseResult<PeekResult<(), ()>> {
229 Ok(PeekResult::Found {
230 end_slice: data.remaining().len(),
231 start: (),
232 end: (),
233 })
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use crate::bytes::token::Token;
240 use crate::peek::{peek, Until, UntilEnd};
241
242 #[test]
243 fn test_until() {
244 let data = b"abc|fdgf";
245 let mut scanner = crate::scanner::Scanner::new(data);
246 let token = Until::new(Token::Pipe);
247 let peeked = peek(token, &mut scanner)
248 .expect("failed to parse")
249 .expect("failed to peek");
250 assert_eq!(peeked.data(), "abc".as_bytes());
251 }
252
253 #[test]
254 fn test_until_end() {
255 let data = b"abc|fdgf";
256 let mut scanner = crate::scanner::Scanner::new(data);
257 let token = UntilEnd::default();
258 let peeked = peek(token, &mut scanner)
259 .expect("failed to parse")
260 .expect("failed to peek");
261 assert_eq!(peeked.data, "abc|fdgf".as_bytes());
262 }
263}