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}