use std::{borrow::Cow, ops::RangeInclusive};
use crate::{MatchAble, MatchBy, MatchSignal, MatchStatus, Matcher};
impl MatchAble for str {
type Slice<'a> = &'a str;
#[inline]
fn len(&self) -> usize {
self.len()
}
#[inline]
fn slice(&self, range: std::ops::Range<usize>) -> &str {
&self[range]
}
#[inline]
fn get_n(&self, ind: &mut usize, n: usize, _status: &MatchStatus) -> Result<&str, MatchSignal> {
let tail = &self[*ind..];
let mut chars = tail.chars();
if chars.nth(n - 1).is_none() {
return Err(MatchSignal::InComplete);
}
let offset = tail.len() - chars.as_str().len();
*ind += offset;
Ok(unsafe { tail.get_unchecked(..offset) })
}
}
impl MatchBy<char> for str {
#[inline]
fn match_by(&self, matcher: char, ind: &mut usize, _status: &MatchStatus) -> MatchSignal {
let len = matcher.len_utf8();
if len + *ind > self.len() {
MatchSignal::InComplete
} else if self[*ind..].starts_with(matcher) {
*ind += len;
MatchSignal::Matched
} else {
MatchSignal::MisMatched
}
}
}
impl<'a> MatchBy<&'a str> for str {
#[inline]
fn match_by(&self, matcher: &'a str, ind: &mut usize, _status: &MatchStatus) -> MatchSignal {
let len = matcher.len();
if len + *ind > self.len() {
MatchSignal::InComplete
} else if self[*ind..].starts_with(matcher) {
*ind += len;
MatchSignal::Matched
} else {
MatchSignal::MisMatched
}
}
}
impl<'a> MatchBy<&'a char> for str {
#[inline]
fn match_by(&self, matcher: &'a char, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
MatchBy::match_by(self, *matcher, ind, status)
}
}
macro_rules! as_ref_impl {
[$($ty:ty) +] => {
$(impl<'a> MatchBy<&'a $ty> for str {
#[inline]
fn match_by(
&self, matcher: &'a $ty, ind: &mut usize, status: &MatchStatus,
) -> MatchSignal {
MatchBy::match_by(self, AsRef::<str>::as_ref(matcher), ind, status)
}
})*
};
}
as_ref_impl![String Box<str> Cow<'a, str>];
impl MatchBy<RangeInclusive<char>> for str {
#[inline]
fn match_by(
&self, matcher: RangeInclusive<char>, ind: &mut usize, _status: &MatchStatus,
) -> MatchSignal {
let Some(cur_char) = self[*ind..].chars().next() else {
return MatchSignal::InComplete;
};
if matcher.contains(&cur_char) {
*ind += cur_char.len_utf8();
MatchSignal::Matched
} else {
MatchSignal::MisMatched
}
}
}
macro_rules! match_char {
($value:ident, $ind:ident, $char:ident => $predicate:expr) => {{
let Some($char) = $value[*$ind..].chars().next() else {
return MatchSignal::InComplete;
};
*$ind += $char.len_utf8();
if $predicate { MatchSignal::Matched } else { MatchSignal::MisMatched }
}};
}
#[inline]
pub fn a_char(predicate: impl Fn(char) -> bool) -> impl Matcher<str> {
move |v, ind, _| match_char!(v, ind, char => predicate(char))
}
#[inline]
pub fn lower(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => char.is_lowercase())
}
#[inline]
pub fn upper(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => char.is_uppercase())
}
#[inline]
pub fn alpha(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => char.is_alphabetic())
}
#[inline]
pub fn num(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => char.is_numeric())
}
#[inline]
pub fn alphanum(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => char.is_alphanumeric())
}
#[inline]
pub fn ws(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => char.is_whitespace())
}
#[inline]
pub fn control(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => char.is_control())
}
#[inline]
pub fn ascii(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => char.is_ascii())
}
#[inline]
pub fn ascii_lower(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => char.is_ascii_lowercase())
}
#[inline]
pub fn ascii_upper(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => char.is_ascii_uppercase())
}
#[inline]
pub fn ascii_alpha(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => char.is_ascii_alphabetic())
}
#[inline]
pub fn ascii_alphanum(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => char.is_ascii_alphanumeric())
}
#[inline]
pub fn ascii_ws(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => char.is_ascii_whitespace())
}
#[inline]
pub fn ascii_control(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => char.is_ascii_control())
}
#[inline]
pub fn ascii_printable(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => char.is_ascii_graphic())
}
#[inline]
pub fn ascii_punct(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => char.is_ascii_punctuation())
}
#[inline]
#[allow(clippy::manual_is_ascii_check)]
pub fn dec(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => matches!(char, '0'..='9'))
}
#[inline]
#[allow(clippy::manual_is_ascii_check)]
pub fn hex(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => matches!(char, '0'..='9' | 'a'..='f' | 'A'..='F'))
}
#[inline]
pub fn hex_lower(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => matches!(char, '0'..='9' | 'a'..='f'))
}
#[inline]
pub fn hex_upper(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => matches!(char, '0'..='9' | 'A'..='F'))
}
#[inline]
pub fn octal(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => matches!(char, '0'..='7'))
}
#[inline]
pub fn bin(value: &str, ind: &mut usize, status: &MatchStatus) -> MatchSignal {
_ = status;
match_char!(value, ind, char => matches!(char, '0' | '1'))
}
#[inline]
pub fn digit(radix: u32) -> impl Matcher<str> {
move |value, ind, _| match_char!(value, ind, char => char.is_digit(radix))
}