token_parser/
lib.rs

1#![deny(missing_docs)]
2
3/*!
4Some utilities for parsing some format based on nested lists into arbitrary data structures.
5It's also meant to be used as a backend for parsers.
6**/
7
8use std::path::PathBuf;
9
10use thiserror::Error;
11
12/// A trait required for all contexts being used for token parsing.
13///
14/// By default, only the empty tuple implements it.
15/// It currently does not contain anything by default. It's just there to achieve compatibility with features and to allow more changes without breaking anything.
16pub trait Context {
17    #[cfg(feature = "radix-parsing")]
18    #[inline]
19    /// Specifies the radix if the feature radix parsing is enabled.
20    fn radix(&self) -> u32 {
21        10
22    }
23}
24
25impl Context for () {}
26
27/// The error type for token parsing.
28#[derive(Debug, Error)]
29pub enum Error {
30    /// The sublist contains less elements than expected by a specified amount.
31    #[error("Not enough elements: {0} more expected")]
32    NotEnoughElements(usize),
33
34    /// The sublist contains more elements than expected by a specified amount.
35    #[error("Too many elements: {0} less expected")]
36    TooManyElements(usize),
37
38    /// No list is allowed in this context.
39    #[error("List not allowed")]
40    ListNotAllowed,
41
42    /// No symbol is allowed in this context.
43    #[error("Symbol not allowed")]
44    SymbolNotAllowed,
45
46    /// Error with string parsing.
47    #[error("String parsing error")]
48    StringParsing,
49
50    /// Some specific element is invalid.
51    #[error("Invalid element")]
52    InvalidElement,
53}
54
55/// The result type for token parsing.
56pub type Result<T> = std::result::Result<T, Error>;
57
58/// Some unit, which represents an intermediate state.
59pub enum Unit {
60    /// The current unit is a single symbol.
61    Symbol(Box<str>),
62    /// The current unit is a parser, which can yield multiple units.
63    Parser(Parser),
64}
65
66impl Unit {
67    /// Returns the symbol, if applicable, as a result type.
68    pub fn symbol(self) -> Result<Box<str>> {
69        if let Self::Symbol(name) = self {
70            Ok(name)
71        } else {
72            Err(Error::ListNotAllowed)
73        }
74    }
75
76    /// Returns the parser, if applicable, as a result type.
77    pub fn parser(self) -> Result<Parser> {
78        if let Self::Parser(parser) = self {
79            Ok(parser)
80        } else {
81            Err(Error::SymbolNotAllowed)
82        }
83    }
84}
85
86/// This trait needs to be implemented for every struct which can be parsed using the token parser.
87pub trait Parsable<C: Context>: Sized {
88    /// When a symbol is found by the parser, this will be called.
89    fn parse_symbol(_name: Box<str>, _context: &C) -> Result<Self> {
90        Err(Error::SymbolNotAllowed)
91    }
92
93    /// When a subparser is found by the parser, this will be called.
94    fn parse_list(_parser: &mut Parser, _context: &C) -> Result<Self> {
95        Err(Error::ListNotAllowed)
96    }
97}
98
99fn parse<C: Context, P: Parsable<C>>(unit: Unit, context: &C) -> Result<P> {
100    use Unit::*;
101    match unit {
102        Symbol(name) => Parsable::parse_symbol(name, context),
103        Parser(mut parser) => parser.parse_rest(context),
104    }
105}
106
107impl<C: Context, T: Parsable<C>> Parsable<C> for Box<T> {
108    fn parse_symbol(name: Box<str>, context: &C) -> Result<Self> {
109        Ok(Self::new(Parsable::parse_symbol(name, context)?))
110    }
111
112    fn parse_list(parser: &mut Parser, context: &C) -> Result<Self> {
113        Ok(Self::new(parser.parse_list(context)?))
114    }
115}
116
117impl<C: Context, T: Parsable<C>> Parsable<C> for Vec<T> {
118    fn parse_list(parser: &mut Parser, context: &C) -> Result<Self> {
119        let Parser { form, count } = parser;
120        form.drain(..)
121            .rev()
122            .map(|unit| {
123                *count += 1;
124                parse(unit, context)
125            })
126            .collect()
127    }
128}
129
130impl<C: Context> Parsable<C> for String {
131    fn parse_symbol(name: Box<str>, _context: &C) -> Result<Self> {
132        Ok(name.into())
133    }
134}
135
136impl<C: Context> Parsable<C> for Box<str> {
137    fn parse_symbol(name: Box<str>, _context: &C) -> Result<Self> {
138        Ok(name)
139    }
140}
141
142impl<C: Context> Parsable<C> for PathBuf {
143    fn parse_symbol(name: Box<str>, _context: &C) -> Result<Self> {
144        Ok(name.as_ref().into())
145    }
146}
147
148#[macro_export]
149/// Derives `Parsable` from symbol for types which implement `FromStr`.
150macro_rules! derive_symbol_parsable {
151    ($t:ty) => {
152        impl<C: $crate::Context> $crate::Parsable<C> for $t {
153            fn parse_symbol(name: Box<str>, _context: &C) -> $crate::Result<Self> {
154                if let Ok(value) = name.parse() {
155                    Ok(value)
156                } else {
157                    Err($crate::Error::StringParsing)
158                }
159            }
160        }
161    };
162    ($t:ty, $($rest:ty),+) => {
163        derive_symbol_parsable!($t);
164        derive_symbol_parsable!($($rest),+);
165    };
166}
167
168#[cfg(not(feature = "radix-parsing"))]
169mod numbers;
170derive_symbol_parsable!(bool);
171
172/// The token parser to parse the units into wanted types.
173pub struct Parser {
174    form: Vec<Unit>,
175    count: usize,
176}
177
178impl Parser {
179    /// Creates a new parser from a list of objects.
180    pub fn new<I: IntoIterator>(form: I) -> Self
181    where
182        I::Item: Into<Unit>,
183    {
184        let mut form: Vec<_> = form.into_iter().map(I::Item::into).collect();
185        form.reverse();
186        Self { form, count: 0 }
187    }
188
189    /// Tries to parse the next unit as the required type.
190    pub fn parse_next<C: Context, T: Parsable<C>>(&mut self, context: &C) -> Result<T> {
191        self.count += 1;
192        if let Some(token) = self.form.pop() {
193            parse(token, context)
194        } else {
195            Result::Err(Error::NotEnoughElements(self.count))
196        }
197    }
198
199    /// Tries to parse the rest of the current list into the required type.
200    /// If not every available token is used, this will be an error.
201    pub fn parse_rest<C: Context, T: Parsable<C>>(&mut self, context: &C) -> Result<T> {
202        let result = self.parse_list(context);
203        let count = self.form.len();
204        if count > 0 {
205            self.form.clear();
206            Err(Error::TooManyElements(count))
207        } else {
208            result
209        }
210    }
211
212    /// Tries to parse as many tokens of the current list as needed into the required type.
213    pub fn parse_list<C: Context, T: Parsable<C>>(&mut self, context: &C) -> Result<T> {
214        Parsable::parse_list(self, context)
215    }
216}
217
218impl Iterator for Parser {
219    type Item = Result<Self>;
220
221    fn next(&mut self) -> Option<Result<Self>> {
222        self.count += 1;
223        Some(self.form.pop()?.parser())
224    }
225}
226
227#[cfg(feature = "radix-parsing")]
228/// Contains utilities for radix parsing.
229pub mod radix;