#[cfg(test)]
mod tests {
use super::*;
const TEST_CASES: [&'static str; 329] = [
"a", "a", "a", "ap1", "am", "am", "at", "ak1", "ai2", "aip1", "aim2", "aik2", "aik2",
"aum2", "auk2", "ia1", "iam1", "iat1", "ian1", "iak1", "ua", "ua", "uai1", "uaip2", "i2",
"im1", "in2", "ik2", "y", "y1", "yp", "yt2", "yn2", "yk2", "ut2", "un1", "em", "et2", "o1",
"om2", "ie", "iei2", "io", "io1", "io2", "ue1", "uep", "uep1", "uet", "uo1", "pa2", "pai2",
"pau1", "pia2", "pua2", "puat2", "pit1", "py", "put", "pui1", "pet2", "pek1", "pek2",
"po1", "po1", "pok", "bap", "bai2", "bait", "bau2", "bia1", "bu1", "bu1", "bup2", "bum1",
"be1", "bet2", "bo1", "bot1", "bon2", "buo1", "ma1", "mam1", "man1", "mak1", "mak2",
"maun1", "mua2", "muak1", "mit", "mit2", "mut2", "mun1", "mui1", "mo1", "mot2", "mok1",
"muo1", "can2", "cai1", "cuau2", "cy2", "cuk2", "cet2", "cei2", "cei2", "co1", "co1",
"cue", "cue1", "cuet", "cuop2", "sa1", "sam1", "sat2", "sak2", "sak2", "sau2", "saup1",
"sia1", "sip1", "sit1", "sin1", "sik2", "sy2", "sy2", "syt2", "su1", "sui1", "sep", "so1",
"son1", "sue1", "suo2", "xa2", "xai1", "xau1", "xi1", "xit2", "xy1", "xep2", "xem1", "xo1",
"xo2", "xom1", "xom1", "xot1", "xuo1", "zap2", "zat1", "zau", "zip1", "zit1", "zui1",
"zuit2", "ze1", "zep1", "zo1", "zie1", "zuo1", "ta", "ta1", "tam2", "tat2", "tan1", "tak1",
"tak1", "tai", "tau2", "taun1", "tauk1", "tia1", "tia2", "tua1", "tua1", "tuai2", "tim1",
"tit1", "tin1", "ty", "tyn1", "tu2", "tum1", "tut1", "tun2", "tuk2", "tuk2", "tui2",
"tuik1", "tet", "ten1", "to2", "dat2", "dau2", "daut1", "dit1", "dui", "det", "dop1",
"na1", "nap1", "nam2", "nan2", "naip2", "nau", "nua2", "nuak1", "ni1", "nim2", "nim2",
"nin1", "nik2", "num1", "nut2", "nun1", "nui2", "ne2", "ne2", "net2", "no1", "nom2",
"non1", "nie", "niek1", "nuok2", "la1", "lai1", "lia1", "lua1", "li1", "li2", "lip",
"lit1", "lin", "ly", "lu2", "lup1", "let1", "lek1", "lo", "lo1", "lot2", "lok1", "lok1",
"liok1", "ka1", "kat1", "kat2", "kan1", "kan1", "kak", "kai1", "kait", "kaik", "kaun1",
"kauk2", "kia1", "kiak1", "kua2", "kuak1", "kuak1", "kuai1", "kuai1", "kuai2", "ki1",
"kit", "kit1", "kit1", "kin2", "kin2", "kik1", "kik1", "ky1", "ky2", "ku", "kut2", "kun2",
"ket", "ko1", "kop1", "kot", "kot2", "kok1", "kue", "kuo1", "kuot1", "gat2", "gak1",
"gau2", "giau1", "gua2", "gy1", "guk2", "gui2", "ge", "gek", "hak2", "hau2", "hauk1",
"hia1", "hua1", "huap1", "huat1", "huai2", "hi1", "hy2", "hup1", "hut2", "hui2", "he",
"hem", "hem1", "hem1", "hen", "hei2", "ho", "ho1", "ho2", "hop1", "hon1", "hok1", "hio1",
"hue", "hue1", "huep2", "huet", "huo2", "huok1", "huok2",
];
const TEST_CASES_RERRLIRATIXKA: [&'static str; 349] = [
"prai", "mrua", "kar", "zer", "zrap", "nran", "cuer", "ber", "murn", "raim", "huart",
"tirn", "ku", "cruk", "em", "a", "hue", "kak", "xrit", "puir", "puri", "sirt", "zerp",
"marn", "krua", "ark", "hruo", "taruk", "zuir", "zuri", "nirn", "lerk", "horp", "xir",
"hiar", "tar", "iarm", "hro", "raik", "huet", "hurp", "kot", "karn", "nrua", "prua", "sry",
"krin", "bait", "xuor", "dui", "mar", "nir", "crei", "lar", "tark", "brai", "kiar", "xerm",
"pret", "sryt", "hruep", "zor", "nrut", "dorp", "srau", "rut", "tyrn", "kyr", "kiark",
"ryk", "tarn", "hrak", "suer", "hrut", "ta", "iarn", "iarn", "lirt", "ret", "rik", "rom",
"arp", "urn", "nret", "nrik", "nrom", "narp", "krut", "mork", "srak", "cran", "sarup",
"lurp", "muir", "muri", "hrui", "kirk", "bur", "at", "paru", "kir", "sar", "tram", "trun",
"muark", "krauk", "grua", "karun", "drau", "marun", "truk", "uari", "io", "sruo", "zau",
"dirt", "grau", "cruop", "mrut", "kait", "hir", "herm", "gruk", "trui", "py", "ly", "ua",
"hem", "am", "ty", "ryn", "ie", "truai", "norn", "put", "a", "lert", "xor", "iart", "muor",
"zier", "gark", "krun", "trau", "iark", "drat", "y", "mrak", "xorm", "sirp", "bap", "rin",
"nram", "riei", "krat", "kart", "tern", "lir", "yr", "biar", "brau", "xra", "nierk", "uer",
"ket", "grat", "hrau", "kaik", "mrot", "ge", "cruau", "turt", "nar", "gyr", "kit", "pok",
"sur", "sirn", "nraip", "ruaip", "rai", "bor", "nrim", "lin", "huarp", "xro", "kork",
"tuar", "tuar", "perk", "prek", "ho", "hor", "hrei", "xort", "arip", "rauk", "raum",
"bret", "burm", "buor", "cret", "cor", "cry", "darut", "giaru", "grui", "hior", "hry",
"hork", "horn", "huar", "hruai", "huer", "huork", "hruok", "ri", "iar", "ryt", "kari",
"kry", "kor", "kuari", "kuort", "kuark", "liar", "liork", "lor", "lork", "lru", "luar",
"mark", "marm", "mrit", "mor", "nau", "nre", "nor", "nrui", "nurm", "nurn", "nruok",
"pria", "por", "pruat", "sarm", "srat", "siar", "srik", "sor", "sorn", "suir", "suri",
"trat", "tet", "tiar", "tria", "tirt", "tro", "tru", "tuirk", "turik", "turm", "xari",
"xaru", "xrep", "xyr", "zart", "zirt", "zruit", "zuor", "kruai", "uor", "pirt", "cari",
"yp", "brup", "crei", "or", "krin", "tark", "kirk", "bur", "raik", "xorm", "sry", "am",
"lo", "herm", "kuor", "srak", "nre", "tai", "haruk", "lrot", "ior", "horn", "liar", "irm",
"karn", "kuark", "a", "rio", "tarun", "uet", "cuet", "truk", "por", "uep", "bort", "lork",
"lip", "uerp", "kuari", "sep", "zirp", "nie", "mit", "nrim", "hen", "he", "ua", "kirt",
"kirt", "iarn", "krot", "lari", "lri", "rai", "bron", "ho", "burm", "kuari", "ruaip",
"drat", "marun", "hork", "krat", "pirt", "pirt", "gek", "yp", "lork", "det", "tiar",
"sruo", "nuark", "tirm", "korp",
];
#[test]
fn parse_all_and_check_equality() {
for index in 0..TEST_CASES.len() {
let syll = PekZepSyllable::parse(TEST_CASES[index]).unwrap();
assert_eq!(TEST_CASES[index], syll.to_string())
}
}
#[test]
#[should_panic]
fn parse_xizi() {
PekZepSyllable::parse("xici").unwrap();
}
#[test]
fn parse_all_rerrliratixka() {
for index in 0..TEST_CASES_RERRLIRATIXKA.len() {
let syll =
PekZepSyllable::parse_from_rerrliratixka(TEST_CASES_RERRLIRATIXKA[index]).unwrap();
if TEST_CASES_RERRLIRATIXKA[index] != syll.to_rerrliratixka() {
assert_eq!(&TEST_CASES_RERRLIRATIXKA[index][1..4], "uri")
}
}
}
}
#[derive(PartialEq, Eq, Debug, Clone, Hash, Copy)]
pub enum Tone {
No,
First,
Second,
}
#[derive(PartialEq, Eq, Debug, Clone, Hash, Copy)]
pub enum Onset {
P,
B,
M,
C,
S,
X,
Z,
T,
D,
N,
L,
K,
G,
H,
Zero,
}
#[derive(PartialEq, Eq, Debug, Clone, Hash, Copy)]
pub enum Vowel {
A,
IA,
UA,
AI,
UAI,
AU,
IAU,
UAU,
E,
IE,
UE,
EI,
IEI,
O,
IO,
UO,
I,
UI,
U,
Y,
}
#[derive(PartialEq, Eq, Debug, Clone, Copy, Hash)]
pub enum Coda {
P,
T,
K,
M,
N,
Zero,
}
use std::fmt;
impl fmt::Display for Tone {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
Tone::First => "1",
Tone::Second => "2",
Tone::No => "",
}
)
}
}
impl fmt::Display for Coda {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
Coda::P => "p",
Coda::T => "t",
Coda::K => "k",
Coda::M => "m",
Coda::N => "n",
Coda::Zero => "",
}
)
}
}
impl fmt::Display for Onset {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
Onset::P => "p",
Onset::T => "t",
Onset::K => "k",
Onset::M => "m",
Onset::N => "n",
Onset::Zero => "",
Onset::B => "b",
Onset::G => "g",
Onset::C => "c",
Onset::S => "s",
Onset::X => "x",
Onset::H => "h",
Onset::Z => "z",
Onset::L => "l",
Onset::D => "d",
}
)
}
}
impl fmt::Display for Vowel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
Vowel::A => "a",
Vowel::IA => "ia",
Vowel::UA => "ua",
Vowel::AI => "ai",
Vowel::UAI => "uai",
Vowel::AU => "au",
Vowel::IAU => "iau",
Vowel::UAU => "uau",
Vowel::E => "e",
Vowel::IE => "ie",
Vowel::UE => "ue",
Vowel::EI => "ei",
Vowel::IEI => "iei",
Vowel::O => "o",
Vowel::IO => "io",
Vowel::UO => "uo",
Vowel::I => "i",
Vowel::UI => "ui",
Vowel::U => "u",
Vowel::Y => "y",
}
)
}
}
impl Vowel {
fn first_tone_rerrliratixka(self) -> &'static str {
match self {
Vowel::A => "ar",
Vowel::IA => "iar",
Vowel::UA => "uar",
Vowel::AI => "ari",
Vowel::UAI => "uari",
Vowel::AU => "aru",
Vowel::IAU => "iaru",
Vowel::UAU => "uaru",
Vowel::E => "er",
Vowel::IE => "ier",
Vowel::UE => "uer",
Vowel::EI => "eri",
Vowel::IEI => "ieri",
Vowel::O => "or",
Vowel::IO => "ior",
Vowel::UO => "uor",
Vowel::I => "ir",
Vowel::UI => "uir",
Vowel::U => "ur",
Vowel::Y => "yr",
}
}
}
#[derive(PartialEq, Eq, Debug, Clone, Hash, Copy)]
pub struct PekZepSyllable {
onset: Onset,
vowel: Vowel,
coda: Coda,
tone: Tone,
}
impl fmt::Display for PekZepSyllable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}{}{}{}",
self.onset.to_string(),
self.vowel.to_string(),
self.coda.to_string(),
self.tone.to_string(),
)
}
}
impl PekZepSyllable {
pub fn to_rerrliratixka(self) -> String {
format!(
"{}{}{}{}",
self.onset.to_string(),
if self.tone == Tone::Second { "r" } else { "" },
if self.tone == Tone::First {
self.vowel.first_tone_rerrliratixka().to_string()
} else {
self.vowel.to_string()
},
self.coda.to_string()
)
}
pub fn parse_from_rerrliratixka(s: &str) -> Option<PekZepSyllable> {
let chars: Vec<char> = s.chars().collect();
let onset = match chars[0] {
'p' => Onset::P,
'b' => Onset::B,
'm' => Onset::M,
'c' => Onset::C,
's' => Onset::S,
'x' => Onset::X,
'z' => Onset::Z,
't' => Onset::T,
'd' => Onset::D,
'n' => Onset::N,
'l' => Onset::L,
'k' => Onset::K,
'g' => Onset::G,
'h' => Onset::H,
'a' | 'e' | 'i' | 'u' | 'o' | 'y' | 'r' => Onset::Zero,
_ => return None,
};
let remaining = chars[(if onset == Onset::Zero { 0 } else { 1 })..chars.len()].to_vec();
let coda = match remaining[remaining.len() - 1] {
'p' => Coda::P,
't' => Coda::T,
'k' => Coda::K,
'm' => Coda::M,
'n' => Coda::N,
_ => Coda::Zero,
};
let vow = remaining[0..remaining.len() - (if coda == Coda::Zero { 0 } else { 1 })].to_vec();
if vow[0] == 'r' {
let vowel = match vow[1..] {
['a'] => Vowel::A,
['i', 'a'] => Vowel::IA,
['u', 'a'] => Vowel::UA,
['a', 'i'] => Vowel::AI,
['u', 'a', 'i'] => Vowel::UAI,
['a', 'u'] => Vowel::AU,
['i', 'a', 'u'] => Vowel::IAU,
['u', 'a', 'u'] => Vowel::UAU,
['e'] => Vowel::E,
['i', 'e'] => Vowel::IE,
['u', 'e'] => Vowel::UE,
['e', 'i'] => Vowel::EI,
['i', 'e', 'i'] => Vowel::IEI,
['o'] => Vowel::O,
['i', 'o'] => Vowel::IO,
['u', 'o'] => Vowel::UO,
['i'] => Vowel::I,
['u', 'i'] => Vowel::UI,
['u'] => Vowel::U,
['y'] => Vowel::Y,
_ => return None,
};
Some(PekZepSyllable {
vowel,
onset,
coda,
tone: Tone::Second,
})
} else {
let (vowel, tone) = match vow.as_slice() {
['a'] => (Vowel::A, Tone::No),
['i', 'a'] => (Vowel::IA, Tone::No),
['u', 'a'] => (Vowel::UA, Tone::No),
['a', 'i'] => (Vowel::AI, Tone::No),
['u', 'a', 'i'] => (Vowel::UAI, Tone::No),
['a', 'u'] => (Vowel::AU, Tone::No),
['i', 'a', 'u'] => (Vowel::IAU, Tone::No),
['u', 'a', 'u'] => (Vowel::UAU, Tone::No),
['e'] => (Vowel::E, Tone::No),
['i', 'e'] => (Vowel::IE, Tone::No),
['u', 'e'] => (Vowel::UE, Tone::No),
['e', 'i'] => (Vowel::EI, Tone::No),
['i', 'e', 'i'] => (Vowel::IEI, Tone::No),
['o'] => (Vowel::O, Tone::No),
['i', 'o'] => (Vowel::IO, Tone::No),
['u', 'o'] => (Vowel::UO, Tone::No),
['i'] => (Vowel::I, Tone::No),
['u', 'i'] => (Vowel::UI, Tone::No),
['u'] => (Vowel::U, Tone::No),
['y'] => (Vowel::Y, Tone::No),
['a', 'r'] => (Vowel::A, Tone::First),
['i', 'a', 'r'] => (Vowel::IA, Tone::First),
['u', 'a', 'r'] => (Vowel::UA, Tone::First),
['a', 'r', 'i'] => (Vowel::AI, Tone::First),
['u', 'a', 'r', 'i'] => (Vowel::UAI, Tone::First),
['a', 'r', 'u'] => (Vowel::AU, Tone::First),
['i', 'a', 'r', 'u'] => (Vowel::IAU, Tone::First),
['u', 'a', 'r', 'u'] => (Vowel::UAU, Tone::First),
['e', 'r'] => (Vowel::E, Tone::First),
['i', 'e', 'r'] => (Vowel::IE, Tone::First),
['u', 'e', 'r'] => (Vowel::UE, Tone::First),
['e', 'r', 'i'] => (Vowel::EI, Tone::First),
['i', 'e', 'r', 'i'] => (Vowel::IEI, Tone::First),
['o', 'r'] => (Vowel::O, Tone::First),
['i', 'o', 'r'] => (Vowel::IO, Tone::First),
['u', 'o', 'r'] => (Vowel::UO, Tone::First),
['i', 'r'] => (Vowel::I, Tone::First),
['u', 'i', 'r'] => (Vowel::UI, Tone::First),
['u', 'r', 'i'] => (Vowel::UI, Tone::First),
['u', 'r'] => (Vowel::U, Tone::First),
['y', 'r'] => (Vowel::Y, Tone::First),
_ => return None,
};
Some(PekZepSyllable {
vowel,
tone,
onset,
coda,
})
}
}
pub fn parse(s: &str) -> Option<PekZepSyllable> {
let chars: Vec<char> = s.chars().collect();
let tone = match chars[chars.len() - 1] {
'1' => Tone::First,
'2' => Tone::Second,
_ => Tone::No,
};
let onset = match chars[0] {
'p' => Onset::P,
'b' => Onset::B,
'm' => Onset::M,
'c' => Onset::C,
's' => Onset::S,
'x' => Onset::X,
'z' => Onset::Z,
't' => Onset::T,
'd' => Onset::D,
'n' => Onset::N,
'l' => Onset::L,
'k' => Onset::K,
'g' => Onset::G,
'h' => Onset::H,
'a' | 'e' | 'i' | 'u' | 'o' | 'y' => Onset::Zero,
_ => return None,
};
let remaining = chars[(if onset == Onset::Zero { 0 } else { 1 })
..chars.len() - (if tone == Tone::No { 0 } else { 1 })]
.to_vec();
let coda = match remaining[remaining.len() - 1] {
'p' => Coda::P,
't' => Coda::T,
'k' => Coda::K,
'm' => Coda::M,
'n' => Coda::N,
_ => Coda::Zero,
};
let vow = remaining[0..remaining.len() - (if coda == Coda::Zero { 0 } else { 1 })].to_vec();
let vowel = match vow.as_slice() {
['a'] => Vowel::A,
['i', 'a'] => Vowel::IA,
['u', 'a'] => Vowel::UA,
['a', 'i'] => Vowel::AI,
['u', 'a', 'i'] => Vowel::UAI,
['a', 'u'] => Vowel::AU,
['i', 'a', 'u'] => Vowel::IAU,
['u', 'a', 'u'] => Vowel::UAU,
['e'] => Vowel::E,
['i', 'e'] => Vowel::IE,
['u', 'e'] => Vowel::UE,
['e', 'i'] => Vowel::EI,
['i', 'e', 'i'] => Vowel::IEI,
['o'] => Vowel::O,
['i', 'o'] => Vowel::IO,
['u', 'o'] => Vowel::UO,
['i'] => Vowel::I,
['u', 'i'] => Vowel::UI,
['u'] => Vowel::U,
['y'] => Vowel::Y,
_ => return None,
};
Some(PekZepSyllable {
onset,
vowel,
coda,
tone,
})
}
}