use regex::Regex;
#[derive(Debug, Clone, PartialEq)]
pub struct Match<'a> {
pub haystack: &'a str,
pub start: usize,
pub end: usize,
}
impl<'a> Match<'a> {
pub fn new(haystack: &'a str, start: usize, end: usize) -> Self {
assert!(start < end);
assert!(haystack.len() >= end);
Self {
haystack,
start,
end,
}
}
pub fn len(&self) -> usize {
self.end - self.start
}
pub fn as_str(&self) -> &'a str {
&self.haystack[self.start..self.end]
}
}
impl<'a> ToString for Match<'a> {
fn to_string(&self) -> String {
self.as_str().to_string()
}
}
pub trait Pattern<'a> {
fn find_in(&self, haystack: &'a str) -> Option<Match<'a>>;
fn rev_find_in(&self, haystack: &'a str) -> Option<Match<'a>> {
let mut cursor = haystack.len();
while cursor > 0 {
cursor -= 1;
if let Some(mat) = self.find_in(&haystack[cursor..]) {
return Some(Match::new(haystack, cursor + mat.start, cursor + mat.end));
}
}
None
}
fn find_prefix_in(&self, haystack: &'a str) -> Option<Match<'a>> {
self.find_in(haystack).filter(|mat| mat.start == 0)
}
fn find_suffix_in(&self, haystack: &'a str) -> Option<Match<'a>> {
self.rev_find_in(haystack)
.filter(|mat| mat.end == haystack.len())
}
}
impl<'a> Pattern<'a> for char {
fn find_in(&self, haystack: &'a str) -> Option<Match<'a>> {
haystack
.find(&self.to_string())
.map(|i| Match::new(haystack, i, i + 1))
}
}
impl<'a> Pattern<'a> for [char] {
fn find_in(&self, haystack: &'a str) -> Option<Match<'a>> {
self.into_iter().flat_map(|c| c.find_in(haystack)).next()
}
}
impl<'a, const N: usize> Pattern<'a> for [char; N] {
fn find_in(&self, haystack: &'a str) -> Option<Match<'a>> {
self.as_slice().find_in(haystack)
}
}
impl<'a, const N: usize> Pattern<'a> for &[char; N] {
fn find_in(&self, haystack: &'a str) -> Option<Match<'a>> {
self.as_slice().find_in(haystack)
}
}
impl<'a> Pattern<'a> for String {
fn find_in(&self, haystack: &'a str) -> Option<Match<'a>> {
self.as_str().find_in(haystack)
}
}
impl<'a> Pattern<'a> for &str {
fn find_in(&self, haystack: &'a str) -> Option<Match<'a>> {
haystack
.find(self)
.map(|i| Match::new(haystack, i, i + self.len()))
}
}
impl<'a> Pattern<'a> for [&str] {
fn find_in(&self, haystack: &'a str) -> Option<Match<'a>> {
self.into_iter().flat_map(|s| s.find_in(haystack)).next()
}
}
impl<'a, const N: usize> Pattern<'a> for [&str; N] {
fn find_in(&self, haystack: &'a str) -> Option<Match<'a>> {
self.as_slice().find_in(haystack)
}
}
impl<'a, const N: usize> Pattern<'a> for &[&str; N] {
fn find_in(&self, haystack: &'a str) -> Option<Match<'a>> {
self.as_slice().find_in(haystack)
}
}
impl<'a: 'b, 'b, F> Pattern<'a> for F
where
F: Fn(&'b str) -> bool,
{
fn find_in(&self, haystack: &'a str) -> Option<Match<'a>> {
let mut cur_1 = 0;
while cur_1 < haystack.len() {
let mut cur_2 = haystack.len();
while cur_2 > cur_1 {
let sub = &haystack[cur_1..cur_2];
if (self)(sub) {
return Some(Match::new(haystack, cur_1, cur_2));
}
cur_2 -= 1
}
cur_1 += 1;
}
None
}
}
impl<'a> Pattern<'a> for Regex {
fn find_in(&self, haystack: &'a str) -> Option<Match<'a>> {
self.find(haystack)
.map(|m| Match::new(haystack, m.start(), m.end()))
}
}