1const INITIAL: [char; 19] = [
26 'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ',
27 'ㅌ', 'ㅍ', 'ㅎ',
28];
29const MEDIAL: [char; 21] = [
30 'ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ', 'ㅗ', 'ㅘ', 'ㅙ', 'ㅚ', 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ',
31 'ㅟ', 'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ',
32];
33const FINAL: [char; 28] = [
34 ' ', 'ㄱ', 'ㄲ', 'ㄳ', 'ㄴ', 'ㄵ', 'ㄶ', 'ㄷ', 'ㄹ', 'ㄺ', 'ㄻ', 'ㄼ', 'ㄽ', 'ㄾ', 'ㄿ', 'ㅀ',
35 'ㅁ', 'ㅂ', 'ㅄ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ',
36];
37
38pub fn is_hangeul(word: char) -> bool {
41 ('가'..='힣').contains(&word)
42}
43
44fn is_consonant(word: char) -> bool {
46 ('ㄱ'..='ㅎ').contains(&word)
47}
48
49fn is_medial(word: char) -> bool {
51 ('ㅏ'..='ㅣ').contains(&word)
52}
53
54fn is_hangul_syllable(word: [char; 3]) -> bool {
59 if is_consonant(word[0]) && is_medial(word[1]) {
60 let res = FINAL.iter().position(|&s| s == word[2]);
61 res.is_some()
62 } else {
63 false
64 }
65}
66
67pub fn join_phonemes(word: [char; 3]) -> char {
78 if !is_hangul_syllable(word) {
80 return word[0];
81 }
82 let idx_begin = INITIAL.iter().position(|&x| x == word[0]).unwrap();
84 let idx_middle = MEDIAL.iter().position(|&x| x == word[1]).unwrap();
85 let idx_end = FINAL.iter().position(|&x| x == word[2]).unwrap();
86 let initial = '가' as u32;
88 let offset = ((idx_begin * MEDIAL.len() + idx_middle) * FINAL.len() + idx_end) as u32;
89 char::from_u32(initial + offset).unwrap()
90}
91
92pub fn modify_finall_jamo(letter: char, jamo: char) -> char {
106 if !is_hangeul(letter) {
107 return letter;
108 }
109 if FINAL.contains(&jamo) {
110 let mut splited_letter = split_phonemes(letter);
111 splited_letter[2] = jamo;
112 join_phonemes(splited_letter)
113 } else {
114 letter
115 }
116}
117
118pub fn split_phonemes(word: char) -> [char; 3] {
122 let mut phonemes: [char; 3] = [' '; 3];
124 if !is_hangeul(word) {
126 phonemes[0] = word;
127 return phonemes;
128 }
129 let unicode = word as u32;
131 let initial = '가' as u32;
132 let offset = unicode - initial;
133 let idx_begin: usize = (offset / (21 * 28)) as usize;
136 phonemes[0] = INITIAL[idx_begin];
137 let idx_middle: usize = ((offset / 28) % 21) as usize;
139 phonemes[1] = MEDIAL[idx_middle];
140 if (((unicode - 0xAC00) % (21 * 28)) % 28) != 0 {
142 let idx_end: usize = (offset % 28) as usize;
143 phonemes[2] = FINAL[idx_end];
144 }
145 phonemes
147}
148
149#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn _is_hangeul() {
156 let temp = '똠';
157 assert_eq!(true, is_hangeul(temp));
158
159 let temp = 'a';
160 assert_eq!(false, is_hangeul(temp));
161
162 let temp = '😀';
163 assert_eq!(false, is_hangeul(temp));
164 }
165
166 #[test]
167 fn _is_hangul_syllable() {
168 let temp = ['ㄱ', 'ㅏ', 'ㄴ'];
169 assert_eq!(true, is_hangul_syllable(temp));
170
171 let temp = ['ㄱ', 'ㄴ', 'ㄷ'];
172 assert_eq!(false, is_hangul_syllable(temp));
173
174 let temp = ['ㅊ', 'ㄴ', 'ㅓ'];
175 assert_eq!(false, is_hangul_syllable(temp));
176
177 let temp = ['😀', 'ㄴ', 'ㄷ'];
178 assert_eq!(false, is_hangul_syllable(temp));
179 }
180}