utils/parser/
one_of.rs

1use crate::parser::then::Then2;
2use crate::parser::{ParseState, Parser, ParserResult};
3
4/// Use a second trait to force usage of the [`one_of`] method, preventing tuples from being used as
5/// parsers directly, which could be confusing.
6#[doc(hidden)]
7pub trait ParserOneOfTuple<'i> {
8    type Output;
9    fn one_of(
10        &self,
11        input: &'i [u8],
12        state: &mut ParseState<'i>,
13        tail: bool,
14    ) -> ParserResult<'i, Self::Output>;
15}
16
17macro_rules! one_of_impl {
18    ($($l:ident: $n:tt),+) => {
19        impl<'i, A: Parser<'i>, $($l: Parser<'i, Output = A::Output>),+> ParserOneOfTuple<'i> for (A, $($l,)*) {
20            type Output = A::Output;
21
22            #[inline(always)]
23            fn one_of(
24                &self,
25                input: &'i [u8],
26                state: &mut ParseState<'i>,
27                tail: bool,
28            ) -> ParserResult<'i, Self::Output> {
29                let mut commit = false;
30                let token = match self.0.parse_ctx(input, state, &mut commit, tail) {
31                    Ok(v) => return Ok(v),
32                    Err(t) if commit => return Err(t),
33                    Err(t) => t,
34                };
35
36                $(
37                let mut commit = false;
38                match self.$n.parse_ctx(input, state, &mut commit, tail) {
39                    Ok(v) => return Ok(v),
40                    Err(t) if commit => return Err(t),
41                    Err(_) => {},
42                }
43                )+
44
45                Err(token)
46            }
47        }
48    };
49}
50
51one_of_impl! {B: 1}
52one_of_impl! {B: 1, C: 2}
53one_of_impl! {B: 1, C: 2, D: 3}
54one_of_impl! {B: 1, C: 2, D: 3, E: 4}
55one_of_impl! {B: 1, C: 2, D: 3, E: 4, F: 5}
56one_of_impl! {B: 1, C: 2, D: 3, E: 4, F: 5, G: 6}
57one_of_impl! {B: 1, C: 2, D: 3, E: 4, F: 5, G: 6, H: 7}
58one_of_impl! {B: 1, C: 2, D: 3, E: 4, F: 5, G: 6, H: 7, I: 8}
59one_of_impl! {B: 1, C: 2, D: 3, E: 4, F: 5, G: 6, H: 7, I: 8, J: 9}
60one_of_impl! {B: 1, C: 2, D: 3, E: 4, F: 5, G: 6, H: 7, I: 8, J: 9, K: 10}
61one_of_impl! {B: 1, C: 2, D: 3, E: 4, F: 5, G: 6, H: 7, I: 8, J: 9, K: 10, L: 11}
62
63#[derive(Copy, Clone)]
64pub struct OneOf<O> {
65    options: O,
66}
67impl<'i, O: ParserOneOfTuple<'i>> Parser<'i> for OneOf<O> {
68    type Output = O::Output;
69    type Then<T: Parser<'i>> = Then2<Self, T>;
70
71    #[inline]
72    fn parse_ctx(
73        &self,
74        input: &'i [u8],
75        state: &mut ParseState<'i>,
76        _: &mut bool,
77        tail: bool,
78    ) -> ParserResult<'i, Self::Output> {
79        self.options.one_of(input, state, tail)
80    }
81}
82
83/// [`Parser`] which tries a list of parsers in order until one succeeds.
84///
85/// If a parser commits, no further parsers are tried.
86///
87/// This is similar to [`Parser::or`] but supports a variable number of parsers.
88///
89/// Prefer [`parser::literal_map`](super::literal_map) if all the parsers are string literals.
90///
91/// # Examples
92/// ```
93/// # use utils::input::InputError;
94/// # use utils::parser::{self, ParseError, Parser};
95/// #[derive(Debug, PartialEq)]
96/// enum Value {
97///     Unsigned8(u8),
98///     Unsigned32(u32),
99///     Signed8(i8),
100///     Signed32(i32),
101/// }
102///
103/// let parser = parser::one_of((
104///     parser::u8().map(Value::Unsigned8),
105///     parser::u32().map(Value::Unsigned32),
106///     parser::i8().map(Value::Signed8),
107///     parser::i32().map(Value::Signed32),
108/// ));
109///
110/// assert_eq!(
111///     parser.parse_complete("31").unwrap(),
112///     Value::Unsigned8(31),
113/// );
114/// assert_eq!(
115///     parser.parse_complete("4294967295").unwrap(),
116///     Value::Unsigned32(4294967295),
117/// );
118/// assert_eq!(
119///     parser.parse_complete("-1").unwrap(),
120///     Value::Signed8(-1)
121/// );
122/// assert_eq!(
123///     parser.parse_complete("-2147483648").unwrap(),
124///     Value::Signed32(-2147483648)
125/// );
126///
127/// assert_eq!(
128///     parser.parse_complete("not a number").unwrap_err().into_source(),
129///     ParseError::Expected("unsigned integer")
130/// );
131/// assert_eq!(
132///     parser.parse_complete("-4294967295").unwrap_err().into_source(),
133///     ParseError::NumberTooSmall(-2147483648)
134/// );
135/// ```
136#[inline]
137#[must_use]
138pub fn one_of<'i, L: ParserOneOfTuple<'i>>(options: L) -> OneOf<L> {
139    OneOf { options }
140}