color_print_proc_macro/parse/
util.rs

1use nom::{
2    Err,
3    sequence::{delimited, preceded},
4    character::complete::{multispace0, alpha1},
5    bytes::complete::tag,
6    combinator::{map, opt},
7    error::ErrorKind,
8};
9
10use super::{Parser, Result, Input, Error, ErrorDetail};
11
12/// Transforms an error into a failure, while adding a message in the error detail.
13pub fn with_failure_message<'a, P, V>(mut parser: P, message: &'a str) -> impl Parser<'a, V>
14where
15    P: Parser<'a, V>,
16{
17    move |input: Input<'a>| parser(input).map_err(
18        |nom_err: Err<Error>| match nom_err {
19            Err::Error(e) => {
20                Err::Failure(e.with_detail(ErrorDetail::new(input, message)))
21            }
22            e => e,
23        }
24    )
25}
26
27/// Checks if the first parser succeeds, then parses the input with the second parser. If an error
28/// is encountered with the second parser, then a failure message is thrown.
29pub fn check_parser_before_failure<'a, C, CV, P, PV>(
30    mut check_parser: C,
31    mut parser: P,
32    failure_msg: &'a str
33) -> impl Parser<'a, PV>
34where
35    C: Parser<'a, CV>,
36    P: Parser<'a, PV>,
37{
38    move |input| {
39        check_parser(input)?;
40        with_failure_message(|input| { parser(input) }, failure_msg)
41        (input)
42    }
43}
44
45/// Creates a parser which accpets spaces around the original parsed input.
46pub fn spaced<'a, P, V>(parser: P) -> impl Parser<'a, V>
47where
48    P: Parser<'a, V>,
49{
50    delimited(
51        multispace0,
52        parser,
53        multispace0,
54    )
55}
56
57/// Parsed a spaced tag.
58pub fn stag(s: &str) -> impl Parser<'_, &str> {
59    spaced(tag(s))
60}
61
62/// Creates a parser which makes the parser optional and returns true if the parse was successful.
63pub fn is_present<'a, P, V>(parser: P) -> impl Parser<'a, bool>
64where
65    P: Parser<'a, V>,
66{
67    map(opt(parser), |v| v.is_some())
68}
69
70/// Creates a parser which parses a function call.
71pub fn function<'a, PV, N, P>(word_parser: N, parser: P) -> impl Parser<'a, PV>
72where
73    N: Parser<'a, &'a str>,
74    P: Parser<'a, PV>,
75{
76    preceded(
77        word(word_parser),
78        delimited(
79            with_failure_message(stag("("), "Missing opening brace"),
80            parser,
81            with_failure_message(stag(")"), "Missing closing brace")
82        )
83    )
84}
85
86/// Parses a word made only by alpha characters ('a' => 'z' and 'A' => 'Z'), and checks if this
87/// word matches exactly the given parser.
88pub fn word<'a, P>(mut word_parser: P) -> impl Parser<'a, &'a str>
89where
90    P: Parser<'a, &'a str>,
91{
92    move |input| {
93        let (input, word) = alpha1(input)?;
94        match word_parser(word) {
95            Ok((_, parsed_word)) => {
96                if word == parsed_word {
97                    Ok((input, word))
98                } else {
99                    Err(Err::Error(Error::new(input, ErrorKind::Alpha, None)))
100                }
101            }
102            Err(e) => Err(e),
103        }
104    }
105}
106
107/// Parses an uppercase word.
108pub fn uppercase_word(input: Input<'_>) -> Result<'_, &str> {
109    let (input, word) = alpha1(input)?;
110    if word.chars().all(|c| c.is_ascii_uppercase()) {
111        Ok((input, word))
112    } else {
113        Err(Err::Error(Error::new(input, ErrorKind::Alpha, None)))
114    }
115}
116
117/// Parses a lowercase word.
118pub fn lowercase_word(input: Input<'_>) -> Result<'_, &str> {
119    let (input, word) = alpha1(input)?;
120    if word.chars().all(|c| c.is_ascii_lowercase()) {
121        Ok((input, word))
122    } else {
123        Err(Err::Error(Error::new(input, ErrorKind::Alpha, None)))
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130
131    #[test]
132    fn test_uppercase_word() {
133        let input = "foo";
134        assert!(uppercase_word(input).is_err());
135        let input = "FOOfoo";
136        assert!(uppercase_word(input).is_err());
137        let input = "FOO";
138        assert!(uppercase_word(input).is_ok());
139        let input = "FOO;;";
140        assert!(uppercase_word(input).is_ok());
141    }
142}