use std::{
fs::File,
io::{self, BufWriter, Write},
};
use glossa_shared::{
PhfTupleKey, ToCompactString, fmt_compact,
phf_triple_key::RawTripleKey,
tap::{Pipe, Tap},
};
use phf_codegen::OrderedMap;
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use crate::{
MiniStr,
generator::{
Generator, MapType, flattening::L10nBTreeMap, output_bincode::create_buf_writer,
},
};
impl<'h> Generator<'h> {
pub fn output_phf_all_in_one(&'h self, non_dsl: MapType) -> io::Result<String> {
let vis_fn = self.get_visibility().as_str();
non_dsl
.get_non_dsl_maps(self)?
.iter()
.filter(|(_, data)| !data.is_empty())
.flat_map(|(lang, map_entry)| {
map_entry
.iter()
.map(|((name, k), v)| {
let new_key =
RawTripleKey(lang.to_compact_string(), name.as_str(), k.as_str());
let value = fmt_compact!(r##########"r#####"{v}"#####"##########);
(new_key, value)
})
})
.fold(OrderedMap::new(), |mut acc, (k, v)| {
acc.entry(k, &v);
acc
})
.pipe_ref_mut(|ordered_map| {
format!(
r#"{vis_fn} const fn map() -> super::PhfL10nAllInOneMap {{
use super::PhfTripleKey as Key;
{code} }}"#,
code = ordered_map
.phf_path("super::phf")
.build()
)
})
.pipe(Ok)
}
pub fn output_phf(&'h self, non_dsl: MapType) -> io::Result<()> {
let vis_fn = self.get_visibility().as_str();
non_dsl
.get_non_dsl_maps(self)?
.par_iter()
.filter(|(_, data)| !data.is_empty())
.map(|(lang, map_entry)| {
let new_phf_map = assemble_phf_map(map_entry);
(lang, new_phf_map)
})
.try_for_each(|(lang, mut map)| {
writeln!(
&mut self.create_rs_mod_file(lang)?,
r##"{vis_fn} const fn map() -> super::PhfL10nOrderedMap {{
use super::PhfTupleKey as Key;
{code} }}"##,
code = map
.phf_path("super::phf")
.build()
)
})
}
pub fn output_phf_without_map_name(&'h self, non_dsl: MapType) -> io::Result<()> {
let vis_fn = self.get_visibility().as_str();
non_dsl
.get_non_dsl_maps(self)?
.par_iter()
.filter(|(_, data)| !data.is_empty())
.map(|(lang, map_entry)| {
let new_phf_map = map_entry
.iter()
.map(|((_name, k), v)| {
let value = fmt_compact!(r##########"r#####"{v}"#####"##########);
(k.as_str(), value)
})
.fold(OrderedMap::new(), |mut acc, (k, v)| {
acc.entry(k, &v);
acc
});
(lang, new_phf_map)
})
.try_for_each(|(lang, mut map)| {
writeln!(
&mut self.create_rs_mod_file(lang)?,
r##"{vis_fn} const fn map() -> super::PhfStrMap {{
{code} }}"##,
code = map
.phf_path("super::phf")
.build()
)
})
}
pub(crate) fn create_rs_mod_file<D: core::fmt::Display>(
&self,
language: &D,
) -> io::Result<BufWriter<File>> {
let mod_prefix = self.get_mod_prefix();
let feat_prefix = self.get_feature_prefix();
let rs_file_name =
fmt_compact!("{mod_prefix}{}.rs", to_lower_snake_case(language));
let mod_name = rs_file_name.trim_end_matches(".rs");
eprintln!(
"#[cfg(feature = \"{feat_prefix}{language}\")]\n\
mod {mod_name};\n",
);
let out_dir = self.get_outdir().as_deref();
create_buf_writer(out_dir, rs_file_name)
}
}
fn assemble_phf_map(map_entry: &L10nBTreeMap) -> OrderedMap<PhfTupleKey<'_>> {
map_entry
.iter()
.map(|((name, k), v)| {
let tuple_key = PhfTupleKey(name.as_str(), k.as_str());
let value = fmt_compact!(r##########"r#####"{v}"#####"##########);
(tuple_key, value)
})
.fold(OrderedMap::new(), |mut acc, x| {
acc.entry(x.0, &x.1);
acc
})
}
pub fn to_lower_snake_case<D: core::fmt::Display>(id: D) -> MiniStr {
fmt_compact!("{id}")
.tap_mut(|s| s.make_ascii_lowercase())
.chars()
.map(|c| match c {
'-' | '.' => '_',
c => c,
})
.collect()
}
#[cfg(test)]
mod tests {
use anyhow::Result as AnyResult;
use glossa_shared::{PhfL10nAllInOneMap, PhfL10nOrderedMap, PhfTripleKey};
use super::*;
use crate::generator::dbg_generator::{
de_en_fr_pt_zh_generator, en_gb_generator, es_generator, new_generator,
};
#[ignore]
#[test]
fn test_build_en_gb_phf() -> AnyResult<()> {
en_gb_generator().output_phf(MapType::Regular)?;
Ok(())
}
#[ignore]
#[test]
fn test_build_es_phf() -> AnyResult<()> {
es_generator().output_phf(MapType::Regular)?;
Ok(())
}
#[ignore]
#[test]
fn test_build_all_phf() -> AnyResult<()> {
new_generator().output_phf(MapType::Regular)?;
Ok(())
}
#[ignore]
#[test]
fn test_build_all_in_one_phf() -> AnyResult<()> {
let function_data = new_generator().output_phf_all_in_one(MapType::Regular)?;
println!("{function_data}");
Ok(())
}
#[ignore]
#[test]
fn test_build_de_zh_fr_pt_phf_all_in_one() -> AnyResult<()> {
let function_data =
de_en_fr_pt_zh_generator().output_phf_all_in_one(MapType::Regular)?;
println!("{function_data}");
Ok(())
}
pub(crate) const fn en_gb_map() -> PhfL10nOrderedMap {
use PhfTupleKey as Key;
use lang_id::maps::phf;
phf::OrderedMap {
key: 12913932095322966823,
disps: &[(0, 0)],
idxs: &[0],
entries: &[(
Key(r#"error"#, r##"text-not-found"##),
r#####"No localised text found"#####,
)],
}
}
pub(crate) const fn all_in_one_map() -> PhfL10nAllInOneMap {
use PhfTripleKey as Key;
use lang_id::maps::phf;
phf::OrderedMap {
key: 12913932095322966823,
disps: &[(2, 3), (2, 0)],
idxs: &[5, 4, 0, 6, 3, 2, 1],
entries: &[
(
Key(r#"de"#, r##"error"##, r###"text-not-found"###),
r#####"Kein lokalisierter Text gefunden"#####,
),
(
Key(r#"en"#, r##"error"##, r###"text-not-found"###),
r#####"No localized text found"#####,
),
(
Key(r#"en-GB"#, r##"error"##, r###"text-not-found"###),
r#####"No localised text found"#####,
),
(
Key(r#"es"#, r##"error"##, r###"text-not-found"###),
r#####"No se encontró texto localizado"#####,
),
(
Key(r#"pt"#, r##"error"##, r###"text-not-found"###),
r#####"Nenhum texto localizado encontrado"#####,
),
(
Key(r#"zh"#, r##"error"##, r###"text-not-found"###),
r#####"未找到本地化文本"#####,
),
(
Key(r#"zh-Latn-CN"#, r##"error"##, r###"text-not-found"###),
r#####"MeiYou ZhaoDao BenDiHua WenBen"#####,
),
],
}
}
#[ignore]
#[test]
fn test_get_phf_en_map() {
let map = en_gb_map();
let v = map.get(&PhfTupleKey("error", "text-not-found"));
dbg!(v);
}
#[ignore]
#[test]
fn doc_test_get_all_in_one_map() {
let map = all_in_one_map();
let get_text =
|language| map.get(&PhfTripleKey(language, "error", "text-not-found"));
let zh_text = get_text("zh");
assert_eq!(zh_text, Some(&"未找到本地化文本"));
let language_chain = ["gsw", "de-CH", "de", "en"];
let text = language_chain
.into_iter()
.find_map(get_text);
assert_eq!(text, Some(&"Kein lokalisierter Text gefunden"));
}
}