use crate::{GraphemePropCb, GraphemePropInCb, GraphemeProps, Mem, impl_trait};
#[derive(Clone, Copy, Debug, Default, Eq)]
pub(crate) enum GraphemeMachineState {
#[default]
Base,
AwaitRegionalPair,
BeforeZwj,
AfterZwj,
IndicConsonant,
IndicLinker,
}
impl_trait! { PartialEq for GraphemeMachineState |self, other| Self::eq(*self, *other) }
impl_trait! { Hash for GraphemeMachineState |self, state| { Mem::discriminant(self).hash(state); } }
impl GraphemeMachineState {
pub const fn eq(self, other: Self) -> bool {
matches!(
(self, other),
(Self::Base, Self::Base)
| (Self::AwaitRegionalPair, Self::AwaitRegionalPair)
| (Self::BeforeZwj, Self::BeforeZwj)
| (Self::AfterZwj, Self::AfterZwj)
| (Self::IndicConsonant, Self::IndicConsonant)
| (Self::IndicLinker, Self::IndicLinker)
)
}
pub const fn transition(
self,
prev: Option<GraphemeProps>,
next: GraphemeProps,
) -> (bool, GraphemeMachineState) {
use GraphemePropCb::{
CR, Extend, ExtendedPictographic, L, LF, LV, LVT, Prepend, RegionalIndicator,
SpacingMark, T, V, Zwj,
};
let next_state = self.next_state(next);
let Some(prev) = prev else {
return (true, next_state);
};
#[rustfmt::skip]
macro_rules! pair_matches {
($prev:pat, $next:pat) => {
matches!(prev.gcb_property(), $prev) && matches!(next.gcb_property(), $next)
};
}
#[rustfmt::skip]
macro_rules! one_matches {
($which:expr, $pat:pat) => { matches!($which.gcb_property(), $pat) };
}
if pair_matches!(CR, LF) {
return (false, next_state);
}
if prev.is_any_control() || next.is_any_control() {
return (true, next_state);
}
if pair_matches!(L, L | V | LV | LVT)
|| pair_matches!(LV | V, V | T)
|| pair_matches!(LVT | T, T)
{
return (false, next_state);
}
if one_matches!(next, Extend | Zwj) {
return (false, next_state);
}
if one_matches!(next, SpacingMark) {
return (false, next_state);
}
if one_matches!(prev, Prepend) {
return (false, next_state);
}
if self.gb9c_active() {
if matches!(prev.incb_property(), GraphemePropInCb::Linker | GraphemePropInCb::Extend)
&& matches!(next.incb_property(), GraphemePropInCb::Consonant)
{
return (false, next_state);
}
}
if self.gb11_active() && pair_matches!(Zwj, ExtendedPictographic) {
return (false, next_state);
}
if self.gb13_active() && pair_matches!(RegionalIndicator, RegionalIndicator) {
return (false, next_state);
}
(true, next_state)
}
const fn next_state(self, next: GraphemeProps) -> Self {
use {GraphemeMachineState as S, GraphemePropCb as P};
if matches!(next.gcb_property(), P::ExtendedPictographic) {
return S::BeforeZwj;
}
if matches!(next.incb_property(), GraphemePropInCb::Consonant) {
return S::IndicConsonant;
}
let gc_prop = next.gcb_property();
let incb_prop = next.incb_property();
match self {
S::Base => match gc_prop {
P::RegionalIndicator => S::AwaitRegionalPair,
_ => S::Base,
},
S::AwaitRegionalPair => S::Base,
S::BeforeZwj => match gc_prop {
P::Zwj => S::AfterZwj,
P::Extend => S::BeforeZwj,
_ => S::Base,
},
S::AfterZwj => S::Base,
S::IndicConsonant => match incb_prop {
GraphemePropInCb::Linker => S::IndicLinker,
GraphemePropInCb::Extend => S::IndicConsonant,
_ => S::Base,
},
S::IndicLinker => match incb_prop {
GraphemePropInCb::Linker | GraphemePropInCb::Extend => S::IndicLinker,
_ => S::Base,
},
}
}
#[inline(always)] #[rustfmt::skip]
const fn gb9c_active(self) -> bool { matches!(self, Self::IndicLinker) }
#[inline(always)] #[rustfmt::skip]
const fn gb11_active(self) -> bool { matches!(self, Self::AfterZwj) }
#[inline(always)] #[rustfmt::skip]
const fn gb13_active(self) -> bool { matches!(self, Self::AwaitRegionalPair) }
}