use core::{fmt::Debug, num::NonZeroUsize};
use core::ops::{RangeTo, RangeInclusive};
#[derive(Debug)]
pub enum Never{}
const _: Option<&dyn Pattern<Err = Never>> = None;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Sep{
Retain,
Yield,
Split,
Conjoin,
}
pub trait Pattern{
type Err: Debug;
fn matches(&mut self, c: char, s: &str) -> Result<bool, Self::Err>;
fn len(&self) -> NonZeroUsize { NonZeroUsize::new(1).unwrap() }
fn sep(&self) -> Sep { Sep::Retain }
}
impl Pattern for isize {
type Err = Never;
fn matches(&mut self, _: char, _: &str) -> Result<bool, Self::Err> {
*self -= 1;
Ok(*self == -1)
}
}
impl Pattern for RangeTo<isize> {
type Err = Never;
fn matches(&mut self, _: char, _: &str) -> Result<bool, Self::Err> {
self.end -= 1;
Ok(self.contains(&-1))
}
}
impl Pattern for RangeInclusive<char> {
type Err = Never;
fn matches(&mut self, c: char, _: &str) -> Result<bool, Self::Err> {
Ok(self.contains(&c))
}
}
impl Pattern for char {
type Err = Never;
fn matches(&mut self, c: char, _: &str) -> Result<bool, Self::Err> {
Ok(c == *self)
}
}
impl Pattern for &str {
type Err = Never;
fn matches(&mut self, _: char, s: &str) -> Result<bool, Self::Err> {
Ok(s.starts_with(*self))
}
fn len(&self) -> NonZeroUsize {
NonZeroUsize::new(self.chars().count())
.expect("\"\" is not a valid pattern")
}
}
impl Pattern for &[char] {
type Err = Never;
fn matches(&mut self, c: char, _: &str) -> Result<bool, Self::Err> {
Ok(self.contains(&c))
}
}
impl<const N: usize> Pattern for [char; N] {
type Err = Never;
fn matches(&mut self, c: char, _: &str) -> Result<bool, Self::Err> {
Ok(self.contains(&c))
}
}
mod private{
pub trait Sealed {}
}
pub trait FallibleBool: private::Sealed {
type Err: Debug;
fn get(self) -> Result<bool, Self::Err>;
}
impl private::Sealed for bool {}
impl FallibleBool for bool {
type Err = Never;
fn get(self) -> Result<bool, Self::Err> {
Ok(self)
}
}
impl private::Sealed for Option<bool> {}
impl FallibleBool for Option<bool> {
type Err = ();
fn get(self) -> Result<bool, ()> {
self.ok_or(())
}
}
impl<E: Debug> private::Sealed for Result<bool, E> {}
impl<E: Debug> FallibleBool for Result<bool, E> {
type Err = E;
fn get(self) -> Result<bool, E> {
self
}
}
impl<F, B> Pattern for F where F: FnMut(char) -> B, B: FallibleBool {
type Err = B::Err;
fn matches(&mut self, c: char, _: &str) -> Result<bool, Self::Err> {
self(c).get()
}
}
pub struct SizedStrPredicate<P: FnMut(&str) -> B, B: FallibleBool> {
pattern: P,
len: NonZeroUsize
}
pub trait StrPredicate<B: FallibleBool>: FnMut(&str) -> B + Sized{
fn expecting(self, len: usize) -> SizedStrPredicate<Self, B> {
let len = NonZeroUsize::new(len)
.expect("pattern cannot have length 0");
SizedStrPredicate { pattern: self, len }
}
}
impl<P: FnMut(&str) -> B, B: FallibleBool> Pattern for SizedStrPredicate<P, B> {
type Err = B::Err;
fn matches(&mut self, _: char, s: &str) -> Result<bool, Self::Err> {
(self.pattern)(s).get()
}
fn len(&self) -> NonZeroUsize { self.len }
}
pub struct SizedCharStrPredicate<P: FnMut(char, &str) -> B, B: FallibleBool> {
pattern: P,
len: NonZeroUsize
}
pub trait CharStrPredicate<B: FallibleBool>: FnMut(char, &str) -> B + Sized{
fn expecting(self, len: usize) -> SizedCharStrPredicate<Self, B> {
let len = NonZeroUsize::new(len)
.expect("pattern cannot have length 0");
SizedCharStrPredicate { pattern: self, len }
}
}
impl<P: FnMut(char, &str) -> B, B: FallibleBool> Pattern for SizedCharStrPredicate<P, B> {
type Err = B::Err;
fn matches(&mut self, c: char, s: &str) -> Result<bool, Self::Err> {
(self.pattern)(c, s).get()
}
fn len(&self) -> NonZeroUsize { self.len }
}
pub(crate) struct PatRef<'t, T: Pattern>(pub(crate) &'t mut T);
impl<'t, T: Pattern> Pattern for PatRef<'t, T> {
type Err = T::Err;
fn len(&self) -> NonZeroUsize {
self.0.len()
}
fn matches(&mut self, c: char, s: &str) -> Result<bool, Self::Err> {
self.0.matches(c, s)
}
fn sep(&self) -> Sep {
self.0.sep()
}
}
#[cfg(feature="std")]
const _:() = {
extern crate alloc;
use alloc::boxed::Box;
impl<E: Debug> Pattern for Box<dyn Pattern<Err = E>> {
type Err = E;
#[doc(hidden)]
fn len(&self) -> NonZeroUsize {
self.as_ref().len()
}
#[doc(hidden)]
fn matches(&mut self, c: char, s: &str) -> Result<bool, Self::Err> {
self.as_mut().matches(c, s)
}
#[doc(hidden)]
fn sep(&self) -> Sep {
self.as_ref().sep()
}
}
};
#[cfg(feature = "std")]
const _: () = {
extern crate alloc;
use alloc::string::String;
impl Pattern for String {
type Err = Never;
fn matches(&mut self, _: char, s: &str) -> Result<bool, Self::Err> {
Ok(self == &s)
}
fn len(&self) -> NonZeroUsize {
NonZeroUsize::new(self.chars().count())
.expect("\"\" is not a valid pattern")
}
}
impl Pattern for &String {
type Err = Never;
fn matches(&mut self, _: char, s: &str) -> Result<bool, Self::Err> {
Ok(self == &s)
}
fn len(&self) -> NonZeroUsize {
NonZeroUsize::new(self.chars().count())
.expect("\"\" is not a valid pattern")
}
}
};
pub struct SepConfig<P: SetSep> {
pattern: P,
config: Sep,
}
impl<P: SetSep> Pattern for SepConfig<P> {
type Err = P::Err;
fn matches(&mut self, c: char, s: &str) -> Result<bool, Self::Err> {
self.pattern.matches(c, s)
}
fn len(&self) -> NonZeroUsize { self.pattern.len() }
fn sep(&self) -> Sep {
self.config
}
}
pub trait SetSep: Pattern + Sized {
fn sep_with(self, sep: Sep) -> SepConfig<Self> {
SepConfig {
pattern: self,
config: sep,
}
}
}
impl<P> SetSep for P where P: Pattern + Sized {}
#[macro_export]
macro_rules! pat {
($p: pat) => {
|c: char| matches!(c, $p)
};
(! $p: pat) => {
|c: char| !matches!(c, $p)
};
($e: expr => $p: pat ) => {
(|s: str| matches!(c, $p)).expecting($e)
};
($e: expr => ! $p: pat ) => {
(|s: str| !matches!(c, $p)).expecting($e)
};
}