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}