🦀/100 Projects/Notes/Source

src/main.rs

View on GitHub
use std::fs::File;
use std::io::{self, BufReader, BufWriter, Read, Write};
use std::path::{Path, PathBuf};
use flate2::write::GzEncoder;
use flate2::read::GzDecoder;
use flate2::Compression;

#[derive(Debug)]
enum CompressionError {
    Io(io::Error),
    InvalidChoice,
    FileNotFound,
    InvalidExtension,
    CompressionFailed,
    DecompressionFailed,
}

impl std::fmt::Display for CompressionError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            CompressionError::Io(e) => write!(f, "I/O error: {}", e),
            CompressionError::InvalidChoice => write!(f, "Invalid choice"),
            CompressionError::FileNotFound => write!(f, "Source file not found"),
            CompressionError::InvalidExtension => write!(f, "Invalid file extension"),
            CompressionError::CompressionFailed => write!(f, "Compression failed"),
            CompressionError::DecompressionFailed => write!(f, "Decompression failed"),
        }
    }
}

impl From<io::Error> for CompressionError {
    fn from(error: io::Error) -> Self {
        CompressionError::Io(error)
    }
}

fn main() {
    if let Err(e) = run() {
        eprintln!("❌ Error: {}", e);
        std::process::exit(1);
    }
}

fn run() -> Result<(), CompressionError> {
    println!("🗜️ File Compression Tool");
    println!("1. Compress a file");
    println!("2. Decompress a file");

    let choice = input("Enter your choice: ").trim().to_string();

    match choice.as_str() {
        "1" => handle_compression()?,
        "2" => handle_decompression()?,
        _ => return Err(CompressionError::InvalidChoice),
    }

    Ok(())
}

fn handle_compression() -> Result<(), CompressionError> {
    let src = get_valid_path("Source file path: ", false)?;
    let dest = get_valid_path("Output .gz file path: ", true)?;
    
    compress_file(&src, &dest)?;
    println!("✅ File compressed to '{}'", dest.display());
    Ok(())
}

fn handle_decompression() -> Result<(), CompressionError> {
    let src = get_valid_path("Source .gz file path: ", false)?;
    let dest = get_valid_path("Output decompressed file path: ", true)?;
    
    decompress_file(&src, &dest)?;
    println!("✅ File decompressed to '{}'", dest.display());
    Ok(())
}

fn compress_file(input_path: &Path, output_path: &Path) -> Result<(), CompressionError> {
    let input = File::open(input_path).map_err(|_| CompressionError::FileNotFound)?;
    let reader = BufReader::new(input);

    let output = File::create(output_path)?;
    let writer = BufWriter::new(output);

    let mut encoder = GzEncoder::new(writer, Compression::default());
    let mut buffer = Vec::new();

    BufReader::new(reader).read_to_end(&mut buffer)?;
    encoder.write_all(&buffer)?;
    encoder.finish()?;

    Ok(())
}

fn decompress_file(input_path: &Path, output_path: &Path) -> Result<(), CompressionError> {
    if input_path.extension().and_then(|s| s.to_str()) != Some("gz") {
        return Err(CompressionError::InvalidExtension);
    }

    let input = File::open(input_path).map_err(|_| CompressionError::FileNotFound)?;
    let mut decoder = GzDecoder::new(BufReader::new(input));

    let output = File::create(output_path)?;
    let mut writer = BufWriter::new(output);

    let mut buffer = Vec::new();
    decoder.read_to_end(&mut buffer)?;
    writer.write_all(&buffer)?;

    Ok(())
}

fn input(prompt: &str) -> String {
    print!("{}", prompt);
    io::stdout().flush().unwrap();
    let mut line = String::new();
    io::stdin().read_line(&mut line).unwrap();
    line.trim().to_string()
}

fn get_valid_path(prompt: &str, is_output: bool) -> Result<PathBuf, CompressionError> {
    let path_str = input(prompt);
    let path = PathBuf::from(&path_str);

    if !is_output && !path.exists() {
        return Err(CompressionError::FileNotFound);
    }

    Ok(path)
}

← Back to folder