Skip to main content

hexz_cli/cmd/data/
extract.rs

1//! Extract data from a Hexz archive.
2
3use anyhow::{Context, Result};
4use colored::Colorize;
5use hexz_core::format::header::Header;
6use hexz_core::format::magic::HEADER_SIZE;
7use hexz_ops::pack::extract_archive;
8use hexz_store::StorageBackend;
9use hexz_store::local::MmapBackend;
10use std::path::{Path, PathBuf};
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!(
41        "{} Extracting {}",
42        "╭".dimmed(),
43        input.display().to_string().cyan()
44    );
45    println!(
46        "{} Output     {}",
47        "╰".dimmed(),
48        output.display().to_string().bright_black()
49    );
50
51    extract_archive(input, &output, password).context("Failed to extract archive")?;
52
53    println!("\n  {} Extraction complete.", "✓".green());
54
55    Ok(())
56}