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 remaining = &data.data()[data.current_position()..];
184 let mut scanner = Scanner::new(remaining);
185 while !scanner.is_empty() {
186 match self.element.clone().recognize_self(&mut scanner) {
187 Ok(Some(element)) => {
188 return Ok(PeekResult::Found {
189 end_slice: scanner.current_position() - self.element.size(),
190 start: element.clone(),
191 end: element.clone(),
192 });
193 }
194 Ok(None) => {
195 scanner.bump_by(1);
196 continue;
197 }
198 Err(_err) => {
199 scanner.bump_by(1);
200 continue;
201 }
202 }
203 }
204 Ok(PeekResult::NotFound)
205 }
206}
207
208/// A `Peekable` that peeks until the end of the `Scanner`.
209///
210/// This `Peekable` will temporarily advance the position of the `Scanner` to
211/// find a match. If a match is found, the `Scanner` is rewound to the original
212/// position and a `PeekResult` is returned. If no match is found, the `Scanner`
213/// is rewound to the original position and an `Err` is returned.
214#[derive(Default)]
215pub struct UntilEnd<T>(PhantomData<T>);
216
217#[cfg(test)]
218mod tests {
219 use crate::bytes::token::Token;
220 use crate::peek::{Until, UntilEnd, peek};
221
222 #[test]
223 fn test_until() {
224 let data = b"abc|fdgf";
225 let mut scanner = crate::scanner::Scanner::new(data);
226 let token = Until::new(Token::Pipe);
227 let peeked = peek(token, &mut scanner)
228 .expect("failed to parse")
229 .expect("failed to peek");
230 assert_eq!(peeked.data(), "abc".as_bytes());
231 }
232
233 #[test]
234 fn test_until_end() {
235 let data = b"abc|fdgf";
236 let mut scanner = crate::scanner::Scanner::new(data);
237 let token = UntilEnd::default();
238 let peeked = peek(token, &mut scanner)
239 .expect("failed to parse")
240 .expect("failed to peek");
241 assert_eq!(peeked.data, "abc|fdgf".as_bytes());
242 }
243}