use std::marker::PhantomData;
use std::str::FromStr;
use std::fmt::{self, Formatter, Debug, Display};
use either::Either;
use regex::{Match, Captures};
use crate::{FromMatch, RegexPattern, ErasedLifetime, Error};
#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize, de::Deserializer};
macro_rules! empty_matcher {
($($name:ident => $pat:expr;)+) => {$(
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct $name;
impl RegexPattern for $name {
fn fmt_pattern(f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&$pat, f)
}
}
impl FromMatch<'_> for $name {
fn from_match(_name: &'static str, m: Match<'_>, _captures: &Captures<'_>) -> Result<Self, Error> {
assert!(m.is_empty());
Ok($name)
}
}
impl ErasedLifetime for $name {
type Erased = Self;
}
)+}
}
empty_matcher! {
LineStart => '^';
LineEnd => '$';
HaystackStart => r"\A";
HaystackEnd => r"\z";
WordStart => r"\<";
WordEnd => r"\>";
WordBoundary => r"\b";
}
#[derive(Clone, Copy, Debug)]
struct EscapeRegex(char);
impl Display for EscapeRegex {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let &EscapeRegex(ch) = self;
if regex_syntax::is_meta_character(ch) {
write!(f, r"\{ch}")
} else if ch.is_whitespace() {
write!(f, "{}", ch.escape_unicode()) } else {
write!(f, "{}", ch.escape_default()) }
}
}
#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, Debug)]
pub struct Char<const CHAR: char>;
impl<const CHAR: char> Char<CHAR> {
pub const fn value(&self) -> char {
CHAR
}
}
impl<const CHAR: char> TryFrom<char> for Char<CHAR> {
type Error = Error;
fn try_from(value: char) -> Result<Self, Self::Error> {
if value == CHAR {
Ok(Self)
} else {
let (expected, actual) = (u32::from(CHAR), u32::from(value));
Err(Error::message(format_args!("expected U+{expected:04X}, got U+{actual:04X}")))
}
}
}
impl<const CHAR: char> From<Char<CHAR>> for char {
fn from(c: Char<CHAR>) -> char {
c.value()
}
}
impl<const CHAR: char> RegexPattern for Char<CHAR> {
fn fmt_pattern(f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&EscapeRegex(CHAR), f)
}
}
impl<const CHAR: char> FromMatch<'_> for Char<CHAR> {
fn from_match(_name: &'static str, m: Match<'_>, _captures: &Captures<'_>) -> Result<Self, Error> {
assert_eq!(m.as_str().parse::<char>().expect("single-character match"), CHAR);
Ok(Char)
}
}
impl<const CHAR: char> ErasedLifetime for Char<CHAR> {
type Erased = Self;
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct CharRange<const START: char, const END: char>(char);
impl<const START: char, const END: char> CharRange<START, END> {
pub const fn value(&self) -> char {
self.0
}
}
impl<const START: char, const END: char> TryFrom<char> for CharRange<START, END> {
type Error = Error;
fn try_from(value: char) -> Result<Self, Self::Error> {
if (START..=END).contains(&value) {
Ok(CharRange(value))
} else {
let (start, end, value) = (START as u32, END as u32, value as u32);
Err(Error::message(format_args!("expected range U+{start:04X}-{end:04X}, got U+{value:04X}")))
}
}
}
impl<const START: char, const END: char> From<CharRange<START, END>> for char {
fn from(cr: CharRange<START, END>) -> char {
cr.value()
}
}
impl<const START: char, const END: char> FromStr for CharRange<START, END> {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse::<char>().map_err(Error::other).and_then(Self::try_from)
}
}
impl<const START: char, const END: char> RegexPattern for CharRange<START, END> {
fn fmt_pattern(f: &mut Formatter<'_>) -> fmt::Result {
assert!(
START <= END,
"range should have start <= end; got: U+{:04X}-{:04X}`",
START as u32, END as u32
);
write!(f, "[{start}-{end}]", start = EscapeRegex(START), end = EscapeRegex(END))
}
}
impl<const START: char, const END: char> FromMatch<'_> for CharRange<START, END> {
fn from_match(name: &'static str, m: Match<'_>, _captures: &Captures<'_>) -> Result<Self, Error> {
m.as_str()
.parse::<Self>()
.map_err(|error| Error::group_from_str(name, m.range(), error))
}
}
impl<const START: char, const END: char> ErasedLifetime for CharRange<START, END> {
type Erased = Self;
}
#[cfg(feature = "serde")]
impl<'de, const START: char, const END: char> Deserialize<'de> for CharRange<START, END> {
fn deserialize<D>(de: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>
{
char::deserialize(de).and_then(|ch| {
Self::try_from(ch).map_err(serde::de::Error::custom)
})
}
}
#[derive(PartialEq, Eq, Hash, Debug)]
pub struct CharClass<C: ?Sized> {
value: char,
class: PhantomData<fn() -> C>,
}
impl<C: ?Sized> CharClass<C> {
pub const fn value(&self) -> char {
self.value
}
}
impl<C: ?Sized> Clone for CharClass<C> {
fn clone(&self) -> Self {
*self
}
}
impl<C: ?Sized> Copy for CharClass<C> {}
impl<C: ?Sized> From<CharClass<C>> for char {
fn from(cc: CharClass<C>) -> char {
cc.value()
}
}
impl<C> RegexPattern for CharClass<C>
where
C: ?Sized + CharClassMarker
{
fn fmt_pattern(f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(C::PATTERN)
}
}
impl<C> FromMatch<'_> for CharClass<C>
where
C: ?Sized + CharClassMarker,
{
fn from_match(name: &'static str, m: Match<'_>, _captures: &Captures<'_>) -> Result<Self, Error> {
m.as_str()
.parse::<char>()
.map(|value| CharClass { value, class: PhantomData })
.map_err(|error| Error::group_from_str(name, m.range(), error))
}
}
impl<C> ErasedLifetime for CharClass<C>
where
C: ?Sized + ErasedLifetime,
{
type Erased = CharClass<C::Erased>;
}
pub trait CharClassMarker {
const PATTERN: &'static str;
}
macro_rules! impl_char_class_marker {
($($ty:ident => $pat:literal;)+) => {$(
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct $ty;
impl CharClassMarker for $ty {
const PATTERN: &'static str = $pat;
}
impl ErasedLifetime for $ty {
type Erased = Self;
}
)+}
}
impl_char_class_marker!{
Whitespace => r"\s";
NotWhitespace => r"\S";
Digit => r"\d";
NotDigit => r"\D";
AsciiAlnum => "[[:alnum:]]";
AsciiAlpha => "[[:alpha:]]";
AsciiAscii => "[[:ascii:]]";
AsciiBlank => "[[:blank:]]";
AsciiCntrl => "[[:cntrl:]]";
AsciiDigit => "[[:digit:]]";
AsciiGraph => "[[:graph:]]";
AsciiLower => "[[:lower:]]";
AsciiPrint => "[[:print:]]";
AsciiPunct => "[[:punct:]]";
AsciiSpace => "[[:space:]]";
AsciiUpper => "[[:upper:]]";
AsciiWord => "[[:word:]]";
AsciiXdigit => "[[:xdigit:]]";
AsciiNotAlnum => "[[:^alnum:]]";
AsciiNotAlpha => "[[:^alpha:]]";
AsciiNotAscii => "[[:^ascii:]]";
AsciiNotBlank => "[[:^blank:]]";
AsciiNotCntrl => "[[:^cntrl:]]";
AsciiNotDigit => "[[:^digit:]]";
AsciiNotGraph => "[[:^graph:]]";
AsciiNotLower => "[[:^lower:]]";
AsciiNotPrint => "[[:^print:]]";
AsciiNotPunct => "[[:^punct:]]";
AsciiNotSpace => "[[:^space:]]";
AsciiNotUpper => "[[:^upper:]]";
AsciiNotWord => "[[:^word:]]";
AsciiNotXdigit => "[[:^xdigit:]]";
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Repeat<const MIN: usize, const MAX: usize, P: ?Sized, T: ?Sized> {
marker: PhantomData<fn() -> P>,
value: T,
}
impl<const MIN: usize, const MAX: usize, P: ?Sized, T: ?Sized> Repeat<MIN, MAX, P, T> {
pub fn value(&self) -> &T {
&self.value
}
}
impl<const MIN: usize, const MAX: usize, P: ?Sized, T> Repeat<MIN, MAX, P, T> {
pub fn into_inner(self) -> T {
self.value
}
}
impl<const MIN: usize, const MAX: usize, P, T> RegexPattern for Repeat<MIN, MAX, P, T>
where
P: ?Sized + RegexPattern,
T: ?Sized,
{
fn fmt_pattern(f: &mut Formatter<'_>) -> fmt::Result {
assert!(MIN <= MAX, "expected repeat MIN <= MAX; got MIN={MIN}, MAX={MAX}");
let subexpr = P::pattern_display();
match (MIN, MAX) {
(0, 1) => write!(f, "(?:{subexpr})?"),
(0, usize::MAX) => write!(f, "(?:{subexpr})*"),
(1, usize::MAX) => write!(f, "(?:{subexpr})+"),
(_, usize::MAX) => write!(f, "(?:{subexpr}){{{MIN},}}"),
(_, _) => write!(f, "(?:{subexpr}){{{MIN},{MAX}}}"),
}
}
}
impl<'h, const MIN: usize, const MAX: usize, P, T> FromMatch<'h> for Repeat<MIN, MAX, P, T>
where
P: ?Sized,
T: FromMatch<'h>,
{
fn from_match(name: &'static str, m: Match<'h>, captures: &Captures<'h>) -> Result<Self, Error> {
T::from_match(name, m, captures)
.map(|value| Repeat { marker: PhantomData, value })
.map_err(|error| Error::group_from_str(name, m.range(), error))
}
}
impl<const MIN: usize, const MAX: usize, P, T> ErasedLifetime for Repeat<MIN, MAX, P, T>
where
P: ?Sized + ErasedLifetime,
T: ?Sized + ErasedLifetime,
{
type Erased = Repeat<MIN, MAX, P::Erased, T::Erased>;
}
pub type AtLeast<const MIN: usize, P, T> = Repeat<MIN, {usize::MAX}, P, T>;
pub type AtMost<const MAX: usize, P, T> = Repeat<0, MAX, P, T>;
pub type Maybe<T> = Repeat<0, 1, T, Option<T>>; pub type Any<P, T> = Repeat<0, {usize::MAX}, P, T>;
pub type Any1<P, T> = Repeat<1, {usize::MAX}, P, T>;
pub type Alternation<L, R> = Either<L, R>;
impl<L, R> RegexPattern for Alternation<L, R>
where
L: RegexPattern,
R: RegexPattern,
{
fn fmt_pattern(f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "(?:{})|(?:{})", L::pattern_display(), R::pattern_display())
}
}
impl<'h, L, R> FromMatch<'h> for Alternation<L, R>
where
L: FromMatch<'h>,
R: FromMatch<'h>,
{
fn from_match(name: &'static str, m: Match<'h>, captures: &Captures<'h>) -> Result<Self, Error> {
L::from_match(name, m, captures)
.map(Alternation::Left)
.or_else(|left_err| {
R::from_match(name, m, captures)
.map(Alternation::Right)
.map_err(|right_err| {
Error::group_from_str(name, m.range(), Error::message(format_args!(
"both sides of alternation failed; left: {left_err}; right: {right_err}"
)))
})
})
}
}
impl<L, R> ErasedLifetime for Alternation<L, R>
where
L: ErasedLifetime,
L::Erased: Sized,
R: ErasedLifetime,
R::Erased: Sized,
{
type Erased = Alternation<L::Erased, R::Erased>;
}
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct Ignore<T: ?Sized>(PhantomData<fn() -> T>);
impl<T: ?Sized> Ignore<T> {
pub const fn new() -> Self {
Ignore(PhantomData)
}
}
impl<T: ?Sized> Clone for Ignore<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized> Copy for Ignore<T> {}
impl<T: ?Sized> Default for Ignore<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: ?Sized> Debug for Ignore<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "Ignore<{}>(..)", std::any::type_name::<T>())
}
}
impl<T> RegexPattern for Ignore<T>
where
T: ?Sized + RegexPattern
{
fn fmt_pattern(f: &mut Formatter<'_>) -> fmt::Result {
T::fmt_pattern(f) }
}
impl<T> FromMatch<'_> for Ignore<T> {
fn from_match(_name: &'static str, _m: Match<'_>, _captures: &Captures<'_>) -> Result<Self, Error> {
Ok(Self::new())
}
}
impl<T: ?Sized + ErasedLifetime> ErasedLifetime for Ignore<T> {
type Erased = Ignore<T::Erased>;
}