Skip to main content

hexz_cli/cmd/data/
pack.rs

1//! Pack data into a Hexz archive.
2
3use crate::ui::progress::create_progress_bar;
4use anyhow::Result;
5use hexz_ops::pack::{PackConfig, PackAnalysisFlags, PackTransformFlags, pack_archive};
6use std::path::PathBuf;
7use std::sync::{Arc, Mutex};
8use colored::Colorize;
9
10/// Execute the pack command to create a Hexz archive archive.
11#[allow(clippy::too_many_arguments, clippy::fn_params_excessive_bools)]
12pub fn run(
13    input: Option<PathBuf>,
14    base: Option<PathBuf>,
15    output: PathBuf,
16    compression: String,
17    encrypt: bool,
18    train_dict: bool,
19    block_size: u32,
20    min_chunk: Option<u32>,
21    avg_chunk: Option<u32>,
22    max_chunk: Option<u32>,
23    workers: Option<usize>,
24    dcam: bool,
25    dcam_optimal: bool,
26    silent: bool,
27) -> Result<()> {
28    // Get password if encryption is enabled
29    let password = if encrypt {
30        Some(match std::env::var("HEXZ_PASSWORD") {
31            Ok(p) => p,
32            Err(_) => rpassword::prompt_password("Enter encryption password: ")?,
33        })
34    } else {
35        None
36    };
37
38    let input_path = input.unwrap_or_else(|| PathBuf::from("."));
39
40    // Calculate total size for progress bar
41    let total_size = if input_path.is_dir() {
42        walkdir::WalkDir::new(&input_path)
43            .into_iter()
44            .filter_map(std::result::Result::ok)
45            .filter(|e: &walkdir::DirEntry| e.file_type().is_file())
46            .map(|e: walkdir::DirEntry| e.metadata().map_or(0, |m| m.len()))
47            .sum()
48    } else {
49        std::fs::metadata(&input_path).map_or(0, |m| m.len())
50    };
51
52    // Setup UI
53    if !silent {
54        println!("{} Packing {}", "╭".dimmed(), output.display().to_string().cyan());
55        println!("{} Input   {}", "│".dimmed(), input_path.display().to_string().bright_black());
56        if let Some(ref b) = base {
57            println!("{} Base    {}", "│".dimmed(), b.display().to_string().bright_black());
58        }
59        println!("{}", "╰".dimmed());
60    }
61
62    // Create progress bar
63    let pb = if silent {
64        None
65    } else {
66        let pb = create_progress_bar(total_size);
67        Some(Arc::new(Mutex::new(pb)))
68    };
69    let pb_clone = pb.clone();
70
71    if train_dict && !silent {
72        println!("  {} Training compression dictionary...", "→".yellow());
73    }
74
75    // Create pack configuration
76    let config = PackConfig {
77        input: input_path,
78        base,
79        output,
80        compression,
81        password,
82        block_size,
83        min_chunk,
84        avg_chunk,
85        max_chunk,
86        num_workers: workers.unwrap_or(0),
87        transform: PackTransformFlags {
88            encrypt,
89            train_dict,
90            parallel: workers != Some(1),
91        },
92        analysis: PackAnalysisFlags {
93            show_progress: true,
94            use_dcam: dcam,
95            dcam_optimal,
96        },
97    };
98
99    // Run the packing operation with progress callback
100    let cb = move |current: u64, _: u64| {
101        if let Some(ref pb) = pb_clone {
102            if let Ok(pb) = pb.lock() {
103                pb.set_position(current);
104            }
105        }
106    };
107    pack_archive(&config, Some(&cb))?;
108
109    if let Some(ref pb) = pb {
110        if let Ok(pb) = pb.lock() {
111            pb.finish_with_message("Done");
112        }
113    }
114
115    if !silent {
116        println!("\n  {} Archive created.", "✓".green());
117    }
118    Ok(())
119}