mod and;
mod falling_edge;
mod heading;
mod one_shot;
mod start_of_next_line;
pub use and::And;
pub use falling_edge::FallingEdge;
pub use heading::Heading;
pub use one_shot::OneShot;
pub use start_of_next_line::StartOfNextLine;
use pulldown_cmark::{Event, Tag};
use std::borrow::Borrow;
pub trait Matcher {
fn matches_event(&mut self, event: &Event<'_>) -> bool;
fn first_match<'src, I, E>(mut self, events: I) -> Option<usize>
where
Self: Sized,
I: IntoIterator<Item = E> + 'src,
E: Borrow<Event<'src>> + 'src,
{
events
.into_iter()
.position(|ev| self.matches_event(ev.borrow()))
}
fn is_in<'src, I, E>(mut self, events: I) -> bool
where
I: IntoIterator<Item = E> + 'src,
E: Borrow<Event<'src>>,
Self: Sized,
{
events.into_iter().any(|ev| self.matches_event(ev.borrow()))
}
fn then_start_of_next_line(self) -> StartOfNextLine<Self>
where
Self: Sized,
{
StartOfNextLine::new(self)
}
fn fuse(self) -> OneShot<Self>
where
Self: Sized,
{
OneShot::new(self)
}
fn falling_edge(self) -> FallingEdge<Self>
where
Self: Sized,
{
FallingEdge::new(self)
}
fn and<M>(self, other: M) -> And<Self, M>
where
Self: Sized,
M: Matcher,
{
And::new(self, other)
}
fn by_ref(&mut self) -> Ref<'_, Self>
where
Self: Sized,
{
Ref(self)
}
}
impl<F> Matcher for F
where
F: FnMut(&Event<'_>) -> bool,
{
fn matches_event(&mut self, event: &Event<'_>) -> bool { self(event) }
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Always;
impl Matcher for Always {
fn matches_event(&mut self, _event: &Event<'_>) -> bool { true }
}
pub fn match_indices<'ev, M, I>(
mut matcher: M,
events: I,
) -> impl Iterator<Item = usize> + 'ev
where
M: Matcher + 'ev,
I: IntoIterator + 'ev,
I::Item: Borrow<Event<'ev>> + 'ev,
{
events
.into_iter()
.enumerate()
.filter_map(move |(i, event)| {
if matcher.matches_event(event.borrow()) {
Some(i)
} else {
None
}
})
}
pub fn between<'ev, S, E>(
start: S,
end: E,
events: &'ev [Event<'ev>],
) -> Option<&'ev [Event<'ev>]>
where
S: Matcher,
E: Matcher,
{
if let Some(start_ix) = match_indices(start, events).next() {
let rest = &events[start_ix..];
return Some(
end.first_match(rest)
.map_or(rest, |end_ix| &rest[..=end_ix]),
);
}
None
}
pub fn exact_text<S: AsRef<str>>(needle: S) -> impl Matcher {
text(move |text| AsRef::<str>::as_ref(text) == needle.as_ref())
}
pub fn text_containing<S: AsRef<str>>(needle: S) -> impl Matcher {
text(move |text| text.contains(needle.as_ref()))
}
pub fn text<P>(mut predicate: P) -> impl Matcher
where
P: FnMut(&str) -> bool,
{
move |ev: &Event<'_>| match ev {
Event::Text(text) => predicate(text.as_ref()),
_ => false,
}
}
pub fn link_with_url_containing<S: AsRef<str>>(needle: S) -> impl Matcher {
move |ev: &Event<'_>| match ev {
Event::Start(Tag::Link(_, link, _)) => {
link.as_ref().contains(needle.as_ref())
},
_ => false,
}
}
#[derive(Debug)]
pub struct Ref<'a, M>(&'a mut M);
impl<'a, M> Matcher for Ref<'a, M>
where
M: Matcher,
{
fn matches_event(&mut self, event: &Event<'_>) -> bool {
self.0.matches_event(event)
}
}