yuru-ko 0.1.10

Korean Hangul matching support for Yuru
Documentation
//! Korean Hangul matching backend for Yuru.
//!
//! The backend adds deterministic Hangul romanization, choseong initials, and
//! Korean 2-set keyboard keys while preserving source spans for highlighting.

/// Hangul decomposition and generated key helpers.
pub mod hangul;

use yuru_core::{
    base_query_variants, KeyBudget, LangMode, LanguageBackend, QueryBudget, QueryVariant, SearchKey,
};

#[derive(Clone, Debug)]
/// Korean language backend for romanization, initials, and keyboard keys.
pub struct KoreanBackend {
    romanization: bool,
    initials: bool,
    keyboard: bool,
}

impl KoreanBackend {
    /// Creates a Korean backend with selected generated key families.
    pub fn new(romanization: bool, initials: bool, keyboard: bool) -> Self {
        Self {
            romanization,
            initials,
            keyboard,
        }
    }
}

impl Default for KoreanBackend {
    fn default() -> Self {
        Self {
            romanization: true,
            initials: true,
            keyboard: true,
        }
    }
}

impl LanguageBackend for KoreanBackend {
    fn mode(&self) -> LangMode {
        LangMode::Korean
    }

    fn build_candidate_keys(&self, text: &str, budget: KeyBudget) -> Vec<SearchKey> {
        hangul::build_korean_keys_with_sources(text, budget.max_keys)
            .into_iter()
            .filter_map(|key| {
                let search_key = match key.kind {
                    hangul::KoreanKeyKind::Romanized => {
                        if !self.romanization {
                            return None;
                        }
                        SearchKey::korean_romanized(key.text)
                    }
                    hangul::KoreanKeyKind::Initials => {
                        if !self.initials {
                            return None;
                        }
                        SearchKey::korean_initials(key.text)
                    }
                    hangul::KoreanKeyKind::Keyboard => {
                        if !self.keyboard {
                            return None;
                        }
                        SearchKey::korean_keyboard(key.text)
                    }
                };
                Some(search_key.with_source_map(key.source_map))
            })
            .collect()
    }

    fn expand_query(&self, query: &str, _budget: QueryBudget) -> Vec<QueryVariant> {
        base_query_variants(query)
    }
}

#[cfg(test)]
mod tests {
    use yuru_core::{build_candidate, KeyKind, SearchConfig};

    use super::*;

    #[test]
    fn korean_mode_builds_hangul_keys() {
        let backend = KoreanBackend::default();
        let cand = build_candidate(0, "한글.txt", &backend, &SearchConfig::default());

        assert!(cand
            .keys
            .iter()
            .any(|key| key.kind == KeyKind::KoreanRomanized && key.text == "hangeul"));
        assert!(cand
            .keys
            .iter()
            .any(|key| key.kind == KeyKind::KoreanInitials && key.text == "ㅎㄱ"));
        assert!(cand
            .keys
            .iter()
            .any(|key| key.kind == KeyKind::KoreanKeyboard && key.text == "gksrmf"));
    }

    #[test]
    fn korean_mode_can_disable_each_generated_key_family() {
        let backend = KoreanBackend::new(false, true, false);
        let cand = build_candidate(0, "한글", &backend, &SearchConfig::default());

        assert!(cand
            .keys
            .iter()
            .any(|key| key.kind == KeyKind::KoreanInitials));
        assert!(!cand
            .keys
            .iter()
            .any(|key| key.kind == KeyKind::KoreanRomanized));
        assert!(!cand
            .keys
            .iter()
            .any(|key| key.kind == KeyKind::KoreanKeyboard));
    }

    #[test]
    fn korean_mode_does_not_build_japanese_or_chinese_keys() {
        let backend = KoreanBackend::default();
        let cand = build_candidate(0, "한글", &backend, &SearchConfig::default());

        assert!(!cand.keys.iter().any(|key| matches!(
            key.kind,
            KeyKind::KanaReading
                | KeyKind::RomajiReading
                | KeyKind::PinyinFull
                | KeyKind::PinyinJoined
                | KeyKind::PinyinInitials
        )));
    }
}