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 colored::Colorize;
6use hexz_ops::pack::{PackAnalysisFlags, PackConfig, PackTransformFlags, pack_archive};
7use std::path::PathBuf;
8use std::sync::{Arc, Mutex};
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!(
55            "{} Packing {}",
56            "╭".dimmed(),
57            output.display().to_string().cyan()
58        );
59        println!(
60            "{} Input   {}",
61            "│".dimmed(),
62            input_path.display().to_string().bright_black()
63        );
64        if let Some(ref b) = base {
65            println!(
66                "{} Base    {}",
67                "│".dimmed(),
68                b.display().to_string().bright_black()
69            );
70        }
71        println!("{}", "╰".dimmed());
72    }
73
74    // Create progress bar
75    let pb = if silent {
76        None
77    } else {
78        let pb = create_progress_bar(total_size);
79        Some(Arc::new(Mutex::new(pb)))
80    };
81    let pb_clone = pb.clone();
82
83    if train_dict && !silent {
84        println!("  {} Training compression dictionary...", "→".yellow());
85    }
86
87    // Create pack configuration
88    let config = PackConfig {
89        input: input_path,
90        base,
91        output,
92        compression,
93        password,
94        block_size,
95        min_chunk,
96        avg_chunk,
97        max_chunk,
98        num_workers: workers.unwrap_or(0),
99        transform: PackTransformFlags {
100            encrypt,
101            train_dict,
102            parallel: workers != Some(1),
103        },
104        analysis: PackAnalysisFlags {
105            show_progress: true,
106            use_dcam: dcam,
107            dcam_optimal,
108        },
109    };
110
111    // Run the packing operation with progress callback
112    let cb = move |current: u64, _: u64| {
113        if let Some(ref pb) = pb_clone {
114            if let Ok(pb) = pb.lock() {
115                pb.set_position(current);
116            }
117        }
118    };
119    pack_archive(&config, Some(&cb))?;
120
121    if let Some(ref pb) = pb {
122        if let Ok(pb) = pb.lock() {
123            pb.finish_with_message("Done");
124        }
125    }
126
127    if !silent {
128        println!("\n  {} Archive created.", "✓".green());
129    }
130    Ok(())
131}