use std::env;
use std::sync::atomic::{AtomicU64, Ordering};
use zesven::progress::ProgressReporter;
use zesven::{Archive, ExtractOptions, Result};
struct ProgressBarReporter {
bytes_extracted: AtomicU64,
total_bytes: u64,
entries_processed: AtomicU64,
total_entries: u64,
current_entry: std::sync::Mutex<String>,
}
impl ProgressBarReporter {
fn new(total_bytes: u64, total_entries: u64) -> Self {
Self {
bytes_extracted: AtomicU64::new(0),
total_bytes,
entries_processed: AtomicU64::new(0),
total_entries,
current_entry: std::sync::Mutex::new(String::new()),
}
}
fn percentage(&self) -> f64 {
if self.total_bytes == 0 {
100.0
} else {
let extracted = self.bytes_extracted.load(Ordering::Relaxed);
(extracted as f64 / self.total_bytes as f64) * 100.0
}
}
fn print_progress(&self) {
let percentage = self.percentage();
let bar_width = 40;
let filled = ((percentage / 100.0) * bar_width as f64) as usize;
let empty = bar_width - filled;
let entries = self.entries_processed.load(Ordering::Relaxed);
let current = self.current_entry.lock().unwrap();
print!(
"\r[{}{}] {:.1}% ({}/{} entries) {}",
"=".repeat(filled),
" ".repeat(empty),
percentage,
entries,
self.total_entries,
current.chars().take(30).collect::<String>()
);
print!("{}", " ".repeat(20));
use std::io::Write;
std::io::stdout().flush().ok();
}
}
impl ProgressReporter for ProgressBarReporter {
fn on_entry_start(&mut self, entry_name: &str, _size: u64) {
if let Ok(mut current) = self.current_entry.lock() {
*current = entry_name.to_string();
}
self.print_progress();
}
fn on_progress(&mut self, bytes_processed: u64, _total_bytes: u64) -> bool {
self.bytes_extracted
.fetch_add(bytes_processed, Ordering::Relaxed);
self.print_progress();
true }
fn on_entry_complete(&mut self, _entry_name: &str, success: bool) {
if success {
self.entries_processed.fetch_add(1, Ordering::Relaxed);
}
self.print_progress();
}
}
struct SimpleProgress {
count: usize,
}
impl SimpleProgress {
fn new() -> Self {
Self { count: 0 }
}
}
impl ProgressReporter for SimpleProgress {
fn on_entry_start(&mut self, entry_name: &str, size: u64) {
println!("Starting: {} ({} bytes)", entry_name, size);
}
fn on_progress(&mut self, bytes_processed: u64, total_bytes: u64) -> bool {
if total_bytes > 0 {
let percent = (bytes_processed as f64 / total_bytes as f64) * 100.0;
print!("\r Progress: {:.0}%", percent);
use std::io::Write;
std::io::stdout().flush().ok();
}
true }
fn on_entry_complete(&mut self, entry_name: &str, success: bool) {
self.count += 1;
let status = if success { "OK" } else { "FAILED" };
println!("\r [{}] {} - {}", self.count, entry_name, status);
}
}
fn main() -> Result<()> {
let args: Vec<String> = env::args().collect();
if args.len() < 3 {
eprintln!("Usage: {} <archive.7z> <output_dir> [--simple]", args[0]);
eprintln!();
eprintln!("Options:");
eprintln!(" --simple Use simple progress output instead of progress bar");
eprintln!();
eprintln!("Examples:");
eprintln!(" {} archive.7z ./output", args[0]);
eprintln!(" {} archive.7z ./output --simple", args[0]);
std::process::exit(1);
}
let archive_path = &args[1];
let output_dir = &args[2];
let use_simple = args.get(3).is_some_and(|a| a == "--simple");
println!("Opening archive: {}", archive_path);
let mut archive = Archive::open_path(archive_path)?;
let total_bytes: u64 = archive.entries().iter().map(|e| e.size).sum();
let total_entries = archive.len() as u64;
println!("Archive info:");
println!(" Entries: {}", total_entries);
println!(" Total size: {} bytes", total_bytes);
println!();
if use_simple {
println!("Extracting with simple progress...");
println!();
let progress = SimpleProgress::new();
let options = ExtractOptions::new().progress(progress);
let result = archive.extract(output_dir, (), &options)?;
println!();
println!("Extraction complete!");
println!(" Extracted: {} entries", result.entries_extracted);
println!(" Failed: {} entries", result.entries_failed);
} else {
println!("Extracting with progress bar...");
println!();
let progress = ProgressBarReporter::new(total_bytes, total_entries);
let options = ExtractOptions::new().progress(progress);
let result = archive.extract(output_dir, (), &options)?;
println!();
println!();
println!("Extraction complete!");
println!(" Extracted: {} entries", result.entries_extracted);
println!(" Bytes: {} bytes", result.bytes_extracted);
println!(" Failed: {} entries", result.entries_failed);
}
Ok(())
}