Skip to main content

hexz_cli/cmd/data/
extract.rs

1//! Extract data from a Hexz archive.
2
3use anyhow::{Context, Result};
4use hexz_ops::pack::extract_archive;
5use hexz_core::format::header::Header;
6use hexz_core::format::magic::HEADER_SIZE;
7use hexz_store::local::MmapBackend;
8use hexz_store::StorageBackend;
9use std::path::{Path, PathBuf};
10use colored::Colorize;
11
12/// Execute the `hexz extract` command.
13pub fn run(input: &Path, output: Option<PathBuf>) -> Result<()> {
14    // 1. Resolve output path
15    let output = if let Some(p) = output {
16        p
17    } else {
18        // Default: if it has a manifest, use dir name, otherwise .bin
19        let mut out = input.to_path_buf();
20        let _ = out.set_extension("");
21        out
22    };
23
24    // 2. Check for encryption
25    let password = {
26        let backend = MmapBackend::new(input)?;
27        let header_bytes = backend.read_exact(0, HEADER_SIZE)?;
28        let header: Header = bincode::deserialize(&header_bytes)?;
29
30        if header.encryption.is_some() {
31            Some(match std::env::var("HEXZ_PASSWORD") {
32                Ok(p) => p,
33                Err(_) => rpassword::prompt_password("Enter decryption password: ")?,
34            })
35        } else {
36            None
37        }
38    };
39
40    println!("{} Extracting {}", "╭".dimmed(), input.display().to_string().cyan());
41    println!("{} Output     {}", "╰".dimmed(), output.display().to_string().bright_black());
42
43    extract_archive(input, &output, password).context("Failed to extract archive")?;
44
45    println!("\n  {} Extraction complete.", "✓".green());
46
47    Ok(())
48}