use crate::{Error, InputToken, Mismatch, Parser};
pub fn satisfy<F, IT, O>(f: F) -> impl Parser<IT, O>
where
F: Fn(&IT::Token) -> Option<O>,
IT: InputToken,
{
move |input| {
match input.peek() {
Some(input_token) => {
let token = input_token.token();
match f(token) {
Some(result) => {
input.next_token(); Ok(result)
}
None => {
let position = input_token.position();
let mismatch = Mismatch::without_expectation(token.to_string());
Err(Error::UnexpectedToken(
input.source_name(),
position,
Some(mismatch),
))
}
}
}
None => Err(Error::EndOfInput(None)),
}
}
}
#[cfg(test)]
mod tests {
use crate::input::Position;
use crate::*;
#[test]
fn digits() {
let parser = satisfy(|token: &char| {
if token.is_ascii_digit() {
Some(*token)
} else {
None
}
});
let mut input = Input::new_from_chars("1".chars(), None);
assert_eq!(parser(&mut input), Ok('1'));
assert!(end_of_input()(&mut input).is_ok());
let mut input = Input::new_from_chars("hello".chars(), None);
let mismatch = Mismatch::without_expectation('h');
assert_eq!(
parser(&mut input),
Err(Error::UnexpectedToken(
None,
Position::new(1, 1),
Some(mismatch)
))
);
assert_eq!(any()(&mut input), Ok('h'));
assert_eq!(any()(&mut input), Ok('e'));
}
}