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::{Recognizable, recognize};
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[0 + self.start.size()..self.end_slice - self.end.size()]
36    }
37}
38
39/// The result of a peeking operation.
40///
41/// A `PeekResult` contains the result of attempting to match a `Peekable`
42/// against the current position of a `Scanner`. If the match succeeds, a
43/// `Found` is returned with the length of the end slice, the start of the
44/// match, and the end of the match. If the match fails, a `NotFound` is
45/// returned.
46#[derive(PartialEq, Debug)]
47pub enum PeekResult<S, E> {
48    /// The match was successful.
49    Found { end_slice: usize, start: S, end: E },
50    /// The match was unsuccessful.
51    NotFound,
52}
53
54/// A type that can be peeked at the current position of a `Scanner`.
55///
56/// A `Peekable` is a type that can be used to peek at the current position of a
57/// `Scanner`. Implementors of `Peekable` must provide a `peek` method that
58/// attempts to match the `Peekable` against the current position of the
59/// `Scanner`.
60///
61/// # Associated Types
62///
63/// * `S` - The type of the start of the match.
64/// * `E` - The type of the end of the match.
65///
66/// # Required Methods
67///
68/// * `peek` - Attempts to match the `Peekable` against the current position of
69///   the `Scanner`.
70pub trait Peekable<'a, T, S, E> {
71    /// Attempt to match the `Peekable` against the current position of the
72    /// `Scanner`.
73    ///
74    /// This method will temporarily advance the position of the `Scanner` to
75    /// find a match. If a match is found, the `Scanner` is rewound to the
76    /// original position and a `PeekResult` is returned. If no match is found,
77    /// the `Scanner` is rewound to the original position and an `Err` is
78    /// returned.
79    ///
80    /// # Arguments
81    ///
82    /// * `data` - The `Scanner` to use when matching.
83    ///
84    /// # Returns
85    ///
86    /// A `PeekResult` if the `Peekable` matches the current position of the
87    /// `Scanner`, or an `Err` otherwise.
88    fn peek(&self, data: &Scanner<'a, T>) -> ParseResult<PeekResult<S, E>>;
89}
90
91/// Attempt to match a `Peekable` against the current position of a `Scanner`.
92///
93/// This function will temporarily advance the position of the `Scanner` to find
94/// a match. If a match is found, the `Scanner` is rewound to the original
95/// position and a `Peeking` is returned. If no match is found, the `Scanner` is
96/// rewound to the original position and an `Err` is returned.
97///
98/// # Arguments
99///
100/// * `peekable` - The `Peekable` to attempt to match.
101/// * `scanner` - The `Scanner` to use when matching.
102///
103/// # Returns
104///
105/// A `Peeking` if the `Peekable` matches the current position of the `Scanner`,
106/// or an `Err` otherwise.
107pub fn peek<'a, T, S, E, P: Peekable<'a, T, S, E>>(
108    peekable: P,
109    scanner: &mut Scanner<'a, T>,
110) -> ParseResult<Option<Peeking<'a, T, S, E>>> {
111    let source_cursor = scanner.current_position();
112    match peekable.peek(scanner)? {
113        PeekResult::Found {
114            end_slice,
115            start,
116            end,
117        } => {
118            let data = &scanner.data()[source_cursor..source_cursor + end_slice];
119            Ok(Some(Peeking {
120                start,
121                end,
122                end_slice,
123                data,
124            }))
125        }
126        PeekResult::NotFound => {
127            scanner.jump_to(source_cursor);
128            Ok(None)
129        }
130    }
131}
132
133/// A `Peekable` that peeks until the given `element` is found in the
134/// `Scanner`.
135///
136/// This `Peekable` will temporarily advance the position of the `Scanner` to
137/// find a match. If a match is found, the `Scanner` is rewound to the original
138/// position and a `PeekResult` is returned. If no match is found, the `Scanner`
139/// is rewound to the original position and an `Err` is returned.
140pub struct Until<'a, T, V> {
141    element: V,
142    _marker: PhantomData<&'a T>,
143}
144
145impl<'a, T, V> Peekable<'a, T, V, V> for Until<'a, T, V>
146where
147    V: Recognizable<'a, T, V> + Clone,
148{
149    /// Peek until the given `element` is found in the `Scanner`.
150    ///
151    /// This function will temporarily advance the position of the `Scanner` to
152    /// find a match. If a match is found, the `Scanner` is rewound to the
153    /// original position and a `PeekResult` is returned. If no match is found,
154    /// the `Scanner` is rewound to the original position and an `Err` is
155    /// returned.
156    ///
157    /// # Arguments
158    ///
159    /// * `data` - The `Scanner` to use when matching.
160    ///
161    /// # Returns
162    ///
163    /// A `PeekResult` if the `element` matches the current position of the
164    /// `Scanner`, or an `Err` otherwise.
165    fn peek(&self, data: &Scanner<'a, T>) -> ParseResult<PeekResult<V, V>> {
166        // create a temporary scanner to peek data
167        let mut scanner = Scanner::new(data.data());
168        while !scanner.is_empty() {
169            match recognize(self.element.clone(), &mut scanner) {
170                Ok(_element) => {
171                    return Ok(PeekResult::Found {
172                        end_slice: scanner.current_position() - self.element.size(),
173                        start: self.element.clone(),
174                        end: self.element.clone(),
175                    });
176                }
177                Err(_err) => {
178                    scanner.bump_by(1);
179                    continue;
180                }
181            }
182        }
183        Ok(PeekResult::NotFound)
184    }
185}
186
187/// A `Peekable` that peeks until the end of the `Scanner`.
188///
189/// This `Peekable` will temporarily advance the position of the `Scanner` to
190/// find a match. If a match is found, the `Scanner` is rewound to the original
191/// position and a `PeekResult` is returned. If no match is found, the `Scanner`
192/// is rewound to the original position and an `Err` is returned.
193#[derive(Default)]
194pub struct UntilEnd<T>(PhantomData<T>);
195
196impl<'a> Peekable<'a, u8, (), ()> for UntilEnd<u8> {
197    /// Peeks at the current position of the `Scanner` until it reaches the end
198    /// of the data.
199    ///
200    /// # Arguments
201    ///
202    /// * `data` - The `Scanner` to use when matching.
203    ///
204    /// # Returns
205    ///
206    /// A `PeekResult` where the `end_slice` is the current position of the
207    /// `Scanner`, and `start` and `end` are both `()`.
208    fn peek(&self, data: &Scanner<'a, u8>) -> ParseResult<PeekResult<(), ()>> {
209        Ok(PeekResult::Found {
210            end_slice: data.current_position(),
211            start: (),
212            end: (),
213        })
214    }
215}