use nom::Err;
use nom::bytes::complete::tag;
use nom::character::complete::{alpha1, multispace0};
use nom::combinator::{map, opt};
use nom::error::ErrorKind;
use nom::sequence::{delimited, preceded};
use super::{Error, ErrorDetail, Input, Parser, Result};
pub fn with_failure_message<'a, P, V>(mut parser: P, message: &'a str) -> impl Parser<'a, V>
where
P: Parser<'a, V>,
{
move |input: Input<'a>| {
parser
.parse(input)
.map_err(|nom_err: Err<Error<'a>>| match nom_err {
Err::Error(e) => Err::Failure(e.with_detail(ErrorDetail::new(input, message))),
e => e,
})
}
}
pub fn check_parser_before_failure<'a, C, CV, P, PV>(
mut check_parser: C,
mut parser: P,
failure_msg: &'a str,
) -> impl Parser<'a, PV>
where
C: Parser<'a, CV>,
P: Parser<'a, PV>,
{
move |input: Input<'a>| {
check_parser.parse(input)?;
parser
.parse(input)
.map_err(|nom_err: Err<Error<'a>>| match nom_err {
Err::Error(e) => Err::Failure(e.with_detail(ErrorDetail::new(input, failure_msg))),
e => e,
})
}
}
pub fn spaced<'a, P, V>(parser: P) -> impl Parser<'a, V>
where
P: Parser<'a, V>,
{
delimited(multispace0, parser, multispace0)
}
pub fn stag(s: &str) -> impl Parser<'_, &str> {
spaced(tag(s))
}
pub fn is_present<'a, P, V>(parser: P) -> impl Parser<'a, bool>
where
P: Parser<'a, V>,
{
map(opt(parser), |v| v.is_some())
}
pub fn function<'a, PV, N, P>(word_parser: N, parser: P) -> impl Parser<'a, PV>
where
N: Parser<'a, &'a str>,
P: Parser<'a, PV>,
{
preceded(
word(word_parser),
delimited(
with_failure_message(stag("("), "Missing opening brace"),
parser,
with_failure_message(stag(")"), "Missing closing brace"),
),
)
}
pub fn word<'a, P>(mut word_parser: P) -> impl Parser<'a, &'a str>
where
P: Parser<'a, &'a str>,
{
move |input| {
let (input, word) = alpha1(input)?;
match word_parser.parse(word) {
Ok((_, parsed_word)) => {
if word == parsed_word {
Ok((input, word))
} else {
Err(Err::Error(Error::new(input, ErrorKind::Alpha, None)))
}
},
Err(e) => Err(e),
}
}
}
pub fn uppercase_word(input: Input<'_>) -> Result<'_, &str> {
let (input, word) = alpha1(input)?;
if word.chars().all(|c| c.is_ascii_uppercase()) {
Ok((input, word))
} else {
Err(Err::Error(Error::new(input, ErrorKind::Alpha, None)))
}
}
pub fn lowercase_word(input: Input<'_>) -> Result<'_, &str> {
let (input, word) = alpha1(input)?;
if word.chars().all(|c| c.is_ascii_lowercase()) {
Ok((input, word))
} else {
Err(Err::Error(Error::new(input, ErrorKind::Alpha, None)))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_uppercase_word() {
let input = "foo";
assert!(uppercase_word(input).is_err());
let input = "FOOfoo";
assert!(uppercase_word(input).is_err());
let input = "FOO";
assert!(uppercase_word(input).is_ok());
let input = "FOO;;";
assert!(uppercase_word(input).is_ok());
}
}