use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::ops::{Range, RangeInclusive};
#[derive(Clone, Debug, Serialize, Deserialize, Hash, Eq, PartialEq)]
pub enum CharacterClass {
RangeInclusive {
from: char,
to: char, },
Range {
from: char,
to: char, },
Contained(Vec<char>),
Choice(Vec<CharacterClass>),
Not(Box<CharacterClass>),
Nothing,
}
impl Display for CharacterClass {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
CharacterClass::RangeInclusive { from, to } if from == to => {
write!(f, "{}", from)
}
CharacterClass::RangeInclusive { from, to } => {
write!(f, "[{}-{}]", from, to)
}
CharacterClass::Range { from, to } => {
write!(f, "[{}-{}] (exclusive)", from, to)
}
CharacterClass::Contained(list) => {
write!(f, "{}", list.iter().join(""))
}
CharacterClass::Choice(ccs) => {
write!(f, "{}", ccs.iter().map(|cc| cc.to_string()).join(" or "))
}
CharacterClass::Not(not) => {
write!(f, "not {}", not)
}
CharacterClass::Nothing => {
write!(f, "")
}
}
}
}
impl CharacterClass {
pub fn contains(&self, c: char) -> bool {
match self {
CharacterClass::RangeInclusive { from, to } => {
c as u32 >= *from as u32 && c as u32 <= *to as u32
}
CharacterClass::Range { from, to } => {
(c as u32) >= *from as u32 && (c as u32) < *to as u32
}
CharacterClass::Choice(parts) => parts.iter().map(|i| i.contains(c)).any(|i| i),
CharacterClass::Not(cls) => !cls.contains(c),
CharacterClass::Nothing => false,
CharacterClass::Contained(chars) => chars.contains(&c),
}
}
pub const fn all_in_vec(chars: Vec<char>) -> Self {
Self::Contained(chars)
}
pub fn invert(self) -> Self {
Self::Not(Box::new(self))
}
pub fn combine(self, other: CharacterClass) -> CharacterClass {
CharacterClass::Choice(vec![self, other])
}
}
impl From<RangeInclusive<char>> for CharacterClass {
fn from(r: RangeInclusive<char>) -> Self {
Self::RangeInclusive {
from: *r.start(),
to: *r.end(),
}
}
}
impl From<Range<char>> for CharacterClass {
fn from(r: Range<char>) -> Self {
Self::Range {
from: r.start,
to: r.end,
}
}
}
impl From<char> for CharacterClass {
fn from(c: char) -> Self {
Self::RangeInclusive { from: c, to: c }
}
}
impl From<&[char]> for CharacterClass {
fn from(s: &[char]) -> Self {
Self::Contained(s.to_vec())
}
}
impl From<Vec<char>> for CharacterClass {
fn from(s: Vec<char>) -> Self {
Self::Contained(s)
}
}
impl From<String> for CharacterClass {
fn from(s: String) -> Self {
Self::Contained(s.chars().collect())
}
}
impl<'a> From<&'a str> for CharacterClass {
fn from(s: &'a str) -> Self {
Self::Contained(s.chars().collect())
}
}