1use std::io::{self, Write};
2
3use glossa_shared::{
4 ToCompactString, fmt_compact,
5 tap::{Pipe, Tap},
6};
7
8use crate::{
9 MiniStr,
10 generator::{Generator, MapType},
11};
12
13impl<'h> Generator<'h> {
14 pub fn output_match_fn(&self, non_dsl: MapType) -> io::Result<()> {
17 const HEADER: &str = r##"const fn map(map_name: &[u8], key: &[u8]) -> &'static str {
18 match (map_name, key) {
19 "##;
20 let new_header = || self.new_fn_header(HEADER);
21
22 non_dsl
24 .get_non_dsl_maps(self)?
25 .iter()
26 .map(|(lang, map_entry)| {
28 let match_fn_string = map_entry
29 .iter()
30 .fold(
31 new_header(), |mut acc, ((map_name, data_k), data_v)| {
33 [
35 "(",
36 key_as_bytes(map_name).as_str(),
37 ", ",
38 key_as_bytes(data_k).as_str(),
39 r#") => r#####"#,
40 "\"", data_v.as_str(),
42 "\"", r###########"#####,"###########, "\n",
45 ]
46 .map(|s| acc.push_str(s));
47 acc
48 },
49 )
50 .tap_mut(|buf| buf.push_str(" _ => \"\",\n}}"));
51 (lang, match_fn_string)
52 })
53 .try_for_each(|(lang, s)| {
54 self
56 .create_rs_mod_file(lang)?
57 .write_all(s.as_bytes())
58 })
59 }
60
61 pub fn output_match_fn_all_in_one(
113 &'h self,
114 non_dsl: MapType,
115 ) -> io::Result<String> {
116 const S_HEADER: &str = r##"const fn map(lang: &[u8], map_name: &[u8], key: &[u8])
117 -> &'static str {
118 match (lang, map_name, key) {
119 "##;
120
121 let new_header = || self.new_fn_header(S_HEADER);
122
123 non_dsl
124 .get_non_dsl_maps(self)?
125 .iter()
126 .flat_map(|(lang, map_entry)| {
128 map_entry
129 .iter()
130 .map(move |e| (lang, e))
131 })
132 .fold(
133 new_header(), |mut acc, (lang, ((name, key), value))| {
135 [
137 "(b\"",
138 lang
139 .to_compact_string()
140 .as_str(),
141 "\", ",
142 key_as_bytes(name).as_str(),
143 ", ",
144 key_as_bytes(key).as_str(),
145 ") => r#####",
146 "\"",
147 value.as_str(),
148 "\"",
149 "#####,",
150 "\n",
151 ]
152 .map(|s| acc.push_str(s));
153 acc
154 },
155 )
156 .tap_mut(|buf| buf.push_str(" _ => \"\",\n}}"))
157 .pipe(Ok)
158 }
159
160 pub fn output_match_fn_all_in_one_by_language(
168 &'h self,
169 non_dsl: MapType,
170 ) -> io::Result<String> {
171 const S_HEADER: &str = r##"const fn map(language: &[u8]) -> &'static str {
172 match language {
173 "##;
174
175 let new_header = || self.new_fn_header(S_HEADER);
176
177 non_dsl
178 .get_non_dsl_maps(self)?
179 .iter()
180 .flat_map(|(lang, map_entry)| {
181 map_entry
182 .iter()
183 .map(move |(_ks, v)| (lang, v))
184 })
185 .fold(
186 new_header(), |mut acc, (lang, value)| {
188 [
189 "b\"",
190 lang
191 .to_compact_string()
192 .as_str(),
193 "\" => r#####",
194 "\"",
195 value.as_str(),
196 "\"",
197 "#####,",
198 "\n",
199 ]
200 .map(|s| acc.push_str(s));
201 acc
202 },
203 )
204 .tap_mut(|buf| buf.push_str(" _ => \"\",\n}}"))
205 .pipe(Ok)
206 }
207
208 pub fn output_match_fn_all_in_one_without_map_name(
239 &'h self,
240 non_dsl: MapType,
241 ) -> io::Result<String> {
242 const S_HEADER: &str = r##"const fn map(language: &[u8], key: &[u8])
243 -> &'static str {
244 match (language, key) {
245 "##;
246
247 let new_header = || self.new_fn_header(S_HEADER);
248
249 non_dsl
250 .get_non_dsl_maps(self)?
251 .iter()
252 .flat_map(|(lang, map_entry)| {
253 map_entry
254 .iter()
255 .map(move |((_name, key), value)| (lang, key, value))
256 })
257 .fold(
258 new_header(), |mut acc, (lang, key, value)| {
260 [
261 "(b\"",
262 lang
263 .to_compact_string()
264 .as_str(),
265 "\", ",
266 key_as_bytes(key).as_str(),
267 ") => r#####",
268 "\"",
269 value.as_str(),
270 "\"",
271 "#####,",
272 "\n",
273 ]
274 .map(|s| acc.push_str(s));
275 acc
276 },
277 )
278 .tap_mut(|buf| buf.push_str(" _ => \"\",\n}}"))
279 .pipe(Ok)
280 }
281
282 pub fn new_fn_header(&self, header: &str) -> String {
284 let vis_fn = self.get_visibility().as_str();
285 String::with_capacity(8192).tap_mut(|buf| {
286 [vis_fn, " ", header].map(|s| buf.push_str(s));
287 })
288 }
289
290 pub fn output_match_fn_without_map_name(
296 &'h self,
297 non_dsl: MapType,
298 ) -> io::Result<()> {
299 const HEADER: &str = r##"const fn map(key: &[u8]) -> &'static str {
300 match key {
301 "##;
302 let new_header = || self.new_fn_header(HEADER);
303
304 non_dsl
306 .get_non_dsl_maps(self)?
307 .iter()
308 .map(|(lang, map_entry)| {
310 let match_fn_string = map_entry
311 .iter()
312 .fold(
313 new_header(), |mut acc, ((_, data_k), data_v)| {
315 [
317 key_as_bytes(data_k).as_str(),
318 r#" => r#####"#,
319 "\"", data_v.as_str(),
321 "\"", r###########"#####,"###########, "\n",
324 ]
325 .map(|s| acc.push_str(s));
326 acc
327 },
328 )
329 .tap_mut(|buf| buf.push_str(" _ => \"\",\n}}"));
330 (lang, match_fn_string)
331 })
332 .try_for_each(|(lang, s)| {
333 self
335 .create_rs_mod_file(lang)?
336 .write_all(s.as_bytes())
337 })
338 }
339}
340
341pub(crate) fn key_as_bytes(key: &str) -> MiniStr {
343 use fmt_compact as fmt;
344
345 match key.is_ascii() {
346 true => fmt!("b{key:?}"),
347 _ => fmt!("{:?}", key.as_bytes()),
348 }
349}
350
351#[cfg(test)]
352mod tests {
353 use glossa_shared::display::puts;
354
355 use super::*;
356 use crate::{
357 AnyResult,
358 generator::dbg_generator::{en_generator, new_generator},
359 };
360
361 #[ignore]
362 #[test]
363 fn test_output_match_fn() -> AnyResult<()> {
364 new_generator().output_match_fn(MapType::Regular)?;
365 Ok(())
366 }
367
368 #[ignore]
369 #[test]
370 fn test_output_aio_match_fn() -> AnyResult<()> {
371 en_generator()
372 .output_match_fn_all_in_one(MapType::Regular)?
373 .pipe_ref(puts)
374 .pipe(Ok)
375 }
376
377 const fn map(language: &[u8], map_name: &[u8], key: &[u8]) -> &'static str {
378 match (language, map_name, key) {
379 (b"de", b"error", b"text-not-found") => {
380 r###"Kein lokalisierter Text gefunden"###
381 }
382 (b"el", b"error", b"text-not-found") => {
383 r###"Δεν βρέθηκε κανένα τοπικό κείμενο"###
384 }
385 (b"en", b"error", b"text-not-found") => r###"No localized text found"###,
386 (b"en", b"test", [240, 159, 145, 139, 240, 159, 140, 144]) => {
387 r###"hello world"###
388 }
389 (b"en", b"test", b"hello") => r###"world"###,
390 (b"en-GB", b"error", b"text-not-found") => r###"No localised text found"###,
391 (b"zh", b"error", b"text-not-found") => r###"未找到本地化文本"###,
392 (b"zh", b"test", b"quote") => r###"""no"''""###,
393 (b"zh-Hant", b"error", b"text-not-found") => r###"沒有找到本地化文本"###,
394 _ => "",
395 }
396 }
397
398 #[ignore]
399 #[test]
400 fn test_get_match_map() {
401 const S: &str = map(b"de", b"error", "text-not-found".as_bytes());
402 assert!(!S.is_empty());
403 println!("{S}");
404 }
405
406 #[ignore]
407 #[test]
408 fn doc_test_all_in_one_match_fn() -> io::Result<()> {
409 use crate::L10nResources;
410 const L10N_DIR: &str = "../../locales/";
411
412 let data = L10nResources::new(L10N_DIR).with_include_languages([
413 "en-GB",
414 "de",
415 "es",
416 "pt",
417 "zh-pinyin",
418 ]);
419
420 let function_data = Generator::default()
421 .with_resources(data)
422 .output_match_fn_all_in_one(MapType::Regular)?;
423
424 assert_eq!(function_data, r#######"
425 pub(crate) const fn map(lang: &[u8], map_name: &[u8], key: &[u8]) ->
426 &'static str {
427 match (lang, map_name, key) {
428 (b"de", b"error", b"text-not-found") => r#####"Kein lokalisierter Text gefunden"#####,
429(b"en-GB", b"error", b"text-not-found") => r#####"No localised text found"#####,
430(b"es", b"error", b"text-not-found") => r#####"No se encontró texto localizado"#####,
431(b"pt", b"error", b"text-not-found") => r#####"Nenhum texto localizado encontrado"#####,
432(b"zh-Latn-CN", b"error", b"text-not-found") => r#####"MeiYou ZhaoDao BenDiHua WenBen"#####,
433 _ => "",
434}}
435 "#######.trim());
436
437 println!("{function_data}");
438 Ok(())
439 }
440}