1#![deny(missing_docs)]
2
3use std::path::PathBuf;
9
10use thiserror::Error;
11
12pub trait Context {
17 #[cfg(feature = "radix-parsing")]
18 #[inline]
19 fn radix(&self) -> u32 {
21 10
22 }
23}
24
25impl Context for () {}
26
27#[derive(Debug, Error)]
29pub enum Error {
30 #[error("Not enough elements: {0} more expected")]
32 NotEnoughElements(usize),
33
34 #[error("Too many elements: {0} less expected")]
36 TooManyElements(usize),
37
38 #[error("List not allowed")]
40 ListNotAllowed,
41
42 #[error("Symbol not allowed")]
44 SymbolNotAllowed,
45
46 #[error("String parsing error")]
48 StringParsing,
49
50 #[error("Invalid element")]
52 InvalidElement,
53}
54
55pub type Result<T> = std::result::Result<T, Error>;
57
58pub enum Unit {
60 Symbol(Box<str>),
62 Parser(Parser),
64}
65
66impl Unit {
67 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 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
86pub trait Parsable<C: Context>: Sized {
88 fn parse_symbol(_name: Box<str>, _context: &C) -> Result<Self> {
90 Err(Error::SymbolNotAllowed)
91 }
92
93 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]
149macro_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
172pub struct Parser {
174 form: Vec<Unit>,
175 count: usize,
176}
177
178impl Parser {
179 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 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 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 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")]
228pub mod radix;