use std::iter;
use crate::hangul::{Choseong, Jongseong, Jungseong, Syllable};
#[derive(Debug)]
pub enum MixedChar {
Character(char),
Syllable(Syllable),
}
impl From<char> for MixedChar {
fn from(c: char) -> Self {
Syllable::try_from(c).map_or_else(
|_| MixedChar::Character(c),
|s| MixedChar::Syllable(s),
)
}
}
#[derive(Clone, Debug)]
pub enum MixedBlock {
String(String),
Syllables(Vec<Syllable>),
}
impl FromIterator<MixedChar> for Vec<MixedBlock> {
fn from_iter<T: IntoIterator<Item = MixedChar>>(iter: T) -> Self {
let mut result = vec![];
let mut current = None;
for c in iter.into_iter() {
let Some(block) = &mut current else {
current = match c {
MixedChar::Character(c) => Some(
MixedBlock::String(c.to_string()),
),
MixedChar::Syllable(syllable) => Some(
MixedBlock::Syllables(vec![syllable]),
),
};
continue;
};
match block {
MixedBlock::String(s) => {
match c {
MixedChar::Character(c) => {
s.push(c);
},
MixedChar::Syllable(syllable) => {
let block = current.unwrap();
result.push(block);
current = Some(MixedBlock::Syllables(vec![syllable]));
},
}
},
MixedBlock::Syllables(syllables) => {
match c {
MixedChar::Character(c) => {
let block = current.unwrap();
result.push(block);
current = Some(MixedBlock::String(c.to_string()));
},
MixedChar::Syllable(syllable) => {
syllables.push(syllable);
},
}
},
}
}
if let Some(current) = current { result.push(current) };
result
}
}
pub trait Romanizable {
fn romanize(self) -> String;
}
impl Romanizable for MixedChar {
fn romanize(self) -> String {
match self {
MixedChar::Character(c) => c.to_string(),
MixedChar::Syllable(syllable) => syllable.romanize(),
}
}
}
impl Romanizable for char {
fn romanize(self) -> String {
self.to_string()
}
}
impl Romanizable for Vec<MixedBlock> {
fn romanize(self) -> String {
self.into_iter()
.map(|m| m.romanize())
.collect()
}
}
impl Romanizable for MixedBlock {
fn romanize(self) -> String {
match self {
MixedBlock::String(s) => s.romanize(),
MixedBlock::Syllables(syllables) => syllables.romanize(),
}
}
}
impl Romanizable for String {
fn romanize(self) -> String {
self
}
}
pub trait RomanizeTransform {
fn t_transcribe(self) -> Self;
fn nasal_assimilate(self) -> Self;
fn epenthetic_insertion(self) -> Self;
fn palatalization(self) -> Self;
}
impl RomanizeTransform for Vec<Syllable> {
fn t_transcribe(self) -> Self {
self.into_iter()
.map(|Syllable (c, ju, jo)| match jo {
Jongseong::Chieut => Syllable(c.clone(), ju.clone(), Jongseong::Digeut),
Jongseong::Tieut => Syllable(c.clone(), ju.clone(), Jongseong::Digeut),
Jongseong::Jieut => Syllable(c.clone(), ju.clone(), Jongseong::Digeut),
_ => Syllable(c, ju, jo),
})
.collect()
}
fn nasal_assimilate(self) -> Self {
if self.is_empty() { return vec![] };
let first_pass: Vec<Syllable> = self.iter()
.cloned()
.zip(self.iter().skip(1))
.map(|(Syllable (c1, ju1, jo1), Syllable (c2, _ju2, _jo2))| match (&jo1, c2) {
(Jongseong::Giyeok, Choseong::Nieun) => Syllable(c1, ju1, Jongseong::Ieung),
(Jongseong::Giyeok, Choseong::Mieum) => Syllable(c1, ju1, Jongseong::Ieung),
(Jongseong::Digeut, Choseong::Nieun) => Syllable(c1, ju1, Jongseong::Nieun),
(Jongseong::Digeut, Choseong::Mieum) => Syllable(c1, ju1, Jongseong::Nieun),
(Jongseong::Bieup, Choseong::Nieun) => Syllable(c1, ju1, Jongseong::Mieum),
(Jongseong::Bieup, Choseong::Mieum) => Syllable(c1, ju1, Jongseong::Mieum),
(Jongseong::Giyeok, Choseong::Rieul) => Syllable(c1, ju1, Jongseong::Ieung),
(Jongseong::Digeut, Choseong::Rieul) => Syllable(c1, ju1, Jongseong::Nieun),
(Jongseong::Bieup, Choseong::Rieul) => Syllable(c1, ju1, Jongseong::Mieum),
_ => Syllable (c1, ju1, jo1),
}).chain(iter::once(self.last().unwrap().clone())).collect();
iter::once(first_pass.first().unwrap().clone()).chain(first_pass
.iter()
.cloned()
.zip(first_pass.iter().skip(1).cloned())
.map(|(Syllable (_c1, _ju1, jo1), Syllable (c2, ju2, jo2))| match (&jo1, &c2) {
(Jongseong::Mieum, Choseong::Rieul) => Syllable (Choseong::Nieun, ju2, jo2),
(Jongseong::Ieung, Choseong::Rieul) => Syllable (Choseong::Nieun, ju2, jo2),
(Jongseong::Giyeok, Choseong::Rieul) => Syllable (Choseong::Nieun, ju2, jo2),
(Jongseong::Digeut, Choseong::Rieul) => Syllable (Choseong::Nieun, ju2, jo2),
(Jongseong::Bieup, Choseong::Rieul) => Syllable (Choseong::Nieun, ju2, jo2),
_ => Syllable (c2, ju2, jo2),
}),
).collect()
}
fn epenthetic_insertion(self) -> Self {
let first_pass: Vec<Syllable> = self.iter()
.cloned()
.zip(self.iter().skip(1))
.map(|(Syllable (c1, ju1, jo1), Syllable (_c2, ju2, _jo2))| match (&jo1, ju2){
(Jongseong::Giyeok, Jungseong::YEO) => Syllable (c1, ju1, Jongseong::Ieung),
_ => Syllable (c1, ju1, jo1),
}).chain(iter::once(self.last().unwrap().clone())).collect();
iter::once(first_pass.first().unwrap().clone()).chain(first_pass
.iter()
.cloned()
.zip(first_pass.iter().skip(1).cloned())
.map(|(Syllable (_c1, _ju1, jo1), Syllable (c2, ju2, jo2))| match (&jo1, &c2, &ju2) {
(Jongseong::Ieung, Choseong::Ieung, Jungseong::YEO) => Syllable (Choseong::Nieun, ju2, jo2),
(Jongseong::Mieum, Choseong::Ieung, Jungseong::I) => Syllable (Choseong::Nieun, ju2, jo2),
(Jongseong::Ieung, Choseong::Ieung, Jungseong::YU) => Syllable (Choseong::Nieun, ju2, jo2),
(Jongseong::Rieul, Choseong::Ieung, Jungseong::YA) => Syllable (Choseong::Nieun, ju2, jo2),
_ => Syllable (c2, ju2, jo2),
}),
).collect()
}
fn palatalization(self) -> Self {
let (moved_forward, mut syllables) = self.iter().cloned().zip(self.iter().skip(1))
.fold((
None,
vec![],
), |(moved_forward, mut list): (
Option<Choseong>,
Vec<Syllable>,
), (Syllable (mut c1, ju1, jo1), Syllable (c2, ju2, _jo2))| {
if let Some(moved) = moved_forward {
c1 = moved.clone();
}
let moved_forward = match (&jo1, c2, ju2) {
(Jongseong::Digeut, Choseong::Ieung, Jungseong::I) => {
Some(Choseong::Jieut)
},
(Jongseong::Tieut, Choseong::Ieung, Jungseong::I) => {
Some(Choseong::Chieut)
},
(Jongseong::Digeut, Choseong::Hieuh, Jungseong::I) => {
Some(Choseong::Chieut)
}
_ => None,
};
list.push(Syllable (c1, ju1, if moved_forward.is_none() { jo1 } else { Jongseong::None }));
(moved_forward, list)
});
if let Some(Syllable (c, ju, jo)) = self.last().cloned() {
syllables.push(Syllable (moved_forward.unwrap_or(c), ju, jo));
}
syllables
}
}
impl Romanizable for Vec<Syllable> {
fn romanize(self) -> String {
let transformed = self
.palatalization()
.t_transcribe()
.nasal_assimilate()
.epenthetic_insertion();
let some_syllables = transformed.clone().into_iter().map(|s| Some(s));
let left = iter::once(None).chain(some_syllables.clone());
let right = some_syllables.skip(1).chain(iter::once(None));
left.zip(transformed.into_iter()).zip(right)
.map(|((prev, Syllable (c, ju, jo)), next)| {
let romanized_choseong = if let Some(Syllable (_, _, prev_jo)) = prev {
match (prev_jo, &c) {
(Jongseong::Rieul, Choseong::Rieul) => "l".into(),
(Jongseong::Nieun, Choseong::Rieul) => "l".into(),
(Jongseong::Rieul, Choseong::Nieun) => "l".into(),
_ => c.romanize()
}
} else {
c.romanize()
};
let romanized_joseong = if let Some(Syllable (next_c, _, _)) = next {
match (&jo, next_c) {
(Jongseong::Giyeok, Choseong::Ieung) => "g".into(),
(Jongseong::Digeut, Choseong::Ieung) => "d".into(),
(Jongseong::Bieup, Choseong::Ieung) => "b".into(),
(Jongseong::Rieul, Choseong::Ieung) => "r".into(),
(Jongseong::Nieun, Choseong::Rieul) => "l".into(),
_ => jo.romanize(),
}
} else {
jo.romanize()
};
format!("{}{}{}", romanized_choseong, ju.romanize(), romanized_joseong)
}).collect()
}
}
impl Romanizable for Syllable {
fn romanize(self) -> String {
let Syllable (choseong, jungseong, jongseong) = self;
let r_choseong = choseong.romanize();
let r_jungseong = jungseong.romanize();
let r_jongseong = jongseong.romanize();
r_choseong + &r_jungseong + &r_jongseong
}
}
impl Romanizable for Choseong {
fn romanize(self) -> String {
use Choseong::*;
match self {
Giyeok => "g",
SsangGiyeok => "kk",
Nieun => "n",
Digeut => "d",
SsangDigeut => "tt",
Rieul => "r",
Mieum => "m",
Bieup => "b",
SsangBieup => "pp",
Siot => "s",
SsangSiot => "ss",
Ieung => "",
Jieut => "j",
SsangJieut => "jj",
Chieut => "ch",
Kieuk => "k",
Tieut => "t",
Pieup => "p",
Hieuh => "h",
}.into()
}
}
impl Romanizable for Jungseong {
fn romanize(self) -> String {
use Jungseong::*;
match self {
A => "a",
AE => "ae",
YA => "ya",
YAE => "yae",
EO => "eo",
E => "e",
YEO => "yeo",
YE => "ye",
O => "o",
WA => "wa",
WAE => "wae",
OE => "oe",
YO => "yo",
U => "u",
WEO => "wo",
WE => "we",
WI => "wi",
YU => "yu",
EU => "eu",
YI => "ui",
I => "i",
}.into()
}
}
impl Romanizable for Jongseong {
fn romanize(self) -> String {
use Jongseong::*;
match self {
None => "",
Giyeok => "k",
SsangGiyeok => "kk",
GiyeokSiot => "ks",
Nieun => "n",
NieunJieut => "nj",
NieunHieuh => "nh",
Digeut => "t",
Rieul => "l",
RieulGiyeok => "lg",
RieulMieum => "lm",
RieulBieup => "lb",
RieulSiot => "ls",
RieulTieut => "lt",
RieulPieup => "lp",
RieulHieuh => "lh",
Mieum => "m",
Bieup => "p",
BieupSiot => "bs",
Siot => "s",
SsangSiot => "ss",
Ieung => "ng",
Jieut => "j",
Chieut => "ch",
Kieuk => "k",
Tieut => "t",
Pieup => "p",
Hieuh => "h",
}.into()
}
}