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::{recognize, Recognizable};
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}