1use thiserror::Error;
2
3#[derive(Error, Debug, PartialEq, Eq)]
5pub enum JamoError {
6 #[error("Could not convert character '{0}' to Jamo")]
8 FromCharError(char),
9}
10
11#[derive(Debug, PartialEq, Eq)]
15pub enum JamoUnicodeType {
16 Modern,
19
20 Compatibility,
26
27 NonStandardModern,
31
32 NonStandardCompatibility,
37
38 NonHangul,
40}
41
42impl JamoUnicodeType {
43 pub fn evaluate(c: char) -> JamoUnicodeType {
47 match c as u32 {
48 0x1100..=0x1112 | 0x1161..=0x1175 | 0x11A8..=0x11C2 => JamoUnicodeType::Modern,
49 0x3130..=0x3163 => JamoUnicodeType::Compatibility,
50 0x1113..=0x1160 | 0x1176..=0x11A7 | 0x11C3..=0x11FF => {
51 JamoUnicodeType::NonStandardModern
52 }
53 0x3164..=0x318F => JamoUnicodeType::NonStandardCompatibility,
54 _ => JamoUnicodeType::NonHangul,
55 }
56 }
57}
58
59pub(crate) const S_BASE: u32 = 0xAC00;
61pub(crate) const L_BASE: u32 = 0x1100;
62pub(crate) const V_BASE: u32 = 0x1161;
63pub(crate) const T_BASE: u32 = 0x11A7;
64pub(crate) const V_COUNT: u32 = 21;
65pub(crate) const T_COUNT: u32 = 28;
66pub(crate) const N_COUNT: u32 = V_COUNT * T_COUNT;
67pub(crate) const S_COUNT: u32 = 11172;
68
69pub fn modernized_jamo_initial(c: char) -> char {
94 match c {
95 '\u{3131}' => '\u{1100}', '\u{3132}' => '\u{1101}', '\u{3134}' => '\u{1102}', '\u{3137}' => '\u{1103}', '\u{3138}' => '\u{1104}', '\u{3139}' => '\u{1105}', '\u{3141}' => '\u{1106}', '\u{3142}' => '\u{1107}', '\u{3143}' => '\u{1108}', '\u{3145}' => '\u{1109}', '\u{3146}' => '\u{110A}', '\u{3147}' => '\u{110B}', '\u{3148}' => '\u{110C}', '\u{3149}' => '\u{110D}', '\u{314A}' => '\u{110E}', '\u{314B}' => '\u{110F}', '\u{314C}' => '\u{1110}', '\u{314D}' => '\u{1111}', '\u{314E}' => '\u{1112}', other => other,
115 }
116}
117
118pub fn modernized_jamo_vowel(c: char) -> char {
143 match c {
144 '\u{314F}' => '\u{1161}', '\u{3150}' => '\u{1162}', '\u{3151}' => '\u{1163}', '\u{3152}' => '\u{1164}', '\u{3153}' => '\u{1165}', '\u{3154}' => '\u{1166}', '\u{3155}' => '\u{1167}', '\u{3156}' => '\u{1168}', '\u{3157}' => '\u{1169}', '\u{3158}' => '\u{116A}', '\u{3159}' => '\u{116B}', '\u{315A}' => '\u{116C}', '\u{315B}' => '\u{116D}', '\u{315C}' => '\u{116E}', '\u{315D}' => '\u{116F}', '\u{315E}' => '\u{1170}', '\u{315F}' => '\u{1171}', '\u{3160}' => '\u{1172}', '\u{3161}' => '\u{1173}', '\u{3162}' => '\u{1174}', '\u{3163}' => '\u{1175}', other => other,
166 }
167}
168
169pub fn modernized_jamo_final(c: char) -> char {
194 match c {
195 '\u{3131}' => '\u{11A8}', '\u{3132}' => '\u{11A9}', '\u{3133}' => '\u{11AA}', '\u{3134}' => '\u{11AB}', '\u{3135}' => '\u{11AC}', '\u{3136}' => '\u{11AD}', '\u{3137}' => '\u{11AE}', '\u{3139}' => '\u{11AF}', '\u{313A}' => '\u{11B0}', '\u{313B}' => '\u{11B1}', '\u{313C}' => '\u{11B2}', '\u{313D}' => '\u{11B3}', '\u{313E}' => '\u{11B4}', '\u{313F}' => '\u{11B5}', '\u{3140}' => '\u{11B6}', '\u{3141}' => '\u{11B7}', '\u{3142}' => '\u{11B8}', '\u{3144}' => '\u{11B9}', '\u{3145}' => '\u{11BA}', '\u{3146}' => '\u{11BB}', '\u{3147}' => '\u{11BC}', '\u{3148}' => '\u{11BD}', '\u{314A}' => '\u{11BE}', '\u{314B}' => '\u{11BF}', '\u{314C}' => '\u{11C0}', '\u{314D}' => '\u{11C1}', '\u{314E}' => '\u{11C2}', other => other,
223 }
224}
225
226pub fn modern_to_compatibility_jamo(c: char) -> char {
234 match c {
235 '\u{1100}' => '\u{3131}', '\u{1101}' => '\u{3132}', '\u{1102}' => '\u{3134}', '\u{1103}' => '\u{3137}', '\u{1104}' => '\u{3138}', '\u{1105}' => '\u{3139}', '\u{1106}' => '\u{3141}', '\u{1107}' => '\u{3142}', '\u{1108}' => '\u{3143}', '\u{1109}' => '\u{3145}', '\u{110A}' => '\u{3146}', '\u{110B}' => '\u{3147}', '\u{110C}' => '\u{3148}', '\u{110D}' => '\u{3149}', '\u{110E}' => '\u{314A}', '\u{110F}' => '\u{314B}', '\u{1110}' => '\u{314C}', '\u{1111}' => '\u{314D}', '\u{1112}' => '\u{314E}', '\u{1161}' => '\u{314F}', '\u{1162}' => '\u{3150}', '\u{1163}' => '\u{3151}', '\u{1164}' => '\u{3152}', '\u{1165}' => '\u{3153}', '\u{1166}' => '\u{3154}', '\u{1167}' => '\u{3155}', '\u{1168}' => '\u{3156}', '\u{1169}' => '\u{3157}', '\u{116A}' => '\u{3158}', '\u{116B}' => '\u{3159}', '\u{116C}' => '\u{315A}', '\u{116D}' => '\u{315B}', '\u{116E}' => '\u{315C}', '\u{116F}' => '\u{315D}', '\u{1170}' => '\u{315E}', '\u{1171}' => '\u{315F}', '\u{1172}' => '\u{3160}', '\u{1173}' => '\u{3161}', '\u{1174}' => '\u{3162}', '\u{1175}' => '\u{3163}', '\u{11A8}' => '\u{3131}', '\u{11A9}' => '\u{3132}', '\u{11AA}' => '\u{3133}', '\u{11AB}' => '\u{3134}', '\u{11AC}' => '\u{3135}', '\u{11AD}' => '\u{3136}', '\u{11AE}' => '\u{3137}', '\u{11AF}' => '\u{3139}', '\u{11B0}' => '\u{313A}', '\u{11B1}' => '\u{313B}', '\u{11B2}' => '\u{313C}', '\u{11B3}' => '\u{313D}', '\u{11B4}' => '\u{313E}', '\u{11B5}' => '\u{313F}', '\u{11B6}' => '\u{3140}', '\u{11B7}' => '\u{3141}', '\u{11B8}' => '\u{3142}', '\u{11B9}' => '\u{3144}', '\u{11BA}' => '\u{3145}', '\u{11BB}' => '\u{3146}', '\u{11BC}' => '\u{3147}', '\u{11BD}' => '\u{3148}', '\u{11BE}' => '\u{314A}', '\u{11BF}' => '\u{314B}', '\u{11C0}' => '\u{314C}', '\u{11C1}' => '\u{314D}', '\u{11C2}' => '\u{314E}', other => other,
309 }
310}
311
312#[derive(Debug, PartialEq, Eq, Clone)]
316pub enum Character {
317 NonHangul(char),
318 Hangul(Jamo),
319}
320
321impl Character {
322 pub fn from_char(c: char) -> Result<Self, JamoError> {
370 match JamoUnicodeType::evaluate(c) {
371 JamoUnicodeType::Modern => {
372 let cc = modern_to_compatibility_jamo(c);
373 Self::from_compatibility_jamo(cc)
374 }
375 JamoUnicodeType::Compatibility => Self::from_compatibility_jamo(c),
376 _ => Ok(Character::NonHangul(c)),
377 }
378 }
379
380 fn from_compatibility_jamo(c: char) -> Result<Self, JamoError> {
381 Ok(Self::Hangul(Jamo::from_compatibility_jamo(c)?))
382 }
383
384 pub fn jamo(&self) -> Option<&Jamo> {
387 match self {
388 Character::Hangul(jamo) => Some(jamo),
389 Character::NonHangul(_) => None,
390 }
391 }
392}
393
394#[derive(Debug, PartialEq, Eq, Clone)]
397pub enum Jamo {
398 Consonant(JamoConsonantSingular),
399 CompositeConsonant(JamoConsonantComposite),
400 Vowel(JamoVowelSingular),
401 CompositeVowel(JamoVowelComposite),
402}
403
404#[derive(Debug, PartialEq, Eq, Clone)]
406pub enum JamoConsonantSingular {
407 Giyeok,
409 Nieun,
411 Digeut,
413 Rieul,
415 Mieum,
417 Bieup,
419 Siot,
421 Ieung,
423 Jieut,
425 Chieut,
427 Kieuk,
429 Tieut,
431 Pieup,
433 Hieut,
435}
436
437impl JamoConsonantSingular {
438 pub fn char_modern(&self, position: JamoPosition) -> Option<char> {
459 match position {
460 JamoPosition::Initial => Some(self.char_modern_initial()),
461 JamoPosition::Final => Some(self.char_modern_final()),
462 _ => None,
463 }
464 }
465
466 fn char_modern_initial(&self) -> char {
467 match self {
468 JamoConsonantSingular::Giyeok => '\u{1100}',
469 JamoConsonantSingular::Nieun => '\u{1102}',
470 JamoConsonantSingular::Digeut => '\u{1103}',
471 JamoConsonantSingular::Rieul => '\u{1105}',
472 JamoConsonantSingular::Mieum => '\u{1106}',
473 JamoConsonantSingular::Bieup => '\u{1107}',
474 JamoConsonantSingular::Siot => '\u{1109}',
475 JamoConsonantSingular::Ieung => '\u{110B}',
476 JamoConsonantSingular::Jieut => '\u{110C}',
477 JamoConsonantSingular::Chieut => '\u{110E}',
478 JamoConsonantSingular::Kieuk => '\u{110F}',
479 JamoConsonantSingular::Tieut => '\u{1110}',
480 JamoConsonantSingular::Pieup => '\u{1111}',
481 JamoConsonantSingular::Hieut => '\u{1112}',
482 }
483 }
484
485 fn char_modern_final(&self) -> char {
486 match self {
487 JamoConsonantSingular::Giyeok => '\u{11A8}',
488 JamoConsonantSingular::Nieun => '\u{11AB}',
489 JamoConsonantSingular::Digeut => '\u{11AE}',
490 JamoConsonantSingular::Rieul => '\u{11AF}',
491 JamoConsonantSingular::Mieum => '\u{11B7}',
492 JamoConsonantSingular::Bieup => '\u{11B8}',
493 JamoConsonantSingular::Siot => '\u{11BA}',
494 JamoConsonantSingular::Ieung => '\u{11BC}',
495 JamoConsonantSingular::Jieut => '\u{11BD}',
496 JamoConsonantSingular::Chieut => '\u{11BE}',
497 JamoConsonantSingular::Kieuk => '\u{11BF}',
498 JamoConsonantSingular::Tieut => '\u{11C0}',
499 JamoConsonantSingular::Pieup => '\u{11C1}',
500 JamoConsonantSingular::Hieut => '\u{11C2}',
501 }
502 }
503
504 pub fn char_compatibility(&self) -> char {
514 match self {
515 JamoConsonantSingular::Giyeok => 'ㄱ',
516 JamoConsonantSingular::Nieun => 'ㄴ',
517 JamoConsonantSingular::Digeut => 'ㄷ',
518 JamoConsonantSingular::Rieul => 'ㄹ',
519 JamoConsonantSingular::Mieum => 'ㅁ',
520 JamoConsonantSingular::Bieup => 'ㅂ',
521 JamoConsonantSingular::Siot => 'ㅅ',
522 JamoConsonantSingular::Ieung => 'ㅇ',
523 JamoConsonantSingular::Jieut => 'ㅈ',
524 JamoConsonantSingular::Chieut => 'ㅊ',
525 JamoConsonantSingular::Kieuk => 'ㅋ',
526 JamoConsonantSingular::Tieut => 'ㅌ',
527 JamoConsonantSingular::Pieup => 'ㅍ',
528 JamoConsonantSingular::Hieut => 'ㅎ',
529 }
530 }
531
532 pub fn combine_for_initial(
555 &self,
556 other: &JamoConsonantSingular,
557 ) -> Option<JamoConsonantComposite> {
558 match (self, other) {
559 (JamoConsonantSingular::Giyeok, JamoConsonantSingular::Giyeok) => {
560 Some(JamoConsonantComposite::SsangGiyeok)
561 }
562 (JamoConsonantSingular::Digeut, JamoConsonantSingular::Digeut) => {
563 Some(JamoConsonantComposite::SsangDigeut)
564 }
565 (JamoConsonantSingular::Bieup, JamoConsonantSingular::Bieup) => {
566 Some(JamoConsonantComposite::SsangBieup)
567 }
568 (JamoConsonantSingular::Siot, JamoConsonantSingular::Siot) => {
569 Some(JamoConsonantComposite::SsangSiot)
570 }
571 (JamoConsonantSingular::Jieut, JamoConsonantSingular::Jieut) => {
572 Some(JamoConsonantComposite::SsangJieut)
573 }
574 _ => None,
575 }
576 }
577
578 pub fn combine_for_final(
609 &self,
610 other: &JamoConsonantSingular,
611 ) -> Option<JamoConsonantComposite> {
612 match (self, other) {
613 (JamoConsonantSingular::Giyeok, JamoConsonantSingular::Siot) => {
614 Some(JamoConsonantComposite::GiyeokSiot)
615 }
616 (JamoConsonantSingular::Nieun, JamoConsonantSingular::Jieut) => {
617 Some(JamoConsonantComposite::NieunJieut)
618 }
619 (JamoConsonantSingular::Nieun, JamoConsonantSingular::Hieut) => {
620 Some(JamoConsonantComposite::NieunHieut)
621 }
622 (JamoConsonantSingular::Rieul, JamoConsonantSingular::Giyeok) => {
623 Some(JamoConsonantComposite::RieulGiyeok)
624 }
625 (JamoConsonantSingular::Rieul, JamoConsonantSingular::Mieum) => {
626 Some(JamoConsonantComposite::RieulMieum)
627 }
628 (JamoConsonantSingular::Rieul, JamoConsonantSingular::Bieup) => {
629 Some(JamoConsonantComposite::RieulBieup)
630 }
631 (JamoConsonantSingular::Rieul, JamoConsonantSingular::Siot) => {
632 Some(JamoConsonantComposite::RieulSiot)
633 }
634 (JamoConsonantSingular::Rieul, JamoConsonantSingular::Tieut) => {
635 Some(JamoConsonantComposite::RieulTieut)
636 }
637 (JamoConsonantSingular::Rieul, JamoConsonantSingular::Pieup) => {
638 Some(JamoConsonantComposite::RieulPieup)
639 }
640 (JamoConsonantSingular::Rieul, JamoConsonantSingular::Hieut) => {
641 Some(JamoConsonantComposite::RieulHieut)
642 }
643 (JamoConsonantSingular::Giyeok, JamoConsonantSingular::Giyeok) => {
644 Some(JamoConsonantComposite::SsangGiyeok)
645 }
646 (JamoConsonantSingular::Siot, JamoConsonantSingular::Siot) => {
647 Some(JamoConsonantComposite::SsangSiot)
648 }
649 (JamoConsonantSingular::Bieup, JamoConsonantSingular::Siot) => {
650 Some(JamoConsonantComposite::BieupSiot)
651 }
652 _ => None,
653 }
654 }
655}
656
657#[derive(Debug, PartialEq, Eq, Clone)]
659pub enum JamoConsonantComposite {
660 GiyeokSiot,
662 NieunJieut,
664 NieunHieut,
666 RieulGiyeok,
668 RieulMieum,
670 RieulBieup,
672 RieulSiot,
674 RieulTieut,
676 RieulPieup,
678 RieulHieut,
680 SsangGiyeok,
682 SsangDigeut,
684 SsangBieup,
686 SsangSiot,
688 SsangJieut,
690 BieupSiot,
692}
693
694impl JamoConsonantComposite {
695 pub fn char_modern(&self, position: JamoPosition) -> Option<char> {
716 match position {
717 JamoPosition::Initial => self.char_modern_initial(),
718 JamoPosition::Final => self.char_modern_final(),
719 _ => None,
720 }
721 }
722
723 fn char_modern_initial(&self) -> Option<char> {
724 match self {
725 JamoConsonantComposite::SsangGiyeok => Some('\u{1101}'),
726 JamoConsonantComposite::SsangDigeut => Some('\u{1104}'),
727 JamoConsonantComposite::SsangBieup => Some('\u{1108}'),
728 JamoConsonantComposite::SsangSiot => Some('\u{110A}'),
729 JamoConsonantComposite::SsangJieut => Some('\u{110D}'),
730 _ => None,
731 }
732 }
733
734 fn char_modern_final(&self) -> Option<char> {
735 match self {
736 JamoConsonantComposite::GiyeokSiot => Some('\u{11AA}'),
737 JamoConsonantComposite::NieunJieut => Some('\u{11AC}'),
738 JamoConsonantComposite::NieunHieut => Some('\u{11AD}'),
739 JamoConsonantComposite::RieulGiyeok => Some('\u{11B0}'),
740 JamoConsonantComposite::RieulMieum => Some('\u{11B1}'),
741 JamoConsonantComposite::RieulBieup => Some('\u{11B2}'),
742 JamoConsonantComposite::RieulSiot => Some('\u{11B3}'),
743 JamoConsonantComposite::RieulTieut => Some('\u{11B4}'),
744 JamoConsonantComposite::RieulPieup => Some('\u{11B5}'),
745 JamoConsonantComposite::RieulHieut => Some('\u{11B6}'),
746 JamoConsonantComposite::SsangGiyeok => Some('\u{11A9}'),
747 JamoConsonantComposite::SsangSiot => Some('\u{11BB}'),
748 JamoConsonantComposite::BieupSiot => Some('\u{11B9}'),
749 _ => None,
750 }
751 }
752
753 pub fn char_compatibility(&self) -> char {
763 match self {
764 JamoConsonantComposite::GiyeokSiot => 'ㄳ',
765 JamoConsonantComposite::NieunJieut => 'ㄵ',
766 JamoConsonantComposite::NieunHieut => 'ㄶ',
767 JamoConsonantComposite::RieulGiyeok => 'ㄺ',
768 JamoConsonantComposite::RieulMieum => 'ㄻ',
769 JamoConsonantComposite::RieulBieup => 'ㄼ',
770 JamoConsonantComposite::RieulSiot => 'ㄽ',
771 JamoConsonantComposite::RieulTieut => 'ㄾ',
772 JamoConsonantComposite::RieulPieup => 'ㄿ',
773 JamoConsonantComposite::RieulHieut => 'ㅀ',
774 JamoConsonantComposite::SsangGiyeok => 'ㄲ',
775 JamoConsonantComposite::SsangDigeut => 'ㄸ',
776 JamoConsonantComposite::SsangBieup => 'ㅃ',
777 JamoConsonantComposite::SsangSiot => 'ㅆ',
778 JamoConsonantComposite::SsangJieut => 'ㅉ',
779 JamoConsonantComposite::BieupSiot => 'ㅄ',
780 }
781 }
782
783 pub fn decompose(&self) -> (Jamo, Jamo) {
799 match self {
800 JamoConsonantComposite::GiyeokSiot => (
801 Jamo::Consonant(JamoConsonantSingular::Giyeok),
802 Jamo::Consonant(JamoConsonantSingular::Siot),
803 ),
804 JamoConsonantComposite::NieunJieut => (
805 Jamo::Consonant(JamoConsonantSingular::Nieun),
806 Jamo::Consonant(JamoConsonantSingular::Jieut),
807 ),
808 JamoConsonantComposite::NieunHieut => (
809 Jamo::Consonant(JamoConsonantSingular::Nieun),
810 Jamo::Consonant(JamoConsonantSingular::Hieut),
811 ),
812 JamoConsonantComposite::RieulGiyeok => (
813 Jamo::Consonant(JamoConsonantSingular::Rieul),
814 Jamo::Consonant(JamoConsonantSingular::Giyeok),
815 ),
816 JamoConsonantComposite::RieulMieum => (
817 Jamo::Consonant(JamoConsonantSingular::Rieul),
818 Jamo::Consonant(JamoConsonantSingular::Mieum),
819 ),
820 JamoConsonantComposite::RieulBieup => (
821 Jamo::Consonant(JamoConsonantSingular::Rieul),
822 Jamo::Consonant(JamoConsonantSingular::Bieup),
823 ),
824 JamoConsonantComposite::RieulSiot => (
825 Jamo::Consonant(JamoConsonantSingular::Rieul),
826 Jamo::Consonant(JamoConsonantSingular::Siot),
827 ),
828 JamoConsonantComposite::RieulTieut => (
829 Jamo::Consonant(JamoConsonantSingular::Rieul),
830 Jamo::Consonant(JamoConsonantSingular::Tieut),
831 ),
832 JamoConsonantComposite::RieulPieup => (
833 Jamo::Consonant(JamoConsonantSingular::Rieul),
834 Jamo::Consonant(JamoConsonantSingular::Pieup),
835 ),
836 JamoConsonantComposite::RieulHieut => (
837 Jamo::Consonant(JamoConsonantSingular::Rieul),
838 Jamo::Consonant(JamoConsonantSingular::Hieut),
839 ),
840 JamoConsonantComposite::SsangGiyeok => (
841 Jamo::Consonant(JamoConsonantSingular::Giyeok),
842 Jamo::Consonant(JamoConsonantSingular::Giyeok),
843 ),
844 JamoConsonantComposite::SsangDigeut => (
845 Jamo::Consonant(JamoConsonantSingular::Digeut),
846 Jamo::Consonant(JamoConsonantSingular::Digeut),
847 ),
848 JamoConsonantComposite::SsangBieup => (
849 Jamo::Consonant(JamoConsonantSingular::Bieup),
850 Jamo::Consonant(JamoConsonantSingular::Bieup),
851 ),
852 JamoConsonantComposite::SsangSiot => (
853 Jamo::Consonant(JamoConsonantSingular::Siot),
854 Jamo::Consonant(JamoConsonantSingular::Siot),
855 ),
856 JamoConsonantComposite::SsangJieut => (
857 Jamo::Consonant(JamoConsonantSingular::Jieut),
858 Jamo::Consonant(JamoConsonantSingular::Jieut),
859 ),
860 JamoConsonantComposite::BieupSiot => (
861 Jamo::Consonant(JamoConsonantSingular::Bieup),
862 Jamo::Consonant(JamoConsonantSingular::Siot),
863 ),
864 }
865 }
866
867 pub fn is_valid_initial(&self) -> bool {
881 matches!(
882 self,
883 JamoConsonantComposite::SsangGiyeok
884 | JamoConsonantComposite::SsangDigeut
885 | JamoConsonantComposite::SsangBieup
886 | JamoConsonantComposite::SsangSiot
887 | JamoConsonantComposite::SsangJieut
888 )
889 }
890
891 pub fn is_valid_final(&self) -> bool {
905 matches!(
906 self,
907 JamoConsonantComposite::GiyeokSiot
908 | JamoConsonantComposite::NieunJieut
909 | JamoConsonantComposite::NieunHieut
910 | JamoConsonantComposite::RieulGiyeok
911 | JamoConsonantComposite::RieulMieum
912 | JamoConsonantComposite::RieulBieup
913 | JamoConsonantComposite::RieulSiot
914 | JamoConsonantComposite::RieulTieut
915 | JamoConsonantComposite::RieulPieup
916 | JamoConsonantComposite::RieulHieut
917 | JamoConsonantComposite::SsangGiyeok
918 | JamoConsonantComposite::SsangSiot
919 | JamoConsonantComposite::BieupSiot
920 )
921 }
922}
923
924#[derive(Debug, PartialEq, Eq, Clone)]
926pub enum JamoVowelSingular {
927 A,
929 Ae,
931 Ya,
933 Yae,
935 Eo,
937 E,
939 Yeo,
941 Ye,
943 O,
945 Yo,
947 U,
949 Yu,
951 Eu,
953 I,
955}
956
957impl JamoVowelSingular {
958 pub fn char_modern(&self) -> char {
970 match self {
971 JamoVowelSingular::A => '\u{1161}',
972 JamoVowelSingular::Ae => '\u{1162}',
973 JamoVowelSingular::Ya => '\u{1163}',
974 JamoVowelSingular::Yae => '\u{1164}',
975 JamoVowelSingular::Eo => '\u{1165}',
976 JamoVowelSingular::E => '\u{1166}',
977 JamoVowelSingular::Yeo => '\u{1167}',
978 JamoVowelSingular::Ye => '\u{1168}',
979 JamoVowelSingular::O => '\u{1169}',
980 JamoVowelSingular::Yo => '\u{116D}',
981 JamoVowelSingular::U => '\u{116E}',
982 JamoVowelSingular::Yu => '\u{1172}',
983 JamoVowelSingular::Eu => '\u{1173}',
984 JamoVowelSingular::I => '\u{1175}',
985 }
986 }
987
988 pub fn char_compatibility(&self) -> char {
998 match self {
999 JamoVowelSingular::A => 'ㅏ',
1000 JamoVowelSingular::Ae => 'ㅐ',
1001 JamoVowelSingular::Ya => 'ㅑ',
1002 JamoVowelSingular::Yae => 'ㅒ',
1003 JamoVowelSingular::Eo => 'ㅓ',
1004 JamoVowelSingular::E => 'ㅔ',
1005 JamoVowelSingular::Yeo => 'ㅕ',
1006 JamoVowelSingular::Ye => 'ㅖ',
1007 JamoVowelSingular::O => 'ㅗ',
1008 JamoVowelSingular::Yo => 'ㅛ',
1009 JamoVowelSingular::U => 'ㅜ',
1010 JamoVowelSingular::Yu => 'ㅠ',
1011 JamoVowelSingular::Eu => 'ㅡ',
1012 JamoVowelSingular::I => 'ㅣ',
1013 }
1014 }
1015
1016 pub fn combine(&self, other: &JamoVowelSingular) -> Option<JamoVowelComposite> {
1040 match (self, other) {
1041 (JamoVowelSingular::O, JamoVowelSingular::A) => Some(JamoVowelComposite::Wa),
1042 (JamoVowelSingular::O, JamoVowelSingular::Ae) => Some(JamoVowelComposite::Wae),
1043 (JamoVowelSingular::O, JamoVowelSingular::I) => Some(JamoVowelComposite::Oe),
1044 (JamoVowelSingular::U, JamoVowelSingular::Eo) => Some(JamoVowelComposite::Wo),
1045 (JamoVowelSingular::U, JamoVowelSingular::E) => Some(JamoVowelComposite::We),
1046 (JamoVowelSingular::U, JamoVowelSingular::I) => Some(JamoVowelComposite::Wi),
1047 (JamoVowelSingular::Eu, JamoVowelSingular::I) => Some(JamoVowelComposite::Ui),
1048 _ => None,
1049 }
1050 }
1051}
1052
1053#[derive(Debug, PartialEq, Eq, Clone)]
1055pub enum JamoVowelComposite {
1056 Wa,
1058 Wae,
1060 Oe,
1062 Wo,
1064 We,
1066 Wi,
1068 Ui,
1070}
1071
1072impl JamoVowelComposite {
1073 pub fn char_modern(&self) -> char {
1085 match self {
1086 JamoVowelComposite::Wa => '\u{116A}',
1087 JamoVowelComposite::Wae => '\u{116B}',
1088 JamoVowelComposite::Oe => '\u{116C}',
1089 JamoVowelComposite::Wo => '\u{116F}',
1090 JamoVowelComposite::We => '\u{1170}',
1091 JamoVowelComposite::Wi => '\u{1171}',
1092 JamoVowelComposite::Ui => '\u{1174}',
1093 }
1094 }
1095
1096 pub fn char_compatibility(&self) -> char {
1106 match self {
1107 JamoVowelComposite::Wa => 'ㅘ',
1108 JamoVowelComposite::Wae => 'ㅙ',
1109 JamoVowelComposite::Oe => 'ㅚ',
1110 JamoVowelComposite::Wo => 'ㅝ',
1111 JamoVowelComposite::We => 'ㅞ',
1112 JamoVowelComposite::Wi => 'ㅟ',
1113 JamoVowelComposite::Ui => 'ㅢ',
1114 }
1115 }
1116
1117 pub fn decompose(&self) -> (Jamo, Jamo) {
1133 match self {
1134 JamoVowelComposite::Wa => (
1135 Jamo::Vowel(JamoVowelSingular::O),
1136 Jamo::Vowel(JamoVowelSingular::A),
1137 ),
1138 JamoVowelComposite::Wae => (
1139 Jamo::Vowel(JamoVowelSingular::O),
1140 Jamo::Vowel(JamoVowelSingular::Ae),
1141 ),
1142 JamoVowelComposite::Oe => (
1143 Jamo::Vowel(JamoVowelSingular::O),
1144 Jamo::Vowel(JamoVowelSingular::I),
1145 ),
1146 JamoVowelComposite::Wo => (
1147 Jamo::Vowel(JamoVowelSingular::U),
1148 Jamo::Vowel(JamoVowelSingular::Eo),
1149 ),
1150 JamoVowelComposite::We => (
1151 Jamo::Vowel(JamoVowelSingular::U),
1152 Jamo::Vowel(JamoVowelSingular::E),
1153 ),
1154 JamoVowelComposite::Wi => (
1155 Jamo::Vowel(JamoVowelSingular::U),
1156 Jamo::Vowel(JamoVowelSingular::I),
1157 ),
1158 JamoVowelComposite::Ui => (
1159 Jamo::Vowel(JamoVowelSingular::Eu),
1160 Jamo::Vowel(JamoVowelSingular::I),
1161 ),
1162 }
1163 }
1164}
1165
1166#[derive(Debug, PartialEq, Eq, Clone)]
1169pub enum JamoPosition {
1170 Initial,
1171 Vowel,
1172 Final,
1173}
1174
1175impl Jamo {
1176 pub fn char_compatibility(&self) -> char {
1186 match self {
1187 Jamo::Consonant(c) => c.char_compatibility(),
1188 Jamo::CompositeConsonant(c) => c.char_compatibility(),
1189 Jamo::Vowel(c) => c.char_compatibility(),
1190 Jamo::CompositeVowel(c) => c.char_compatibility(),
1191 }
1192 }
1193
1194 pub fn char_modern(&self, position: JamoPosition) -> Option<char> {
1208 match self {
1209 Jamo::Consonant(c) => c.char_modern(position),
1210 Jamo::CompositeConsonant(c) => match position {
1211 JamoPosition::Initial => c.char_modern_initial(),
1212 JamoPosition::Final => c.char_modern_final(),
1213 JamoPosition::Vowel => None,
1214 },
1215 Jamo::Vowel(c) => match position {
1216 JamoPosition::Vowel => Some(c.char_modern()),
1217 _ => None,
1218 },
1219 Jamo::CompositeVowel(c) => match position {
1220 JamoPosition::Vowel => Some(c.char_modern()),
1221 _ => None,
1222 },
1223 }
1224 }
1225
1226 pub fn from_modern_jamo(c: char) -> Result<Self, JamoError> {
1236 let cc = modern_to_compatibility_jamo(c);
1237 Self::from_compatibility_jamo(cc)
1238 }
1239
1240 pub fn from_compatibility_jamo(c: char) -> Result<Self, JamoError> {
1249 match c {
1250 'ㄱ' => Ok(Jamo::Consonant(JamoConsonantSingular::Giyeok)),
1252 'ㄴ' => Ok(Jamo::Consonant(JamoConsonantSingular::Nieun)),
1253 'ㄷ' => Ok(Jamo::Consonant(JamoConsonantSingular::Digeut)),
1254 'ㄹ' => Ok(Jamo::Consonant(JamoConsonantSingular::Rieul)),
1255 'ㅁ' => Ok(Jamo::Consonant(JamoConsonantSingular::Mieum)),
1256 'ㅂ' => Ok(Jamo::Consonant(JamoConsonantSingular::Bieup)),
1257 'ㅅ' => Ok(Jamo::Consonant(JamoConsonantSingular::Siot)),
1258 'ㅇ' => Ok(Jamo::Consonant(JamoConsonantSingular::Ieung)),
1259 'ㅈ' => Ok(Jamo::Consonant(JamoConsonantSingular::Jieut)),
1260 'ㅊ' => Ok(Jamo::Consonant(JamoConsonantSingular::Chieut)),
1261 'ㅋ' => Ok(Jamo::Consonant(JamoConsonantSingular::Kieuk)),
1262 'ㅌ' => Ok(Jamo::Consonant(JamoConsonantSingular::Tieut)),
1263 'ㅍ' => Ok(Jamo::Consonant(JamoConsonantSingular::Pieup)),
1264 'ㅎ' => Ok(Jamo::Consonant(JamoConsonantSingular::Hieut)),
1265
1266 'ㄳ' => Ok(Jamo::CompositeConsonant(JamoConsonantComposite::GiyeokSiot)),
1268 'ㄵ' => Ok(Jamo::CompositeConsonant(JamoConsonantComposite::NieunJieut)),
1269 'ㄶ' => Ok(Jamo::CompositeConsonant(JamoConsonantComposite::NieunHieut)),
1270 'ㄺ' => Ok(Jamo::CompositeConsonant(
1271 JamoConsonantComposite::RieulGiyeok,
1272 )),
1273 'ㄻ' => Ok(Jamo::CompositeConsonant(JamoConsonantComposite::RieulMieum)),
1274 'ㄼ' => Ok(Jamo::CompositeConsonant(JamoConsonantComposite::RieulBieup)),
1275 'ㄽ' => Ok(Jamo::CompositeConsonant(JamoConsonantComposite::RieulSiot)),
1276 'ㄾ' => Ok(Jamo::CompositeConsonant(JamoConsonantComposite::RieulTieut)),
1277 'ㄿ' => Ok(Jamo::CompositeConsonant(JamoConsonantComposite::RieulPieup)),
1278 'ㅀ' => Ok(Jamo::CompositeConsonant(JamoConsonantComposite::RieulHieut)),
1279 'ㄲ' => Ok(Jamo::CompositeConsonant(
1280 JamoConsonantComposite::SsangGiyeok,
1281 )),
1282 'ㄸ' => Ok(Jamo::CompositeConsonant(
1283 JamoConsonantComposite::SsangDigeut,
1284 )),
1285 'ㅃ' => Ok(Jamo::CompositeConsonant(JamoConsonantComposite::SsangBieup)),
1286 'ㅆ' => Ok(Jamo::CompositeConsonant(JamoConsonantComposite::SsangSiot)),
1287 'ㅉ' => Ok(Jamo::CompositeConsonant(JamoConsonantComposite::SsangJieut)),
1288 'ㅄ' => Ok(Jamo::CompositeConsonant(JamoConsonantComposite::BieupSiot)),
1289
1290 'ㅏ' => Ok(Jamo::Vowel(JamoVowelSingular::A)),
1292 'ㅐ' => Ok(Jamo::Vowel(JamoVowelSingular::Ae)),
1293 'ㅑ' => Ok(Jamo::Vowel(JamoVowelSingular::Ya)),
1294 'ㅒ' => Ok(Jamo::Vowel(JamoVowelSingular::Yae)),
1295 'ㅓ' => Ok(Jamo::Vowel(JamoVowelSingular::Eo)),
1296 'ㅔ' => Ok(Jamo::Vowel(JamoVowelSingular::E)),
1297 'ㅕ' => Ok(Jamo::Vowel(JamoVowelSingular::Yeo)),
1298 'ㅖ' => Ok(Jamo::Vowel(JamoVowelSingular::Ye)),
1299 'ㅗ' => Ok(Jamo::Vowel(JamoVowelSingular::O)),
1300 'ㅛ' => Ok(Jamo::Vowel(JamoVowelSingular::Yo)),
1301 'ㅜ' => Ok(Jamo::Vowel(JamoVowelSingular::U)),
1302 'ㅠ' => Ok(Jamo::Vowel(JamoVowelSingular::Yu)),
1303 'ㅡ' => Ok(Jamo::Vowel(JamoVowelSingular::Eu)),
1304 'ㅣ' => Ok(Jamo::Vowel(JamoVowelSingular::I)),
1305
1306 'ㅘ' => Ok(Jamo::CompositeVowel(JamoVowelComposite::Wa)),
1308 'ㅙ' => Ok(Jamo::CompositeVowel(JamoVowelComposite::Wae)),
1309 'ㅚ' => Ok(Jamo::CompositeVowel(JamoVowelComposite::Oe)),
1310 'ㅝ' => Ok(Jamo::CompositeVowel(JamoVowelComposite::Wo)),
1311 'ㅞ' => Ok(Jamo::CompositeVowel(JamoVowelComposite::We)),
1312 'ㅟ' => Ok(Jamo::CompositeVowel(JamoVowelComposite::Wi)),
1313 'ㅢ' => Ok(Jamo::CompositeVowel(JamoVowelComposite::Ui)),
1314
1315 _ => Err(JamoError::FromCharError(c)),
1316 }
1317 }
1318}
1319
1320#[cfg(test)]
1321mod tests {
1322 use super::*;
1323
1324 #[test]
1325 fn character_from_char_identifies_valid_consonants_compatibility() {
1326 let tests = vec![
1327 ('ㄱ', Jamo::Consonant(JamoConsonantSingular::Giyeok)),
1328 ('ㄴ', Jamo::Consonant(JamoConsonantSingular::Nieun)),
1329 ('ㄷ', Jamo::Consonant(JamoConsonantSingular::Digeut)),
1330 ('ㄹ', Jamo::Consonant(JamoConsonantSingular::Rieul)),
1331 ('ㅁ', Jamo::Consonant(JamoConsonantSingular::Mieum)),
1332 ('ㅂ', Jamo::Consonant(JamoConsonantSingular::Bieup)),
1333 ('ㅅ', Jamo::Consonant(JamoConsonantSingular::Siot)),
1334 ('ㅇ', Jamo::Consonant(JamoConsonantSingular::Ieung)),
1335 ('ㅈ', Jamo::Consonant(JamoConsonantSingular::Jieut)),
1336 ('ㅊ', Jamo::Consonant(JamoConsonantSingular::Chieut)),
1337 ('ㅋ', Jamo::Consonant(JamoConsonantSingular::Kieuk)),
1338 ('ㅌ', Jamo::Consonant(JamoConsonantSingular::Tieut)),
1339 ('ㅍ', Jamo::Consonant(JamoConsonantSingular::Pieup)),
1340 ('ㅎ', Jamo::Consonant(JamoConsonantSingular::Hieut)),
1341 ];
1342 for (c, expected_jamo) in tests {
1343 let result = Character::from_char(c);
1344 assert_eq!(
1345 result,
1346 Ok(Character::Hangul(expected_jamo)),
1347 "Failed on consonant: {}; got result: {:?}",
1348 c,
1349 result
1350 )
1351 }
1352 }
1353
1354 #[test]
1355 fn character_from_char_identifies_valid_consonants_modern() {
1356 let tests = vec![
1357 ('ᄀ', Jamo::Consonant(JamoConsonantSingular::Giyeok)),
1358 ('ᄂ', Jamo::Consonant(JamoConsonantSingular::Nieun)),
1359 ('ᄃ', Jamo::Consonant(JamoConsonantSingular::Digeut)),
1360 ('ᄅ', Jamo::Consonant(JamoConsonantSingular::Rieul)),
1361 ('ᄆ', Jamo::Consonant(JamoConsonantSingular::Mieum)),
1362 ('ᄇ', Jamo::Consonant(JamoConsonantSingular::Bieup)),
1363 ('ᄉ', Jamo::Consonant(JamoConsonantSingular::Siot)),
1364 ('ᄋ', Jamo::Consonant(JamoConsonantSingular::Ieung)),
1365 ('ᄌ', Jamo::Consonant(JamoConsonantSingular::Jieut)),
1366 ('ᄎ', Jamo::Consonant(JamoConsonantSingular::Chieut)),
1367 ('ᄏ', Jamo::Consonant(JamoConsonantSingular::Kieuk)),
1368 ('ᄐ', Jamo::Consonant(JamoConsonantSingular::Tieut)),
1369 ('ᄑ', Jamo::Consonant(JamoConsonantSingular::Pieup)),
1370 ('ᄒ', Jamo::Consonant(JamoConsonantSingular::Hieut)),
1371 ];
1372 for (c, expected_jamo) in tests {
1373 let result = Character::from_char(c);
1374 assert_eq!(
1375 result,
1376 Ok(Character::Hangul(expected_jamo)),
1377 "Failed on consonant: {}; got result: {:?}",
1378 c,
1379 result
1380 )
1381 }
1382 }
1383
1384 #[test]
1385 fn character_from_char_identifies_valid_vowels_compatibility() {
1386 let tests = vec![
1387 ('ㅏ', Jamo::Vowel(JamoVowelSingular::A)),
1388 ('ㅐ', Jamo::Vowel(JamoVowelSingular::Ae)),
1389 ('ㅑ', Jamo::Vowel(JamoVowelSingular::Ya)),
1390 ('ㅒ', Jamo::Vowel(JamoVowelSingular::Yae)),
1391 ('ㅓ', Jamo::Vowel(JamoVowelSingular::Eo)),
1392 ('ㅔ', Jamo::Vowel(JamoVowelSingular::E)),
1393 ('ㅕ', Jamo::Vowel(JamoVowelSingular::Yeo)),
1394 ('ㅖ', Jamo::Vowel(JamoVowelSingular::Ye)),
1395 ('ㅗ', Jamo::Vowel(JamoVowelSingular::O)),
1396 ('ㅛ', Jamo::Vowel(JamoVowelSingular::Yo)),
1397 ('ㅜ', Jamo::Vowel(JamoVowelSingular::U)),
1398 ('ㅠ', Jamo::Vowel(JamoVowelSingular::Yu)),
1399 ('ㅡ', Jamo::Vowel(JamoVowelSingular::Eu)),
1400 ('ㅣ', Jamo::Vowel(JamoVowelSingular::I)),
1401 ];
1402 for (c, expected_jamo) in tests {
1403 let result = Character::from_char(c);
1404 assert_eq!(
1405 result,
1406 Ok(Character::Hangul(expected_jamo)),
1407 "Failed on vowel: {}; got result: {:?}",
1408 c,
1409 result
1410 )
1411 }
1412 }
1413
1414 #[test]
1415 fn character_from_char_identifies_valid_vowels_modern() {
1416 let tests = vec![
1417 ('ᅡ', Jamo::Vowel(JamoVowelSingular::A)),
1418 ('ᅢ', Jamo::Vowel(JamoVowelSingular::Ae)),
1419 ('ᅣ', Jamo::Vowel(JamoVowelSingular::Ya)),
1420 ('ᅤ', Jamo::Vowel(JamoVowelSingular::Yae)),
1421 ('ᅥ', Jamo::Vowel(JamoVowelSingular::Eo)),
1422 ('ᅦ', Jamo::Vowel(JamoVowelSingular::E)),
1423 ('ᅧ', Jamo::Vowel(JamoVowelSingular::Yeo)),
1424 ('ᅨ', Jamo::Vowel(JamoVowelSingular::Ye)),
1425 ('ᅩ', Jamo::Vowel(JamoVowelSingular::O)),
1426 ('ᅭ', Jamo::Vowel(JamoVowelSingular::Yo)),
1427 ('ᅮ', Jamo::Vowel(JamoVowelSingular::U)),
1428 ('ᅲ', Jamo::Vowel(JamoVowelSingular::Yu)),
1429 ('ᅳ', Jamo::Vowel(JamoVowelSingular::Eu)),
1430 ('ᅵ', Jamo::Vowel(JamoVowelSingular::I)),
1431 ];
1432 for (c, expected_jamo) in tests {
1433 let result = Character::from_char(c);
1434 assert_eq!(
1435 result,
1436 Ok(Character::Hangul(expected_jamo)),
1437 "Failed on vowel: {}; got result: {:?}",
1438 c,
1439 result
1440 )
1441 }
1442 }
1443
1444 #[test]
1445 fn character_from_char_identifies_double_initials_compatibility() {
1446 let tests = vec![
1447 (
1448 'ㄲ',
1449 Jamo::CompositeConsonant(JamoConsonantComposite::SsangGiyeok),
1450 ),
1451 (
1452 'ㄸ',
1453 Jamo::CompositeConsonant(JamoConsonantComposite::SsangDigeut),
1454 ),
1455 (
1456 'ㅃ',
1457 Jamo::CompositeConsonant(JamoConsonantComposite::SsangBieup),
1458 ),
1459 (
1460 'ㅆ',
1461 Jamo::CompositeConsonant(JamoConsonantComposite::SsangSiot),
1462 ),
1463 (
1464 'ㅉ',
1465 Jamo::CompositeConsonant(JamoConsonantComposite::SsangJieut),
1466 ),
1467 ];
1468 for (c, expected_jamo) in tests {
1469 let result = Character::from_char(c);
1470 assert_eq!(
1471 result,
1472 Ok(Character::Hangul(expected_jamo)),
1473 "Failed on double initial: {}; got result: {:?}",
1474 c,
1475 result
1476 )
1477 }
1478 }
1479
1480 #[test]
1481 fn character_from_char_identifies_double_initials_modern() {
1482 let tests = vec![
1483 (
1484 'ᄁ',
1485 Jamo::CompositeConsonant(JamoConsonantComposite::SsangGiyeok),
1486 ),
1487 (
1488 'ᄄ',
1489 Jamo::CompositeConsonant(JamoConsonantComposite::SsangDigeut),
1490 ),
1491 (
1492 'ᄈ',
1493 Jamo::CompositeConsonant(JamoConsonantComposite::SsangBieup),
1494 ),
1495 (
1496 'ᄊ',
1497 Jamo::CompositeConsonant(JamoConsonantComposite::SsangSiot),
1498 ),
1499 (
1500 'ᄍ',
1501 Jamo::CompositeConsonant(JamoConsonantComposite::SsangJieut),
1502 ),
1503 ];
1504 for (c, expected_jamo) in tests {
1505 let result = Character::from_char(c);
1506 assert_eq!(
1507 result,
1508 Ok(Character::Hangul(expected_jamo)),
1509 "Failed on double initial: {}; got result: {:?}",
1510 c,
1511 result
1512 )
1513 }
1514 }
1515
1516 #[test]
1517 fn character_from_char_identifies_composite_vowels_compatibility() {
1518 let tests = vec![
1519 ('ㅘ', Jamo::CompositeVowel(JamoVowelComposite::Wa)),
1520 ('ㅙ', Jamo::CompositeVowel(JamoVowelComposite::Wae)),
1521 ('ㅚ', Jamo::CompositeVowel(JamoVowelComposite::Oe)),
1522 ('ㅝ', Jamo::CompositeVowel(JamoVowelComposite::Wo)),
1523 ('ㅞ', Jamo::CompositeVowel(JamoVowelComposite::We)),
1524 ('ㅟ', Jamo::CompositeVowel(JamoVowelComposite::Wi)),
1525 ('ㅢ', Jamo::CompositeVowel(JamoVowelComposite::Ui)),
1526 ];
1527 for (c, expected_jamo) in tests {
1528 let result = Character::from_char(c);
1529 assert_eq!(
1530 result,
1531 Ok(Character::Hangul(expected_jamo)),
1532 "Failed on composite vowel: {}; got result: {:?}",
1533 c,
1534 result
1535 )
1536 }
1537 }
1538
1539 #[test]
1540 fn character_from_char_identifies_composite_vowels_modern() {
1541 let tests = vec![
1542 ('ᅪ', Jamo::CompositeVowel(JamoVowelComposite::Wa)),
1543 ('ᅫ', Jamo::CompositeVowel(JamoVowelComposite::Wae)),
1544 ('ᅬ', Jamo::CompositeVowel(JamoVowelComposite::Oe)),
1545 ('ᅯ', Jamo::CompositeVowel(JamoVowelComposite::Wo)),
1546 ('ᅰ', Jamo::CompositeVowel(JamoVowelComposite::We)),
1547 ('ᅱ', Jamo::CompositeVowel(JamoVowelComposite::Wi)),
1548 ('ᅴ', Jamo::CompositeVowel(JamoVowelComposite::Ui)),
1549 ];
1550 for (c, expected_jamo) in tests {
1551 let result = Character::from_char(c);
1552 assert_eq!(
1553 result,
1554 Ok(Character::Hangul(expected_jamo)),
1555 "Failed on composite vowel: {}; got result: {:?}",
1556 c,
1557 result
1558 )
1559 }
1560 }
1561
1562 #[test]
1563 fn character_from_char_identifies_composite_finals_compatibility() {
1564 let tests = vec![
1565 (
1566 'ㄳ',
1567 Jamo::CompositeConsonant(JamoConsonantComposite::GiyeokSiot),
1568 ),
1569 (
1570 'ㄵ',
1571 Jamo::CompositeConsonant(JamoConsonantComposite::NieunJieut),
1572 ),
1573 (
1574 'ㄶ',
1575 Jamo::CompositeConsonant(JamoConsonantComposite::NieunHieut),
1576 ),
1577 (
1578 'ㄺ',
1579 Jamo::CompositeConsonant(JamoConsonantComposite::RieulGiyeok),
1580 ),
1581 (
1582 'ㄻ',
1583 Jamo::CompositeConsonant(JamoConsonantComposite::RieulMieum),
1584 ),
1585 (
1586 'ㄼ',
1587 Jamo::CompositeConsonant(JamoConsonantComposite::RieulBieup),
1588 ),
1589 (
1590 'ㄽ',
1591 Jamo::CompositeConsonant(JamoConsonantComposite::RieulSiot),
1592 ),
1593 (
1594 'ㄾ',
1595 Jamo::CompositeConsonant(JamoConsonantComposite::RieulTieut),
1596 ),
1597 (
1598 'ㄿ',
1599 Jamo::CompositeConsonant(JamoConsonantComposite::RieulPieup),
1600 ),
1601 (
1602 'ㅀ',
1603 Jamo::CompositeConsonant(JamoConsonantComposite::RieulHieut),
1604 ),
1605 ];
1606 for (c, expected_jamo) in tests {
1607 let result = Character::from_char(c);
1608 assert_eq!(
1609 result,
1610 Ok(Character::Hangul(expected_jamo)),
1611 "Failed on composite final: {}; got result: {:?}",
1612 c,
1613 result
1614 )
1615 }
1616 }
1617
1618 #[test]
1619 fn character_from_char_identifies_composite_finals_modern() {
1620 let tests = vec![
1621 (
1622 'ᆪ',
1623 Jamo::CompositeConsonant(JamoConsonantComposite::GiyeokSiot),
1624 ),
1625 (
1626 'ᆬ',
1627 Jamo::CompositeConsonant(JamoConsonantComposite::NieunJieut),
1628 ),
1629 (
1630 'ᆭ',
1631 Jamo::CompositeConsonant(JamoConsonantComposite::NieunHieut),
1632 ),
1633 (
1634 'ᆰ',
1635 Jamo::CompositeConsonant(JamoConsonantComposite::RieulGiyeok),
1636 ),
1637 (
1638 'ᆱ',
1639 Jamo::CompositeConsonant(JamoConsonantComposite::RieulMieum),
1640 ),
1641 (
1642 'ᆲ',
1643 Jamo::CompositeConsonant(JamoConsonantComposite::RieulBieup),
1644 ),
1645 (
1646 'ᆳ',
1647 Jamo::CompositeConsonant(JamoConsonantComposite::RieulSiot),
1648 ),
1649 (
1650 'ᆴ',
1651 Jamo::CompositeConsonant(JamoConsonantComposite::RieulTieut),
1652 ),
1653 (
1654 'ᆵ',
1655 Jamo::CompositeConsonant(JamoConsonantComposite::RieulPieup),
1656 ),
1657 (
1658 'ᆶ',
1659 Jamo::CompositeConsonant(JamoConsonantComposite::RieulHieut),
1660 ),
1661 ];
1662 for (c, expected_jamo) in tests {
1663 let result = Character::from_char(c);
1664 assert_eq!(
1665 result,
1666 Ok(Character::Hangul(expected_jamo)),
1667 "Failed on composite final: {}; got result: {:?}",
1668 c,
1669 result
1670 )
1671 }
1672 }
1673
1674 #[test]
1675 fn character_from_char_identifies_non_hangul() {
1676 let non_hangul_chars = "ABCxyz123!@# ";
1677 for c in non_hangul_chars.chars() {
1678 let result = Character::from_char(c);
1679 assert!(
1680 result == Ok(Character::NonHangul(c)),
1681 "Failed on non-Hangul char: {}; got result: {:?}",
1682 c,
1683 result
1684 );
1685 }
1686 }
1687}