use crate::query::pest::{Pair, Pairs, Rule};
pub(crate) type OnePair<'a> = OneOf<Pair<'a>>;
pub(crate) trait PairMatcher {
fn matches(&self, pair: &Pair) -> bool;
fn find_all_in(self, pairs: Pairs) -> Vec<Pair>
where
Self: Sized,
{
FindAll::new(self).find_in(pairs)
}
}
pub(crate) trait PairMatchStore<'a> {
type Output;
fn match_and_store(&mut self, pair: Pair<'a>) -> MatchStoreResult<'a>;
fn get(self) -> Self::Output;
fn find_in(mut self, pairs: Pairs<'a>) -> Self::Output
where
Self: Sized,
{
fn build<'b>(me: &mut impl PairMatchStore<'b>, pairs: Pairs<'b>) {
for pair in pairs {
if let MatchStoreResult::NotStored(unmatched) = me.match_and_store(pair) {
build(me, unmatched.into_inner())
}
}
}
build(&mut self, pairs);
self.get()
}
}
pub(crate) enum MatchStoreResult<'a> {
Stored,
NotStored(Pair<'a>),
}
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
pub(crate) struct Present(bool);
impl Present {
pub(crate) fn is_present(&self) -> bool {
self.0
}
}
impl Present {
pub(crate) fn store(&mut self, _pair: Pair) {
self.0 = true
}
}
#[derive(Debug)]
pub(crate) struct OneOf<T>(Result<Option<T>, ()>);
impl<T> Default for OneOf<T> {
fn default() -> Self {
Self(Ok(None))
}
}
impl<T> OneOf<T> {
pub(crate) fn take(self) -> Result<Option<T>, String> {
self.0.map_err(|_| "multiple items found".to_string())
}
pub(crate) fn store(&mut self, item: T) {
self.0 = match self.0 {
Ok(Some(_)) | Err(_) => Err(()),
Ok(None) => Ok(Some(item)),
}
}
}
#[derive(Debug)]
pub(crate) struct FindAll<'a, M>(M, Vec<Pair<'a>>);
impl<M> FindAll<'_, M> {
pub(crate) fn new(matcher: M) -> Self {
Self(matcher, Vec::new())
}
}
impl<'a, M> PairMatchStore<'a> for FindAll<'a, M>
where
M: PairMatcher,
{
type Output = Vec<Pair<'a>>;
fn match_and_store(&mut self, pair: Pair<'a>) -> MatchStoreResult<'a> {
if self.0.matches(&pair) {
self.1.push(pair);
MatchStoreResult::Stored
} else {
MatchStoreResult::NotStored(pair)
}
}
fn get(self) -> Self::Output {
self.1
}
}
#[derive(Debug)]
pub(crate) struct ByRule(Rule);
impl ByRule {
pub(crate) fn new(rule: Rule) -> Self {
Self(rule)
}
}
impl PairMatcher for ByRule {
fn matches(&self, pair: &Pair) -> bool {
self.0 == pair.as_rule()
}
}
#[derive(Debug)]
pub(crate) struct ByTag(&'static str);
impl ByTag {
pub(crate) fn new(tag: &'static str) -> Self {
Self(tag)
}
}
impl PairMatcher for ByTag {
fn matches(&self, pair: &Pair) -> bool {
match pair.as_node_tag() {
Some(t) => t == self.0,
None => false,
}
}
}