liblevenshtein 0.9.1

Levenshtein/Universal Automata for approximate string matching using various dictionary backends
Documentation
//! Thai script vowel classifier.
//!
//! Supports Thai (ภาษาไทย) language.
//! Thai is a Brahmic-derived abugida with unique features:
//! - No spaces between words
//! - 5 tones (not fully marked in writing)
//! - Vowels can appear before, after, above, below, or around consonants

use super::VowelClassifier;

/// Vowel classifier for Thai script.
///
/// Thai has complex vowel positioning:
/// - **Leading vowels**: Appear before the consonant (เ, แ, โ, ใ, ไ)
/// - **Following vowels**: Appear after the consonant (ะ, า, อ)
/// - **Upper vowels**: Appear above the consonant (◌ิ, ◌ี, ◌ึ, ◌ื, ◌ั)
/// - **Lower vowels**: Appear below the consonant (◌ุ, ◌ู)
/// - **Surrounding vowels**: Consonant appears inside (เ◌ะ, แ◌ะ, โ◌ะ, etc.)
///
/// The inherent vowel is /o/ or /a/ depending on syllable structure.
#[derive(Debug, Clone, Copy, Default)]
pub struct ThaiClassifier;

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

/// Thai vowels (all forms).
static THAI_VOWELS: &[char] = &[
    // Leading vowels
    '', // e (sara e)
    '', // ae (sara ae)
    '', // o (sara o)
    '', // ai (sara ai mai muan)
    '', // ai (sara ai mai malai)
    // Following vowels
    '', // a short (sara a)
    '', // aa long (sara aa)
    '', // am (sara am)
    // Upper vowels (combining)
    '',         // i (sara i)
    '',         // ii (sara ii)
    '',         // ue (sara ue)
    '',         // uee (sara uee)
    '',         // mai han akat (short vowel marker)
    '\u{0E47}', // mai tai khu (shortens vowel)
    // Lower vowels (combining)
    '', // u (sara u)
    '', // uu (sara uu)
];

impl VowelClassifier for ThaiClassifier {
    fn is_vowel(&self, c: char) -> bool {
        let code = c as u32;
        match code {
            // Leading vowels
            0x0E40..=0x0E44 => true, // เ through ไ

            // Following vowels
            0x0E30..=0x0E31 => true, // ะ, ◌ั
            0x0E32..=0x0E33 => true, // า, ำ

            // Upper vowels (above consonant)
            0x0E34..=0x0E39 => true, // ◌ิ through ◌ู

            // Mai tai khu (shortening mark, functions as vowel modifier)
            0x0E47 => true,

            _ => false,
        }
    }

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

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

    fn is_consonant(&self, c: char) -> bool {
        let code = c as u32;
        match code {
            // Thai consonants (44 characters)
            0x0E01..=0x0E2E => true, // ก through ฮ

            // Obsolete consonants (rare)
            0x0E2F => false, // ฯ (paiyannoi - abbreviation)

            // Tone marks are neither vowels nor consonants
            0x0E48..=0x0E4B => false,

            // Thai currency and other symbols
            0x0E3F => false,

            _ => false,
        }
    }
}

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

    #[test]
    fn test_leading_vowels() {
        let c = ThaiClassifier::new();
        assert!(c.is_vowel('')); // sara e
        assert!(c.is_vowel('')); // sara ae
        assert!(c.is_vowel('')); // sara o
        assert!(c.is_vowel('')); // sara ai mai muan
        assert!(c.is_vowel('')); // sara ai mai malai
    }

    #[test]
    fn test_following_vowels() {
        let c = ThaiClassifier::new();
        assert!(c.is_vowel('')); // sara a (short)
        assert!(c.is_vowel('')); // sara aa (long)
        assert!(c.is_vowel('')); // sara am
    }

    #[test]
    fn test_upper_vowels() {
        let c = ThaiClassifier::new();
        assert!(c.is_vowel('')); // sara i
        assert!(c.is_vowel('')); // sara ii
        assert!(c.is_vowel('')); // sara ue
        assert!(c.is_vowel('')); // sara uee
        assert!(c.is_vowel('')); // mai han akat
    }

    #[test]
    fn test_lower_vowels() {
        let c = ThaiClassifier::new();
        assert!(c.is_vowel('')); // sara u
        assert!(c.is_vowel('')); // sara uu
    }

    #[test]
    fn test_consonants() {
        let c = ThaiClassifier::new();
        assert!(!c.is_vowel('')); // ko kai
        assert!(!c.is_vowel('')); // kho khai
        assert!(!c.is_vowel('')); // kho khwai
        assert!(!c.is_vowel('')); // ngo ngu
        assert!(!c.is_vowel('')); // cho chan
        assert!(!c.is_vowel('')); // tho thahan
        assert!(!c.is_vowel('')); // no nu
        assert!(!c.is_vowel('')); // mo ma
        assert!(!c.is_vowel('')); // yo yak
        assert!(!c.is_vowel('')); // ro ruea
        assert!(!c.is_vowel('')); // lo ling
        assert!(!c.is_vowel('')); // wo waen
        assert!(!c.is_vowel('')); // so suea
        assert!(!c.is_vowel('')); // ho hip
        assert!(!c.is_vowel('')); // o ang
        assert!(!c.is_vowel('')); // ho nokhuk
    }

    #[test]
    fn test_is_consonant() {
        let c = ThaiClassifier::new();
        assert!(c.is_consonant('')); // ko kai
        assert!(c.is_consonant('')); // kho khai
        assert!(c.is_consonant('')); // ngo ngu
        assert!(c.is_consonant('')); // mo ma
        assert!(c.is_consonant('')); // no nu
        assert!(c.is_consonant('')); // ho nokhuk
    }

    #[test]
    fn test_tone_marks_not_vowel() {
        let c = ThaiClassifier::new();
        assert!(!c.is_vowel('')); // mai ek
        assert!(!c.is_vowel('')); // mai tho
        assert!(!c.is_vowel('')); // mai tri
        assert!(!c.is_vowel('')); // mai chattawa
        assert!(!c.is_consonant(''));
        assert!(!c.is_consonant(''));
    }

    #[test]
    fn test_thanthakhat_not_vowel() {
        let c = ThaiClassifier::new();
        // Thanthakhat (cancellation mark) - silences consonant
        assert!(!c.is_vowel(''));
        assert!(!c.is_consonant(''));
    }
}