1use std::fs::{self, File};
2use std::path::{Path, PathBuf};
3use std::{env, io};
4
5use clap::Args;
6use color_eyre::eyre::Result;
7use fluent_templates::Loader;
8use novel_api::Timing;
9use zip::ZipArchive;
10
11use crate::{LANG_ID, LOCALES, utils};
12
13#[must_use]
14#[derive(Args)]
15#[command(arg_required_else_help = true,
16 about = LOCALES.lookup(&LANG_ID, "unzip_command"))]
17pub struct Unzip {
18 #[arg(help = LOCALES.lookup(&LANG_ID, "epub_path"))]
19 pub epub_path: PathBuf,
20
21 #[arg(short, long, default_value_t = false,
22 help = LOCALES.lookup(&LANG_ID, "delete"))]
23 pub delete: bool,
24}
25
26pub fn execute(config: Unzip) -> Result<()> {
27 let mut timing = Timing::new();
28
29 utils::ensure_epub_file(&config.epub_path)?;
30
31 unzip(&config.epub_path)?;
32
33 if config.delete {
34 utils::remove_file_or_dir(&config.epub_path)?;
35 }
36
37 tracing::debug!("Time spent on `unzip`: {}", timing.elapsed()?);
38
39 Ok(())
40}
41
42fn unzip<T>(path: T) -> Result<()>
43where
44 T: AsRef<Path>,
45{
46 let path = path.as_ref();
47
48 let output_dir = env::current_dir()?.join(path.file_stem().unwrap());
49 if output_dir.try_exists()? {
50 tracing::warn!("The epub output directory already exists and will be deleted");
51 utils::remove_file_or_dir(&output_dir)?;
52 }
53
54 let file = File::open(path)?;
55 let mut archive = ZipArchive::new(file)?;
56
57 for i in 0..archive.len() {
58 let mut file = archive.by_index(i)?;
59 let outpath = match file.enclosed_name() {
60 Some(path) => path.to_owned(),
61 None => continue,
62 };
63 let outpath = output_dir.join(outpath);
64
65 if (*file.name()).ends_with('/') {
66 fs::create_dir_all(&outpath)?;
67 } else {
68 if let Some(p) = outpath.parent() {
69 if !p.try_exists()? {
70 fs::create_dir_all(p)?;
71 }
72 }
73 let mut outfile = fs::File::create(&outpath)?;
74 io::copy(&mut file, &mut outfile)?;
75 }
76
77 #[cfg(unix)]
78 {
79 use std::fs::Permissions;
80 use std::os::unix::fs::PermissionsExt;
81
82 if let Some(mode) = file.unix_mode() {
83 fs::set_permissions(&outpath, Permissions::from_mode(mode))?;
84 }
85 }
86 }
87
88 Ok(())
89}