1pub mod hangul;
8
9use yuru_core::{
10 base_query_variants, KeyBudget, LangMode, LanguageBackend, QueryBudget, QueryVariant, SearchKey,
11};
12
13#[derive(Clone, Debug)]
14pub struct KoreanBackend {
16 romanization: bool,
17 initials: bool,
18 keyboard: bool,
19}
20
21impl KoreanBackend {
22 pub fn new(romanization: bool, initials: bool, keyboard: bool) -> Self {
24 Self {
25 romanization,
26 initials,
27 keyboard,
28 }
29 }
30}
31
32impl Default for KoreanBackend {
33 fn default() -> Self {
34 Self {
35 romanization: true,
36 initials: true,
37 keyboard: true,
38 }
39 }
40}
41
42impl LanguageBackend for KoreanBackend {
43 fn mode(&self) -> LangMode {
44 LangMode::Korean
45 }
46
47 fn build_candidate_keys(&self, text: &str, budget: KeyBudget) -> Vec<SearchKey> {
48 if budget.max_keys == 0 || text.len() > budget.max_total_bytes {
49 return Vec::new();
50 }
51 hangul::build_korean_keys_with_sources_with_budget(
52 text,
53 budget.max_keys,
54 budget.max_total_bytes,
55 )
56 .into_iter()
57 .filter_map(|key| {
58 let search_key = match key.kind {
59 hangul::KoreanKeyKind::Romanized => {
60 if !self.romanization {
61 return None;
62 }
63 SearchKey::korean_romanized(key.text)
64 }
65 hangul::KoreanKeyKind::Initials => {
66 if !self.initials {
67 return None;
68 }
69 SearchKey::korean_initials(key.text)
70 }
71 hangul::KoreanKeyKind::Keyboard => {
72 if !self.keyboard {
73 return None;
74 }
75 SearchKey::korean_keyboard(key.text)
76 }
77 };
78 Some(search_key.with_source_map(key.source_map))
79 })
80 .collect()
81 }
82
83 fn expand_query(&self, query: &str, _budget: QueryBudget) -> Vec<QueryVariant> {
84 base_query_variants(query)
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use yuru_core::{build_candidate, KeyKind, SearchConfig};
91
92 use super::*;
93
94 #[test]
95 fn korean_mode_builds_hangul_keys() {
96 let backend = KoreanBackend::default();
97 let cand = build_candidate(0, "한글.txt", &backend, &SearchConfig::default());
98
99 assert!(cand
100 .keys
101 .iter()
102 .any(|key| key.kind == KeyKind::KoreanRomanized && key.text == "hangeul"));
103 assert!(cand
104 .keys
105 .iter()
106 .any(|key| key.kind == KeyKind::KoreanInitials && key.text == "ㅎㄱ"));
107 assert!(cand
108 .keys
109 .iter()
110 .any(|key| key.kind == KeyKind::KoreanKeyboard && key.text == "gksrmf"));
111 }
112
113 #[test]
114 fn korean_mode_can_disable_each_generated_key_family() {
115 let backend = KoreanBackend::new(false, true, false);
116 let cand = build_candidate(0, "한글", &backend, &SearchConfig::default());
117
118 assert!(cand
119 .keys
120 .iter()
121 .any(|key| key.kind == KeyKind::KoreanInitials));
122 assert!(!cand
123 .keys
124 .iter()
125 .any(|key| key.kind == KeyKind::KoreanRomanized));
126 assert!(!cand
127 .keys
128 .iter()
129 .any(|key| key.kind == KeyKind::KoreanKeyboard));
130 }
131
132 #[test]
133 fn korean_mode_does_not_build_japanese_or_chinese_keys() {
134 let backend = KoreanBackend::default();
135 let cand = build_candidate(0, "한글", &backend, &SearchConfig::default());
136
137 assert!(!cand.keys.iter().any(|key| matches!(
138 key.kind,
139 KeyKind::KanaReading
140 | KeyKind::RomajiReading
141 | KeyKind::PinyinFull
142 | KeyKind::PinyinJoined
143 | KeyKind::PinyinInitials
144 )));
145 }
146}