glossa_codegen/generator/
output_bincode.rs1use std::{
2 fs::File,
3 io::{self, BufWriter},
4 path::Path,
5};
6
7use glossa_shared::{MiniStr, fmt_compact, tap::Pipe};
8use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
9
10use crate::{
11 AnyResult,
12 generator::{Generator, MapType},
13 resources::L10nResMap,
14};
15
16impl<'h> Generator<'h> {
17 pub(crate) fn get_l10n_res_map(&self) -> &L10nResMap {
19 self
20 .get_resources()
21 .get_or_init_data()
22 }
23
24 fn encode_bincode<T, D>(&self, lang_id: D, data: T) -> AnyResult<()>
32 where
33 T: serde::Serialize,
34 D: core::fmt::Display,
35 {
36 bincode::serde::encode_into_std_write(
37 data,
38 &mut self.create_bincode_file(lang_id)?,
39 bincode::config::standard(),
40 )?;
41 Ok(())
42 }
43
44 pub fn output_bincode_all_in_one(&'h self, map_type: MapType) -> AnyResult<()> {
58 let all = "all";
59
60 if map_type.is_dsl() {
61 return match self.get_or_init_dsl_maps() {
62 x if x.is_empty() => Ok(()),
63 data => self.encode_bincode(all, data),
64 };
65 }
66
67 let data = map_type.get_non_dsl_maps(self)?;
68 self.encode_bincode(all, data)
69 }
70
71 pub fn output_bincode(&'h self, map_type: MapType) -> AnyResult<()> {
141 if map_type.is_dsl() {
142 return match self.get_or_init_dsl_maps() {
143 x if x.is_empty() => Ok(()),
144 iter => iter
145 .par_iter()
146 .try_for_each(|(lang, data)| self.encode_bincode(lang, data)),
147 };
148 }
149
150 map_type
151 .get_non_dsl_maps(self)?
152 .par_iter()
153 .try_for_each(|(lang, data)| self.encode_bincode(lang, data))
154 }
155
156 pub(crate) fn create_bincode_file<D: core::fmt::Display>(
165 &self,
166 language: D,
167 ) -> io::Result<BufWriter<File>> {
168 let suffix = self.get_bincode_suffix();
169 let bincode_name = fmt_compact!("{language}{suffix}");
170 let out_dir = self.get_outdir().as_deref();
171
172 create_buf_writer(out_dir, bincode_name)
173 }
174}
175
176pub(crate) fn create_buf_writer(
184 out_dir: Option<&Path>,
185 bincode_name: MiniStr,
186) -> io::Result<BufWriter<File>> {
187 out_dir
188 .ok_or_else(|| io::Error::other("Invalid outdir"))?
189 .join(bincode_name)
190 .pipe(File::create)?
191 .pipe(BufWriter::new)
192 .pipe(Ok)
193}
194
195#[cfg(test)]
196mod tests {
197 use testutils::simple_benchmark;
198
199 use super::*;
200 use crate::generator::dbg_generator::{en_gb_generator, new_generator};
201
202 #[ignore]
203 #[test]
204 fn test_output_tmpl_maps_to_bincode_files() -> AnyResult<()> {
205 new_generator()
206 .with_bincode_suffix(".tmpl.bincode".into())
207 .output_bincode(MapType::DSL)
208 }
209
210 #[ignore]
211 #[test]
212 fn doc_test_encode_and_decode_tmpl_bincode() -> AnyResult<()> {
213 let resources = crate::L10nResources::new("../../locales/");
214
215 Generator::default()
217 .with_resources(resources)
218 .with_outdir("tmp")
219 .with_bincode_suffix("_dsl.bincode".into())
220 .output_bincode(MapType::DSL)?;
221
222 let file = Path::new("tmp").join("en_dsl.bincode");
223 let dsl_maps = glossa_shared::decode::file::decode_single_file_to_dsl_map(file)?;
224
225 let unread_resolver = dsl_maps
226 .get("unread")
227 .expect("Failed to get DSL-AST (map_name: unread)");
228
229 let get_text = |num_str| {
230 unread_resolver
231 .get_with_context("show-unread-messages-count", &[("num", num_str)])
232 };
233
234 let one = get_text("1")?;
235 assert_eq!(one, "You have one unread message.");
236
237 let zero = get_text("0")?;
238 assert_eq!(zero, "No unread messages.");
239
240 Ok(())
241 }
242
243 #[ignore]
244 #[test]
245 fn test_encode_regular_aio_bincode() -> AnyResult<()> {
246 new_generator()
247 .with_bincode_suffix("_regular.bincode".into())
248 .output_bincode_all_in_one(MapType::Regular)
249 }
250
251 #[ignore]
252 #[test]
253 fn test_encode_regular_en_gb_bincode() -> AnyResult<()> {
254 en_gb_generator()
255 .with_bincode_suffix(".regular.bincode".into())
256 .output_bincode(MapType::Regular)
257 }
258
259 #[ignore]
260 #[test]
261 #[cfg(feature = "highlight")]
262 fn test_encode_highlight_aio_bincode() -> AnyResult<()> {
263 use crate::generator::dbg_generator;
264
265 dbg_generator::highlight_generator()
266 .with_bincode_suffix(".highlight.bincode".into())
267 .output_bincode_all_in_one(MapType::Highlight)
268 }
269
270 #[ignore]
271 #[test]
272 #[cfg(feature = "highlight")]
273 fn test_decode_highlight_aio() -> glossa_shared::decode::ResolverResult<()> {
274 let data =
275 glossa_shared::decode::file::decode_file_to_maps("tmp/all.highlight.bincode")?;
276
277 let en_maps = data.get("en").unwrap();
278 let value = en_maps
279 .get(&("md_md".into(), "pwsh".into()))
280 .unwrap();
281 println!("{value}");
282 Ok(())
283 }
284
285 #[ignore]
286 #[test]
287 fn test_encode_tmpl_aio_bincode() -> AnyResult<()> {
288 new_generator()
289 .with_bincode_suffix("_tmpl.bincode".into())
290 .output_bincode_all_in_one(MapType::DSL)
291 }
292
293 #[ignore]
294 #[test]
295 fn test_decode_tmpl_aio_bincode() -> AnyResult<()> {
296 let raw_map =
297 glossa_shared::decode::file::decode_file_to_dsl_maps("tmp/all_tmpl.bincode")?;
298
299 let zh_maps = raw_map.get("zh").unwrap();
300 let zh_unread_map = zh_maps.get("unread").unwrap();
301
302 let text = zh_unread_map
303 .get_with_context("show-unread-messages-count", &[("num", "2")])?;
304 dbg!(text);
305
306 Ok(())
307 }
308
309 #[ignore]
321 #[test]
322 fn bench_decode_regular_file() -> AnyResult<()> {
323 let file = Path::new("tmp").join("all_regular.bincode");
324
325 eprintln!("decode from file");
326 simple_benchmark(|| {
327 let _ = glossa_shared::decode::file::decode_file_to_maps(&file);
328 });
329
330 let bytes = std::fs::read(&file)?;
331 let decode_slice = || glossa_shared::decode::slice::decode_to_maps(&bytes);
332
333 eprintln!("decode from slice");
334 simple_benchmark(|| {
335 let _ = decode_slice();
336 });
337
338 Ok(())
339 }
340}