alpm_parsers/
macros.rs

1//! Macros used with the winnow parser.
2
3/// Take an array of `dyn Iterator<Item = &'static str>` and return a closure that flattens the
4/// outer array to map the inner values  onto [winnow::error::StrContextValue::StringLiteral].
5///
6/// # Example
7///
8/// ```
9/// use alpm_parsers::iter_str_context;
10/// use winnow::{
11///     ModalResult,
12///     Parser,
13///     combinator::{alt, cut_err},
14/// };
15/// /// A parser with a single list of keywords
16/// fn parser_single<'a>(input: &mut &'a str) -> ModalResult<&'a str> {
17///     let first_list = ["first", "second", "third"];
18///     alt(first_list)
19///         .context_with(iter_str_context!([first_list]))
20///         .parse_next(input)
21/// }
22///
23/// const FIRST_LIST: [&str; 3] = ["first", "second", "third"];
24/// const SECOND_LIST: [&str; 2] = ["fourth", "fifth"];
25///
26/// fn first_parser<'a>(input: &mut &'a str) -> ModalResult<&'a str> {
27///     alt(FIRST_LIST).parse_next(input)
28/// }
29///
30/// fn second_parser<'a>(input: &mut &'a str) -> ModalResult<&'a str> {
31///     alt(SECOND_LIST).parse_next(input)
32/// }
33///
34/// /// Can be used like this on static arrays of varying length.
35/// fn parser_multi<'a>(input: &mut &'a str) -> ModalResult<&'a str> {
36///     cut_err(alt((first_parser, second_parser)))
37///         .context_with(iter_str_context!([
38///             FIRST_LIST.to_vec(),
39///             SECOND_LIST.to_vec()
40///         ]))
41///         .parse_next(input)
42/// }
43///
44/// /// And as follows on local arrays of varying length.
45/// fn parser_multi_alt<'a>(input: &mut &'a str) -> ModalResult<&'a str> {
46///     let first_list = ["first", "second", "third"];
47///     let second_list = ["fourth", "fifth"];
48///     alt((first_parser, second_parser))
49///         .context_with(iter_str_context!([
50///             first_list.to_vec(),
51///             second_list.to_vec()
52///         ]))
53///         .parse_next(input)
54/// }
55///
56/// assert!(parser_single.parse("second").is_ok());
57/// assert!(parser_multi.parse("fourth").is_ok());
58/// assert!(parser_multi_alt.parse("fourth").is_ok());
59/// ```
60#[macro_export]
61macro_rules! iter_str_context {
62    ($iter:expr) => {
63        || {
64            use winnow::error::{StrContext, StrContextValue};
65            $iter
66                .into_iter()
67                .flatten()
68                .map(|s| StrContext::Expected(StrContextValue::StringLiteral(s)))
69        }
70    };
71}
72
73/// Take a `dyn Iterator<Item = &char>` and return a closure that calls `.iter()` and maps
74/// the values onto [winnow::error::StrContextValue::CharLiteral].
75///
76/// # Example
77///
78/// ```
79/// use alpm_parsers::iter_char_context;
80/// use winnow::{ModalResult, Parser, combinator::cut_err, token::one_of};
81///
82/// fn parser(input: &mut &str) -> ModalResult<char> {
83///     let accepted_characters = ['a', 'b', 'c'];
84///     cut_err(one_of(accepted_characters))
85///         .context_with(iter_char_context!(accepted_characters))
86///         .parse_next(input)
87/// }
88///
89/// assert!(parser.parse("a").is_ok());
90/// ```
91#[macro_export]
92macro_rules! iter_char_context {
93    ($iter:expr) => {
94        || {
95            use winnow::error::{StrContext, StrContextValue};
96            $iter
97                .iter()
98                .map(|c| StrContext::Expected(StrContextValue::CharLiteral(*c)))
99        }
100    };
101}