1#[derive(Copy, Clone, Debug, Eq, PartialEq)]
5pub enum ConversionDirection {
6 RuToEn,
7 EnToRu,
8}
9
10const fn is_latin_letter(ch: char) -> bool {
11 ch.is_ascii_alphabetic()
12}
13
14const fn is_cyrillic_letter(ch: char) -> bool {
15 matches!(ch, 'А'..='Я' | 'а'..='я' | 'Ё' | 'ё')
16}
17
18const fn map_ru_to_en(ch: char) -> char {
19 match ch {
20 ',' => '?',
22 '.' => '/',
23 '?' => '&',
24
25 '"' => '@', '№' => '#', ';' => '$', ':' => '^', 'й' => 'q',
32 'ц' => 'w',
33 'у' => 'e',
34 'к' => 'r',
35 'е' => 't',
36 'н' => 'y',
37 'г' => 'u',
38 'ш' => 'i',
39 'щ' => 'o',
40 'з' => 'p',
41 'х' => '[',
42 'ъ' => ']',
43 'ф' => 'a',
44 'ы' => 's',
45 'в' => 'd',
46 'а' => 'f',
47 'п' => 'g',
48 'р' => 'h',
49 'о' => 'j',
50 'л' => 'k',
51 'д' => 'l',
52 'ж' => ';',
53 'э' => '\'',
54 'я' => 'z',
55 'ч' => 'x',
56 'с' => 'c',
57 'м' => 'v',
58 'и' => 'b',
59 'т' => 'n',
60 'ь' => 'm',
61 'б' => ',',
62 'ю' => '.',
63 'ё' => '`',
64
65 'Й' => 'Q',
66 'Ц' => 'W',
67 'У' => 'E',
68 'К' => 'R',
69 'Е' => 'T',
70 'Н' => 'Y',
71 'Г' => 'U',
72 'Ш' => 'I',
73 'Щ' => 'O',
74 'З' => 'P',
75 'Х' => '{',
76 'Ъ' => '}',
77 'Ф' => 'A',
78 'Ы' => 'S',
79 'В' => 'D',
80 'А' => 'F',
81 'П' => 'G',
82 'Р' => 'H',
83 'О' => 'J',
84 'Л' => 'K',
85 'Д' => 'L',
86 'Ж' => ':',
87 'Э' => '"',
88 'Я' => 'Z',
89 'Ч' => 'X',
90 'С' => 'C',
91 'М' => 'V',
92 'И' => 'B',
93 'Т' => 'N',
94 'Ь' => 'M',
95 'Б' => '<',
96 'Ю' => '>',
97 'Ё' => '~',
98 _ => ch,
99 }
100}
101
102const fn map_en_to_ru(ch: char) -> char {
103 match ch {
104 'q' => 'й',
106 'w' => 'ц',
107 'e' => 'у',
108 'r' => 'к',
109 't' => 'е',
110 'y' => 'н',
111 'u' => 'г',
112 'i' => 'ш',
113 'o' => 'щ',
114 'p' => 'з',
115 '[' => 'х',
116 ']' => 'ъ',
117 'a' => 'ф',
118 's' => 'ы',
119 'd' => 'в',
120 'f' => 'а',
121 'g' => 'п',
122 'h' => 'р',
123 'j' => 'о',
124 'k' => 'л',
125 'l' => 'д',
126 ';' => 'ж',
127 '\'' => 'э',
128 'z' => 'я',
129 'x' => 'ч',
130 'c' => 'с',
131 'v' => 'м',
132 'b' => 'и',
133 'n' => 'т',
134 'm' => 'ь',
135 ',' => 'б',
136 '.' => 'ю',
137 '`' => 'ё',
138
139 '?' => ',',
141 '/' => '.',
142 '&' => '?',
143
144 '@' => '"', '#' => '№', '$' => ';', '^' => ':', 'Q' => 'Й',
153 'W' => 'Ц',
154 'E' => 'У',
155 'R' => 'К',
156 'T' => 'Е',
157 'Y' => 'Н',
158 'U' => 'Г',
159 'I' => 'Ш',
160 'O' => 'Щ',
161 'P' => 'З',
162 '{' => 'Х',
163 '}' => 'Ъ',
164 'A' => 'Ф',
165 'S' => 'Ы',
166 'D' => 'В',
167 'F' => 'А',
168 'G' => 'П',
169 'H' => 'Р',
170 'J' => 'О',
171 'K' => 'Л',
172 'L' => 'Д',
173 ':' => 'Ж',
174 '"' => 'Э',
175 'Z' => 'Я',
176 'X' => 'Ч',
177 'C' => 'С',
178 'V' => 'М',
179 'B' => 'И',
180 'N' => 'Т',
181 'M' => 'Ь',
182 '<' => 'Б',
183 '>' => 'Ю',
184 '~' => 'Ё',
185 _ => ch,
186 }
187}
188
189fn letter_counts(text: &str) -> (usize, usize) {
190 let mut cyr = 0usize;
191 let mut lat = 0usize;
192 for ch in text.chars() {
193 if is_cyrillic_letter(ch) {
194 cyr += 1;
195 } else if is_latin_letter(ch) {
196 lat += 1;
197 }
198 }
199 (cyr, lat)
200}
201
202#[must_use]
206pub fn conversion_direction_for_text(text: &str) -> Option<ConversionDirection> {
207 let (cyr, lat) = letter_counts(text);
208 match cyr.cmp(&lat) {
209 std::cmp::Ordering::Greater => Some(ConversionDirection::RuToEn),
210 std::cmp::Ordering::Less => Some(ConversionDirection::EnToRu),
211 std::cmp::Ordering::Equal => None,
212 }
213}
214
215#[must_use]
217pub fn convert_ru_en_with_direction(text: &str, direction: ConversionDirection) -> String {
218 let mut out = match direction {
221 ConversionDirection::RuToEn => String::with_capacity(text.len()),
222 ConversionDirection::EnToRu => String::with_capacity(text.len().saturating_mul(2)),
223 };
224 match direction {
225 ConversionDirection::RuToEn => {
226 for ch in text.chars() {
227 out.push(map_ru_to_en(ch));
228 }
229 }
230 ConversionDirection::EnToRu => {
231 for ch in text.chars() {
232 out.push(map_en_to_ru(ch));
233 }
234 }
235 }
236 out
237}
238
239#[must_use]
242pub fn convert_ru_en_bidirectional(text: &str) -> String {
243 let direction = conversion_direction_for_text(text).unwrap_or(ConversionDirection::RuToEn);
244 convert_ru_en_with_direction(text, direction)
245}