use std::fmt::Write;
use crate::error::CharClassError;
use super::unicode::{Category, CodeBlock, OtherProperties, Script};
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub(crate) enum CharGroup {
Dot,
CodePoint,
Items(Vec<GroupItem>),
}
impl CharGroup {
pub(crate) fn try_from_range(first: char, last: char) -> Option<Self> {
if first <= last {
Some(CharGroup::Items(vec![GroupItem::Range { first, last }]))
} else {
None
}
}
pub(crate) fn from_chars(chars: &str) -> Self {
CharGroup::Items(chars.chars().map(GroupItem::Char).collect())
}
pub(crate) fn from_char(c: char) -> Self {
CharGroup::Items(vec![GroupItem::Char(c)])
}
pub(crate) fn try_from_group_name(name: &str, negative: bool) -> Result<Self, CharClassError> {
Ok(match name {
_ if name == "ascii" || name.starts_with("ascii_") => {
CharGroup::Items(super::ascii::parse_ascii_group(name, negative)?)
}
"codepoint" | "cp" | "." if negative => {
return Err(CharClassError::Negative);
}
"let" | "lazy" | "greedy" | "range" | "atomic" | "enable" | "disable" => {
return Err(CharClassError::Keyword(name.to_string()));
}
"codepoint" | "cp" => CharGroup::CodePoint,
"." => CharGroup::Dot,
_ => CharGroup::Items(vec![GroupItem::Named {
name: super::unicode::parse_group_name(name)?,
negative,
}]),
})
}
pub(crate) fn add(&mut self, other: CharGroup) -> Result<(), CharClassError> {
match (self, other) {
(CharGroup::Items(it), CharGroup::Items(other)) => {
it.extend(other);
Ok(())
}
_ => Err(CharClassError::Unallowed),
}
}
}
impl core::fmt::Display for CharGroup {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CharGroup::Dot => f.write_str("`.`"),
CharGroup::CodePoint => f.write_str("`codepoint`"),
CharGroup::Items(i) => core::fmt::Debug::fmt(i, f),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum GroupItem {
Char(char),
Range { first: char, last: char },
Named { name: GroupName, negative: bool },
}
impl GroupItem {
pub(crate) fn range_unchecked(first: char, last: char) -> Self {
GroupItem::Range { first, last }
}
}
impl core::fmt::Debug for GroupItem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Self::Char(c) => c.fmt(f),
Self::Range { first, last } => write!(f, "{first:?}-{last:?}"),
Self::Named { name, negative } => {
if negative {
f.write_char('!')?;
}
let name = match name {
GroupName::Word => "word",
GroupName::Digit => "digit",
GroupName::Space => "space",
GroupName::HorizSpace => "horiz_space",
GroupName::VertSpace => "vert_space",
GroupName::Category(c) => {
f.write_str("category=")?;
c.as_str()
}
GroupName::Script(s) => {
f.write_str("script=")?;
s.as_str()
}
GroupName::CodeBlock(b) => {
f.write_str("block=")?;
b.as_str()
}
GroupName::OtherProperties(b) => {
f.write_str("prop=")?;
b.as_str()
}
};
f.write_str(name)
}
}
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub(crate) enum GroupName {
Word,
Digit,
Space,
HorizSpace,
VertSpace,
Category(Category),
Script(Script),
CodeBlock(CodeBlock),
OtherProperties(OtherProperties),
}