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::{ParseError, ParseResult};
7use crate::recognizer::{Recognizable, recognize};
8use crate::scanner::Scanner;
9use std::marker::PhantomData;
10
11/// A successful peeking result.
12///
13/// A `Peeking` contains the start and end results of a successful peek, the
14/// length of the end slice, and a reference to the data that was peeked.
15#[derive(Debug)]
16pub struct Peeking<'a, T, S, E> {
17    /// The start of the match.
18    pub start: S,
19    /// The end of the match.
20    pub end: E,
21    /// The length of peeked slice.
22    pub end_slice: usize,
23    /// The data that was peeked.
24    pub data: &'a [T],
25}
26
27/// The result of a peeking operation.
28///
29/// A `PeekResult` contains the result of attempting to match a `Peekable`
30/// against the current position of a `Scanner`. If the match succeeds, a
31/// `Found` is returned with the length of the end slice, the start of the
32/// match, and the end of the match. If the match fails, a `NotFound` is
33/// returned.
34pub enum PeekResult<S, E> {
35    /// The match was successful.
36    Found { end_slice: usize, start: S, end: E },
37    /// The match was unsuccessful.
38    NotFound,
39}
40
41/// A type that can be peeked at the current position of a `Scanner`.
42///
43/// A `Peekable` is a type that can be used to peek at the current position of a
44/// `Scanner`. Implementors of `Peekable` must provide a `peek` method that
45/// attempts to match the `Peekable` against the current position of the
46/// `Scanner`.
47///
48/// # Associated Types
49///
50/// * `S` - The type of the start of the match.
51/// * `E` - The type of the end of the match.
52///
53/// # Required Methods
54///
55/// * `peek` - Attempts to match the `Peekable` against the current position of
56///   the `Scanner`.
57pub trait Peekable<'a, T, S, E> {
58    /// Attempt to match the `Peekable` against the current position of the
59    /// `Scanner`.
60    ///
61    /// This method will temporarily advance the position of the `Scanner` to
62    /// find a match. If a match is found, the `Scanner` is rewound to the
63    /// original position and a `PeekResult` is returned. If no match is found,
64    /// the `Scanner` is rewound to the original position and an `Err` is
65    /// returned.
66    ///
67    /// # Arguments
68    ///
69    /// * `data` - The `Scanner` to use when matching.
70    ///
71    /// # Returns
72    ///
73    /// A `PeekResult` if the `Peekable` matches the current position of the
74    /// `Scanner`, or an `Err` otherwise.
75    fn peek(&self, data: &mut Scanner<'a, T>) -> ParseResult<PeekResult<S, E>>;
76}
77
78/// Attempt to match a `Peekable` against the current position of a `Scanner`.
79///
80/// This function will temporarily advance the position of the `Scanner` to find
81/// a match. If a match is found, the `Scanner` is rewound to the original
82/// position and a `Peeking` is returned. If no match is found, the `Scanner` is
83/// rewound to the original position and an `Err` is returned.
84///
85/// # Arguments
86///
87/// * `peekable` - The `Peekable` to attempt to match.
88/// * `scanner` - The `Scanner` to use when matching.
89///
90/// # Returns
91///
92/// A `Peeking` if the `Peekable` matches the current position of the `Scanner`,
93/// or an `Err` otherwise.
94pub fn peek<'a, T, S, E, P: Peekable<'a, T, S, E>>(
95    peekable: P,
96    scanner: &'a mut Scanner<'a, T>,
97) -> ParseResult<Peeking<'a, T, S, E>> {
98    let source_cursor = scanner.current_position();
99    match peekable.peek(scanner)? {
100        PeekResult::Found {
101            end_slice,
102            start,
103            end,
104        } => {
105            let data = &scanner.data()[source_cursor..source_cursor + end_slice];
106            Ok(Peeking {
107                start,
108                end,
109                end_slice,
110                data,
111            })
112        }
113        PeekResult::NotFound => {
114            scanner.jump_to(source_cursor);
115            Err(ParseError::UnexpectedToken)
116        }
117    }
118}
119
120/// A `Peekable` that peeks until the given `element` is found in the
121/// `Scanner`.
122///
123/// This `Peekable` will temporarily advance the position of the `Scanner` to
124/// find a match. If a match is found, the `Scanner` is rewound to the original
125/// position and a `PeekResult` is returned. If no match is found, the `Scanner`
126/// is rewound to the original position and an `Err` is returned.
127pub struct Until<'a, T, V> {
128    element: V,
129    _marker: PhantomData<&'a T>,
130}
131
132impl<'a, T, V> Peekable<'a, T, V, V> for Until<'a, T, V>
133where
134    V: Recognizable<'a, T, V> + Clone,
135{
136    /// Peek until the given `element` is found in the `Scanner`.
137    ///
138    /// This function will temporarily advance the position of the `Scanner` to
139    /// find a match. If a match is found, the `Scanner` is rewound to the
140    /// original position and a `PeekResult` is returned. If no match is found,
141    /// the `Scanner` is rewound to the original position and an `Err` is
142    /// returned.
143    ///
144    /// # Arguments
145    ///
146    /// * `data` - The `Scanner` to use when matching.
147    ///
148    /// # Returns
149    ///
150    /// A `PeekResult` if the `element` matches the current position of the
151    /// `Scanner`, or an `Err` otherwise.
152    fn peek(&self, data: &mut Scanner<'a, T>) -> ParseResult<PeekResult<V, V>> {
153        // create a temporary scanner to peek data
154        let mut scanner = Scanner::new(data.data());
155        while !scanner.is_empty() {
156            match recognize(self.element.clone(), &mut scanner) {
157                Ok(_element) => {
158                    return Ok(PeekResult::Found {
159                        end_slice: scanner.current_position() - self.element.size(),
160                        start: self.element.clone(),
161                        end: self.element.clone(),
162                    });
163                }
164                Err(_err) => {
165                    scanner.bump_by(1);
166                    continue;
167                }
168            }
169        }
170        Ok(PeekResult::NotFound)
171    }
172}