use std::mem::swap;
use crate::{
cache::Cache,
error::{
MatcherRunError,
error_handler::{ErrorHandler, MultiErrorHandler},
},
input::{Input, InputStream},
matcher::{MatchRunner, Matcher, MatcherCombinator},
parser::capture::MatchResult,
};
#[derive(Clone, Debug)]
pub struct CommitMatcher<CommitOn, ThenMatch> {
commit_on: CommitOn,
then_matcher: ThenMatch,
}
impl<CommitOn, ThenMatch> MatcherCombinator for CommitMatcher<CommitOn, ThenMatch> {}
impl<Commit, Match> CommitMatcher<Commit, Match> {
pub fn new(commit_on: Commit, matcher: Match) -> Self {
Self {
commit_on,
then_matcher: matcher,
}
}
}
impl<'src, Inp: Input<'src>, MRes, CommitOn, ThenMatch>
super::internal::MatcherImpl<'src, Inp, MRes> for CommitMatcher<CommitOn, ThenMatch>
where
CommitOn: Matcher<'src, Inp, MRes>,
ThenMatch: Matcher<'src, Inp, MRes>,
Inp: Input<'src>,
MRes: MatchResult,
{
const CAN_MATCH_DIRECTLY: bool =
CommitOn::CAN_MATCH_DIRECTLY && ThenMatch::CAN_MATCH_DIRECTLY_ASSUMING_NO_FAIL;
const HAS_PROPERTY: bool = CommitOn::HAS_PROPERTY || ThenMatch::HAS_PROPERTY;
const CAN_FAIL: bool = CommitOn::CAN_FAIL;
#[inline]
fn match_with_runner<'a, Runner>(
&'a self,
runner: &mut Runner,
error_handler: &mut impl ErrorHandler,
input: &mut InputStream<'src, Inp>,
) -> Result<bool, MatcherRunError>
where
Runner: MatchRunner<'a, 'src, Inp, MRes = MRes>,
'src: 'a,
{
if !runner.run_match(&self.commit_on, error_handler, input)? {
return Ok(false);
}
if !runner.is_in_error_recovery_mode() {
if runner.run_match(&self.then_matcher, error_handler, input)? {
return Ok(true);
}
return Err(MatcherRunError::RetryRerunNeeded);
}
let mut inner_error_handler = MultiErrorHandler::new(input.get_pos().into());
let mut cache = Cache::new();
swap(&mut runner.get_parser_context().cache, &mut cache);
let result = runner.run_match(&self.then_matcher, &mut inner_error_handler, input);
swap(&mut runner.get_parser_context().cache, &mut cache);
let result = result?;
if result {
Ok(true)
} else {
Err(MatcherRunError::FurthestFail(
inner_error_handler.to_parser_error(),
))
}
}
}
pub fn commit_on<CommitOn, ThenMatch>(
commit_on: CommitOn,
then_matcher: ThenMatch,
) -> CommitMatcher<CommitOn, ThenMatch> {
CommitMatcher::new(commit_on, then_matcher)
}