noa_parser/
separated_list.rs

1use crate::errors::{ParseError, ParseResult};
2use crate::scanner::Scanner;
3use crate::visitor::Visitor;
4use std::marker::PhantomData;
5
6pub struct SeparatedList<T, V, S> {
7    pub(crate) data: Vec<V>,
8    separator: PhantomData<(S, T)>,
9}
10
11enum YieldResult<V> {
12    Last(V),
13    MaybeNext(V),
14}
15
16impl<T, V, S> SeparatedList<T, V, S> {
17    /// Consume the `SeparatedList` and return an iterator over the elements.
18    ///
19    /// # Returns
20    ///
21    /// An iterator over the elements of the `SeparatedList`.
22    pub fn into_iter(self) -> impl Iterator<Item = V> {
23        self.data.into_iter()
24    }
25}
26
27/// Yield the next element in the list and tell if it's the last one.
28///
29/// # Type Parameters
30///
31/// * `T` - The type of the data to scan.
32/// * `V` - The type of the element to yield.
33/// * `S` - The type of the separator to consume.
34///
35/// # Arguments
36///
37/// * `scanner` - The scanner to use.
38///
39/// # Returns
40///
41/// A `YieldResult` containing the element and whether it's the last one.
42///
43/// # Errors
44///
45/// Any error the visitor for the element or the separator returns.
46fn yield_element<'a, T, V, S>(scanner: &mut Scanner<'a, T>) -> ParseResult<YieldResult<V>>
47where
48    V: Visitor<'a, T>,
49    S: Visitor<'a, T>,
50{
51    let cursor = scanner.current_position();
52    let element = match scanner.visit::<V>() {
53        Ok(element) => element,
54        Err(err) => {
55            scanner.jump_to(cursor);
56            return Err(err);
57        }
58    };
59
60    if scanner.remaining().is_empty() {
61        return Ok(YieldResult::Last(element));
62    }
63
64    // consume the separator if not the end of the slice
65    scanner.visit::<S>()?;
66
67    Ok(YieldResult::MaybeNext(element))
68}
69
70impl<'a, T, V, S> Visitor<'a, T> for SeparatedList<T, V, S>
71where
72    V: Visitor<'a, T>,
73    S: Visitor<'a, T>,
74{
75    /// Accept a list of elements separated by a separator.
76    ///
77    /// # Arguments
78    ///
79    /// * `scanner` - The scanner to use.
80    ///
81    /// # Returns
82    ///
83    /// A `ParseResult` containing the accepted `SeparatedList` on success, or
84    /// an error on failure.
85    ///
86    /// # Errors
87    ///
88    /// Any error the visitor for the element or the separator returns, or
89    /// `ParseError::UnexpectedToken` if the scanner is empty when attempting
90    /// to parse the separator.
91    fn accept(scanner: &mut Scanner<'a, T>) -> ParseResult<Self> {
92        let mut elements = vec![];
93        let cursor = scanner.current_position();
94
95        loop {
96            if let Ok(result) = yield_element::<T, V, S>(scanner) {
97                let element: YieldResult<V> = result;
98
99                match element {
100                    YieldResult::Last(element) => {
101                        elements.push(element);
102                        break;
103                    }
104                    YieldResult::MaybeNext(element) => {
105                        elements.push(element);
106                    }
107                }
108            } else {
109                scanner.jump_to(cursor);
110                return Err(ParseError::UnexpectedToken);
111            }
112        }
113
114        Ok(SeparatedList {
115            data: elements,
116            separator: PhantomData,
117        })
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use crate::bytes::primitives::number::Number;
124    use crate::bytes::token::Token;
125    use crate::errors::ParseResult;
126    use crate::recognizer::recognize;
127    use crate::scanner::Scanner;
128    use crate::separated_list::SeparatedList;
129    use crate::visitor::Visitor;
130
131    struct SeparatorComma;
132
133    impl<'a> Visitor<'a, u8> for SeparatorComma {
134        fn accept(scanner: &mut Scanner<'a, u8>) -> ParseResult<Self> {
135            recognize(Token::Comma, scanner)?;
136            Ok(SeparatorComma)
137        }
138    }
139
140    /// Tests parsing a list of `Number`s separated by commas.
141    ///
142    /// Input: `b"12,4,78,22"`
143    /// Output: `vec![Number(12), Number(4), Number(78), Number(22)]`
144    /// Final position: 10
145    #[test]
146    fn test_parse_number_list() {
147        let data = b"12,4,78,22";
148        let mut scanner = Scanner::new(data);
149        let result = scanner
150            .visit::<SeparatedList<u8, Number<usize>, SeparatorComma>>()
151            .expect("failed to parse");
152        assert_eq!(
153            result.data,
154            vec![Number(12), Number(4), Number(78), Number(22)]
155        );
156        assert_eq!(scanner.current_position(), 10);
157    }
158}