oxiarc-cli 0.3.2

Command-line interface for OxiArc archive operations
use crate::style::Styler;
use oxiarc_archive::{ArchiveFormat, CabReader, IsoReader, SevenZReader, ZipReader};
use std::fs::File;
use std::io::{BufReader, Seek, SeekFrom};
use std::path::PathBuf;

pub fn cmd_info(archive: &PathBuf, styler: &Styler) -> Result<(), Box<dyn std::error::Error>> {
    let file = File::open(archive)?;
    let mut reader = BufReader::new(file);

    let (format, _) = ArchiveFormat::detect(&mut reader)?;
    let metadata = std::fs::metadata(archive)?;

    println!("{}", styler.header("Archive Information"));
    println!("{}", styler.header("==================="));
    println!("File: {}", styler.path(&archive.display().to_string()));
    println!("Format: {}", format);
    println!(
        "Size: {}",
        styler.size(&format!("{} bytes", metadata.len()))
    );
    println!("MIME type: {}", format.mime_type());

    reader.seek(SeekFrom::Start(0))?;

    match format {
        ArchiveFormat::Zip => {
            let zip = ZipReader::new(reader)?;
            let entries = zip.entries();
            let total_size: u64 = entries.iter().map(|e| e.size).sum();
            let total_compressed: u64 = entries.iter().map(|e| e.compressed_size).sum();

            println!();
            println!("{}", styler.header("Contents:"));
            println!(
                "  Files: {}",
                entries.iter().filter(|e| e.is_file()).count()
            );
            println!(
                "  Directories: {}",
                entries.iter().filter(|e| e.is_dir()).count()
            );
            println!(
                "  Total size: {}",
                styler.size(&format!("{total_size} bytes"))
            );
            println!(
                "  Compressed size: {}",
                styler.size(&format!("{total_compressed} bytes"))
            );
            if total_size > 0 {
                println!(
                    "  Compression ratio: {:.1}%",
                    (1.0 - total_compressed as f64 / total_size as f64) * 100.0
                );
            }
        }
        ArchiveFormat::Gzip => {
            let gzip = oxiarc_archive::GzipReader::new(reader)?;
            let header = gzip.header();

            println!();
            println!("{}", styler.header("GZIP Header:"));
            if let Some(name) = &header.filename {
                println!("  Original filename: {}", styler.path(name));
            }
            if header.mtime > 0 {
                println!("  Modification time: {} (Unix timestamp)", header.mtime);
            }
        }
        ArchiveFormat::SevenZip => {
            let sevenz = SevenZReader::new(reader)?;
            let entries = sevenz.entries();
            let total_size: u64 = entries.iter().map(|e| e.size).sum();

            println!();
            println!("{}", styler.header("Contents:"));
            println!(
                "  Files: {}",
                entries.iter().filter(|e| e.is_file()).count()
            );
            println!(
                "  Directories: {}",
                entries.iter().filter(|e| e.is_dir()).count()
            );
            println!(
                "  Total size: {}",
                styler.size(&format!("{total_size} bytes"))
            );
        }
        ArchiveFormat::Cab => {
            let cab = CabReader::new(reader)?;
            let (major, minor) = cab.version();
            let entries = cab.entries();
            let total_size: u64 = entries.iter().map(|e| e.size).sum();

            println!();
            println!("{}", styler.header("Cabinet Info:"));
            println!("  Version: {}.{}", major, minor);
            println!("  Folders: {}", cab.num_folders());
            println!(
                "  Cabinet size: {}",
                styler.size(&format!("{} bytes", cab.cabinet_size()))
            );
            println!();
            println!("{}", styler.header("Contents:"));
            println!(
                "  Files: {}",
                entries.iter().filter(|e| e.is_file()).count()
            );
            println!(
                "  Directories: {}",
                entries.iter().filter(|e| e.is_dir()).count()
            );
            println!(
                "  Total size: {}",
                styler.size(&format!("{total_size} bytes"))
            );
        }
        ArchiveFormat::Iso9660 => {
            let iso = IsoReader::new(reader)?;
            let file_count = iso.entries().iter().filter(|e| !e.is_dir).count();
            let dir_count = iso.entries().iter().filter(|e| e.is_dir).count();
            let total_size: u64 = iso
                .entries()
                .iter()
                .filter(|e| !e.is_dir)
                .map(|e| e.size)
                .sum();

            println!();
            println!("{}", styler.header("ISO 9660 Image Info:"));
            println!("  Volume ID: {}", styler.path(iso.volume_id.trim()));
            println!("  Total LBAs: {}", iso.total_lbas);
            println!("  Logical block size: {} bytes", iso.logical_block_size);
            println!(
                "  Joliet extensions: {}",
                if iso.is_joliet() { "yes" } else { "no" }
            );
            println!();
            println!("{}", styler.header("Contents:"));
            println!("  Files: {}", file_count);
            println!("  Directories: {}", dir_count);
            println!(
                "  Total file data: {}",
                styler.size(&format!("{total_size} bytes"))
            );
        }
        _ => {}
    }

    Ok(())
}