use std::{
fs::{self, File},
io::Write,
path::{Path, PathBuf},
};
use anyhow::{Ok, Result};
use zip::ZipWriter;
use zip::write::SimpleFileOptions;
use crate::{
cli::Options,
dict::{
index::get_index,
writer::{STYLES_CSS, STYLES_CSS_EXPERIMENTAL},
},
lang::Lang,
models::yomitan::{YomitanDict, YomitanEntry},
path::PathManager,
tags::get_tag_bank_as_tag_info,
utils::pretty_print_at_path,
};
const BANK_SIZE: usize = 25_000;
enum Sink<'a> {
Disk,
Zip(&'a mut ZipWriter<File>, SimpleFileOptions),
}
pub fn write_test_yomitan(opts: &Options, pm: &PathManager, ydict: YomitanDict) -> Result<PathBuf> {
let out_dir = pm.dir_temp_dict();
fs::create_dir_all(&out_dir)?;
let mut bank_index = 0;
for group in ydict.into_iter_grouped() {
write_banks(
opts.pretty,
opts.quiet,
&group.entries,
&mut bank_index,
group.label,
&out_dir,
Sink::Disk,
)?;
}
Ok(out_dir)
}
pub fn write_yomitan(
source: Lang,
target: Lang,
opts: &Options,
pm: &PathManager,
ydict: YomitanDict,
) -> Result<PathBuf> {
let writer_path = pm.path_dict();
let writer_file = File::create(&writer_path)?;
let mut zip = ZipWriter::new(writer_file);
let zip_opts =
SimpleFileOptions::default().compression_method(zip::CompressionMethod::Deflated);
let index_string = get_index(pm.dict_ty, &pm.dict_name_expanded(), source, target);
zip.start_file("index.json", zip_opts)?;
zip.write_all(index_string.as_bytes())?;
zip.start_file("styles.css", zip_opts)?;
if opts.experimental {
zip.write_all(STYLES_CSS_EXPERIMENTAL)?;
} else {
zip.write_all(STYLES_CSS)?;
}
let tag_bank = get_tag_bank_as_tag_info(target);
let tag_bank_bytes = serde_json::to_vec_pretty(&tag_bank)?;
zip.start_file("tag_bank_1.json", zip_opts)?; zip.write_all(&tag_bank_bytes)?;
let mut bank_index = 0;
for group in ydict.into_iter_grouped() {
write_banks(
opts.pretty,
opts.quiet,
&group.entries,
&mut bank_index,
group.label,
&writer_path,
Sink::Zip(&mut zip, zip_opts),
)?;
}
zip.finish()?;
Ok(writer_path)
}
#[tracing::instrument(skip_all, level = "DEBUG")]
fn write_banks(
pretty: bool,
quiet: bool,
yomitan_entries: &[YomitanEntry],
bank_index: &mut usize,
label: &'static str,
out_dir: &Path,
mut sink: Sink,
) -> Result<()> {
let bank_name_prefix = match yomitan_entries.first() {
Some(first) => first.file_prefix(),
None => return Ok(()),
};
let total_bank_num = yomitan_entries.len().div_ceil(BANK_SIZE);
for (bank_num, bank) in yomitan_entries.chunks(BANK_SIZE).enumerate() {
*bank_index += 1;
let json_bytes = if pretty {
serde_json::to_vec_pretty(&bank)?
} else {
serde_json::to_vec(&bank)?
};
let bank_name = format!("{bank_name_prefix}_{bank_index}.json");
let file_path = out_dir.join(&bank_name);
match sink {
Sink::Disk => {
let mut file = File::create(&file_path)?;
file.write_all(&json_bytes)?;
}
Sink::Zip(ref mut zip, zip_options) => {
zip.start_file(&bank_name, zip_options)?;
zip.write_all(&json_bytes)?;
}
}
if !quiet {
if bank_num > 0 {
print!("\r\x1b[K");
}
pretty_print_at_path(
&format!(
"Wrote yomitan {label} bank {}/{total_bank_num} ({} entries)",
bank_num + 1,
bank.len()
),
file_path,
);
std::io::stdout().flush()?;
}
}
if !quiet {
println!();
}
Ok(())
}