use std::error::Error;
use std::fmt;
const CHOSEONG_TO_JAMO: [u32; 19] = [
1, 2, 4, 7, 8, 9, 17, 18, 19, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
];
const JUNGSEONG_START: u32 = 0x1161;
const JUNGSEONG_END: u32 = 0x1175;
const JUNGSEONG_COUNT: u32 = JUNGSEONG_END - JUNGSEONG_START + 1;
const JONGSEONG_START: u32 = 0x11A8;
const JONGSEONG_END: u32 = 0x11C2;
const JONGSEONG_COUNT: u32 = JONGSEONG_END - JONGSEONG_START + 2; const JONGSEONG_TO_JAMO: [u32; 27] = [
1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 26, 27, 28, 29,
30,
];
const JAMO_START: u32 = 0x3130;
const SYLLABLE_START: u32 = 0xAC00; const SYLLABLE_END: u32 = 0xD7A3;
pub trait HangulExt {
fn is_syllable(self) -> bool;
fn is_open(self) -> Result<bool, ParseSyllableError>;
fn is_closed(self) -> Result<bool, ParseSyllableError>;
fn choseong(self) -> Result<char, ParseSyllableError>;
fn jungseong(self) -> Result<char, ParseSyllableError>;
fn jongseong(self) -> Result<Option<char>, ParseSyllableError>;
fn to_jamo(self) -> Result<(char, char, Option<char>), ParseSyllableError>;
fn jamos(self) -> Result<Jamos, ParseSyllableError>;
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug)]
pub struct ParseSyllableError(pub char);
impl fmt::Display for ParseSyllableError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} is not a Hangul Syllable", self.0)
}
}
impl Error for ParseSyllableError {}
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug)]
pub struct Jamos {
value: Vec<char>,
}
impl Iterator for Jamos {
type Item = char;
fn next(&mut self) -> Option<char> {
self.value.pop()
}
}
impl HangulExt for char {
fn is_syllable(self) -> bool {
let cp = self as u32; cp >= SYLLABLE_START && cp <= SYLLABLE_END
}
fn is_open(self) -> Result<bool, ParseSyllableError> {
self.jongseong().map(|j| j.is_none())
}
fn is_closed(self) -> Result<bool, ParseSyllableError> {
self.jongseong().map(|j| j.is_some())
}
fn choseong(self) -> Result<char, ParseSyllableError> {
if self.is_syllable() {
let choseong_offset = (offset(self) / JONGSEONG_COUNT / JUNGSEONG_COUNT) as usize;
Ok(std::char::from_u32(JAMO_START + CHOSEONG_TO_JAMO[choseong_offset]).unwrap())
} else {
Err(ParseSyllableError(self))
}
}
fn jungseong(self) -> Result<char, ParseSyllableError> {
if self.is_syllable() {
let jungseong_offset = (offset(self) / JONGSEONG_COUNT % JUNGSEONG_COUNT) as u32;
Ok(std::char::from_u32(JAMO_START + jungseong_offset + 31).unwrap())
} else {
Err(ParseSyllableError(self))
}
}
fn jongseong(self) -> Result<Option<char>, ParseSyllableError> {
if self.is_syllable() {
let jongseong_offset = (offset(self) % JONGSEONG_COUNT) as usize;
if jongseong_offset > 0 {
Ok(std::char::from_u32(
JAMO_START + JONGSEONG_TO_JAMO[jongseong_offset - 1],
))
} else {
Ok(None)
}
} else {
Err(ParseSyllableError(self))
}
}
fn to_jamo(self) -> Result<(char, char, Option<char>), ParseSyllableError> {
if self.is_syllable() {
Ok((
self.choseong().unwrap(),
self.jungseong().unwrap(),
self.jongseong().unwrap(),
))
} else {
Err(ParseSyllableError(self))
}
}
fn jamos(self) -> Result<Jamos, ParseSyllableError> {
if self.is_syllable() {
let mut jamos = Jamos { value: Vec::new() };
if let Ok(Some(jong)) = self.jongseong() {
jamos.value.push(jong);
}
jamos.value.push(self.jungseong().unwrap());
jamos.value.push(self.choseong().unwrap());
Ok(jamos)
} else {
Err(ParseSyllableError(self))
}
}
}
fn offset(c: char) -> u32 {
c as u32 - SYLLABLE_START
}