#[rustfmt::skip]
mod data;
pub(crate) fn get_data(ch: char) -> Option<GreekPrecomposedLetterData> {
let ch_i = ch as usize;
let packed = if (0x370..=0x3FF).contains(&ch_i) {
*data::DATA_370.get(ch_i - 0x370)?
} else if (0x1f00..0x1fff).contains(&ch_i) {
*data::DATA_1F00.get(ch_i - 0x1f00)?
} else {
data::match_extras(ch)?
};
let packed = PackedGreekPrecomposedLetterData(packed);
GreekPrecomposedLetterData::try_from(packed).ok()
}
#[derive(Debug, Clone, Copy)]
pub struct PackedGreekPrecomposedLetterData(pub u8);
impl TryFrom<PackedGreekPrecomposedLetterData> for GreekPrecomposedLetterData {
type Error = ();
fn try_from(other: PackedGreekPrecomposedLetterData) -> Result<GreekPrecomposedLetterData, ()> {
if other.0 == 0 {
return Err(());
}
if other.0 & 0x80 == 0 {
let diacritics = GreekDiacritics {
accented: other.0 & 0x40 != 0,
dialytika: other.0 & 0x20 != 0,
ypogegrammeni: other.0 & 0x10 != 0,
};
let vowel = GreekVowel::try_from(other.0 & 0b1111);
debug_assert!(vowel.is_ok());
let vowel = vowel.unwrap_or(GreekVowel::Α);
Ok(GreekPrecomposedLetterData::Vowel(vowel, diacritics))
} else {
Ok(GreekPrecomposedLetterData::Consonant(other.0 == 0x81))
}
}
}
impl From<GreekPrecomposedLetterData> for PackedGreekPrecomposedLetterData {
fn from(other: GreekPrecomposedLetterData) -> Self {
match other {
GreekPrecomposedLetterData::Vowel(vowel, diacritics) => {
let mut bits = 0;
if diacritics.accented {
bits |= 0x40;
}
if diacritics.dialytika {
bits |= 0x20;
}
if diacritics.ypogegrammeni {
bits |= 0x10;
}
bits |= vowel as u8;
PackedGreekPrecomposedLetterData(bits)
}
GreekPrecomposedLetterData::Consonant(is_rho) => {
PackedGreekPrecomposedLetterData(0x80 + is_rho as u8)
}
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum GreekPrecomposedLetterData {
Vowel(GreekVowel, GreekDiacritics),
Consonant(bool),
}
#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
pub enum GreekVowel {
Α = 1,
Ε = 2,
Η = 3,
Ι = 4,
Ο = 5,
Υ = 6,
Ω = 7,
ϒ = 8,
}
pub const CAPITAL_RHO: char = 'Ρ';
impl From<GreekVowel> for char {
fn from(other: GreekVowel) -> Self {
match other {
GreekVowel::Α => 'Α',
GreekVowel::Ε => 'Ε',
GreekVowel::Η => 'Η',
GreekVowel::Ι => 'Ι',
GreekVowel::Ο => 'Ο',
GreekVowel::Υ => 'Υ',
GreekVowel::Ω => 'Ω',
GreekVowel::ϒ => 'ϒ',
}
}
}
impl TryFrom<char> for GreekVowel {
type Error = ();
fn try_from(other: char) -> Result<Self, ()> {
Ok(match other {
'Α' => GreekVowel::Α,
'Ε' => GreekVowel::Ε,
'Η' => GreekVowel::Η,
'Ι' => GreekVowel::Ι,
'Ο' => GreekVowel::Ο,
'Υ' => GreekVowel::Υ,
'Ω' => GreekVowel::Ω,
'ϒ' => GreekVowel::ϒ,
_ => return Err(()),
})
}
}
impl TryFrom<u8> for GreekVowel {
type Error = ();
fn try_from(other: u8) -> Result<Self, ()> {
Ok(match other {
1 => Self::Α,
2 => Self::Ε,
3 => Self::Η,
4 => Self::Ι,
5 => Self::Ο,
6 => Self::Υ,
7 => Self::Ω,
8 => Self::ϒ,
_ => return Err(()),
})
}
}
#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
pub struct GreekDiacritics {
pub accented: bool,
pub dialytika: bool,
pub ypogegrammeni: bool,
}
#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]
pub struct GreekCombiningCharacterSequenceDiacritics {
pub precomposed: GreekDiacritics,
pub combining: GreekDiacritics,
}
pub const TONOS: char = '\u{0301}';
pub const DIALYTIKA: char = '\u{0308}';
pub const DIALYTIKA_TONOS: char = '\u{0344}';
pub const YPOGEGRAMMENI: char = '\u{0345}';
#[macro_export]
#[doc(hidden)] macro_rules! diacritics {
(ACCENTS) => {
'\u{0300}' | $crate::greek_to_me::TONOS | '\u{0342}' | '\u{0302}' | '\u{0303}' | '\u{0311}' };
(BREATHING_AND_LENGTH) => {
'\u{0304}' | '\u{0306}' | '\u{0313}' | '\u{0314}' | '\u{0343}' };
(DIALYTIKA_ALL) => { $crate::greek_to_me::DIALYTIKA | $crate::greek_to_me::DIALYTIKA_TONOS };
(DIALYTIKA) => { $crate::greek_to_me::DIALYTIKA };
(DIALYTIKA_TONOS) => { $crate::greek_to_me::DIALYTIKA_TONOS };
(YPOGEGRAMMENI) => { $crate::greek_to_me::YPOGEGRAMMENI };
($($i:ident)|+) => { $(diacritics!($i))|+};
}
pub use crate::diacritics;
impl GreekDiacritics {
pub(crate) fn consume_greek_diacritics(&mut self, context_after: &str) {
for c in context_after.chars() {
match c {
diacritics!(ACCENTS) => self.accented = true,
DIALYTIKA_TONOS => {
self.dialytika = true;
self.accented = true;
}
DIALYTIKA => self.dialytika = true,
YPOGEGRAMMENI => self.ypogegrammeni = true,
diacritics!(BREATHING_AND_LENGTH) => (),
_ => break,
}
}
}
}
pub(crate) fn preceded_by_greek_letter(context_before: &str) -> bool {
for c in context_before.chars().rev() {
match c {
diacritics!(ACCENTS | BREATHING_AND_LENGTH | DIALYTIKA_ALL | YPOGEGRAMMENI) => continue,
_ => return get_data(c).is_some(),
}
}
false
}
pub(crate) fn preceding_greek_vowel_diacritics(
context_before: &str,
) -> Option<GreekCombiningCharacterSequenceDiacritics> {
let mut combining: GreekDiacritics = Default::default();
for c in context_before.chars().rev() {
match c {
diacritics!(ACCENTS) => combining.accented = true,
diacritics!(DIALYTIKA_TONOS) => {
combining.dialytika = true;
combining.accented = true;
}
diacritics!(DIALYTIKA) => combining.dialytika = true,
diacritics!(BREATHING_AND_LENGTH) => continue,
_ => {
let data = get_data(c);
if let Some(GreekPrecomposedLetterData::Vowel(_vowel, diacritics)) = data {
return Some(GreekCombiningCharacterSequenceDiacritics {
precomposed: diacritics,
combining,
});
} else {
return None;
}
}
}
}
None
}
pub(crate) fn is_greek_diacritic_except_ypogegrammeni(c: char) -> bool {
matches!(
c,
diacritics!(ACCENTS | BREATHING_AND_LENGTH | DIALYTIKA_ALL)
)
}