liblevenshtein 0.9.1

Levenshtein/Universal Automata for approximate string matching using various dictionary backends
Documentation
//! Korean Hangul vowel classifier.
//!
//! Supports Korean jamo (vowel) components in Hangul syllable blocks.

use super::VowelClassifier;

/// Vowel classifier for Korean Hangul script.
///
/// Hangul is an alphabetic script where syllable blocks are composed of:
/// - Initial consonant (choseong)
/// - Vowel (jungseong) - the nucleus
/// - Optional final consonant (jongseong)
///
/// This classifier identifies:
/// - Hangul Jamo vowels (U+1161-U+1175, U+11A8-U+11C2)
/// - Hangul Compatibility Jamo vowels (U+314F-U+3163)
/// - Vowel components within syllable blocks (U+AC00-U+D7A3)
#[derive(Debug, Clone, Copy, Default)]
pub struct HangulClassifier;

impl HangulClassifier {
    /// Create a new Hangul classifier.
    pub fn new() -> Self {
        Self
    }
}

/// Static list of Hangul jamo vowels.
static HANGUL_VOWELS: &[char] = &[
    // Basic vowels (monophthongs)
    '', // a
    '', // eo
    '', // o
    '', // u
    '', // eu
    '', // i
    '', // ae
    '', // e
    // Y-vowels (iotized)
    '', // ya
    '', // yeo
    '', // yo
    '', // yu
    '', // yae
    '', // ye
    // Compound vowels (diphthongs)
    '', // wa
    '', // wae
    '', // oe
    '', // wo
    '', // we
    '', // wi
    '', // ui
];

impl VowelClassifier for HangulClassifier {
    fn is_vowel(&self, c: char) -> bool {
        let code = c as u32;
        match code {
            // Hangul Compatibility Jamo vowels (ㅏ-ㅣ)
            0x314F..=0x3163 => true,

            // Hangul Jamo vowels (medial vowels)
            0x1161..=0x1175 => true,

            // Hangul Jamo Extended-A (medial vowels)
            0xA960..=0xA97C => false, // These are initial consonants

            // Hangul Jamo Extended-B (some vowels)
            0xD7B0..=0xD7C6 => true,

            // For complete syllable blocks, we can't easily tell
            // if it "contains" a vowel - the block itself is a syllable
            // with an inherent vowel. Return false for syllable blocks.
            0xAC00..=0xD7A3 => false,

            _ => false,
        }
    }

    fn script_name(&self) -> &'static str {
        "Hangul"
    }

    fn vowels(&self) -> &[char] {
        HANGUL_VOWELS
    }

    fn is_consonant(&self, c: char) -> bool {
        let code = c as u32;
        match code {
            // Hangul Compatibility Jamo consonants (ㄱ-ㅎ)
            0x3131..=0x314E => true,

            // Hangul Jamo initial consonants
            0x1100..=0x1112 => true,

            // Hangul Jamo final consonants
            0x11A8..=0x11C2 => true,

            // Syllable blocks are neither pure vowel nor consonant
            0xAC00..=0xD7A3 => false,

            _ => false,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_basic_vowels() {
        let c = HangulClassifier::new();
        assert!(c.is_vowel('')); // a
        assert!(c.is_vowel('')); // eo
        assert!(c.is_vowel('')); // o
        assert!(c.is_vowel('')); // u
        assert!(c.is_vowel('')); // eu
        assert!(c.is_vowel('')); // i
    }

    #[test]
    fn test_iotized_vowels() {
        let c = HangulClassifier::new();
        assert!(c.is_vowel('')); // ya
        assert!(c.is_vowel('')); // yeo
        assert!(c.is_vowel('')); // yo
        assert!(c.is_vowel('')); // yu
    }

    #[test]
    fn test_compound_vowels() {
        let c = HangulClassifier::new();
        assert!(c.is_vowel('')); // wa
        assert!(c.is_vowel('')); // wae
        assert!(c.is_vowel('')); // oe
        assert!(c.is_vowel('')); // wo
        assert!(c.is_vowel('')); // we
        assert!(c.is_vowel('')); // wi
        assert!(c.is_vowel('')); // ui
    }

    #[test]
    fn test_consonants() {
        let c = HangulClassifier::new();
        assert!(!c.is_vowel('')); // g/k
        assert!(!c.is_vowel('')); // n
        assert!(!c.is_vowel('')); // d/t
        assert!(!c.is_vowel('')); // r/l
        assert!(!c.is_vowel('')); // m
        assert!(!c.is_vowel('')); // b/p
        assert!(!c.is_vowel('')); // s
        assert!(!c.is_vowel('')); // h
    }

    #[test]
    fn test_double_consonants() {
        let c = HangulClassifier::new();
        assert!(!c.is_vowel('')); // kk
        assert!(!c.is_vowel('')); // tt
        assert!(!c.is_vowel('')); // pp
        assert!(!c.is_vowel('')); // ss
        assert!(!c.is_vowel('')); // jj
    }

    #[test]
    fn test_syllable_blocks() {
        let c = HangulClassifier::new();
        // Complete syllable blocks are not classified as vowels
        // because they contain both consonant and vowel components
        assert!(!c.is_vowel('')); // ga
        assert!(!c.is_vowel('')); // na
        assert!(!c.is_vowel('')); // han
        assert!(!c.is_vowel('')); // geul
    }
}