i6-pack 0.1.13

A tool to compress and optionally encrypt files
Documentation
use std::fs::{self, File};
use std::io::{self, Write};
use std::path::Path;
use tar::Builder;
use walkdir::WalkDir;
use zstd::stream::{decode_all, encode_all};
use zstd::Encoder;

pub fn create_tar_archive<P: AsRef<Path>>(
  folder: P,
  tar_file: &str,
) -> io::Result<()> {
  let tar_gz = File::create(tar_file)?;
  let mut archive = Builder::new(tar_gz);

  for entry in WalkDir::new(folder) {
    let entry = match entry {
      Ok(e) => e,
      Err(e) => {
        eprintln!("Warning: failed to read directory entry: {}", e);
        continue;
      }
    };
    let path = entry.path();
    if path.is_symlink() {
      match std::fs::read_link(path) {
        Ok(target) => {
          if target.exists() {
            if let Err(e) = archive.append_path_with_name(&target, path) {
              eprintln!("Warning: failed to append symlink {:?}: {}", path, e);
            }
          } else {
            eprintln!(
              "Warning: symlink target {:?} does not exist, skipping",
              target
            );
          }
        }
        Err(e) => {
          eprintln!(
            "Warning: failed to read symlink {:?}, skipping: {}",
            path, e
          );
        }
      }
    } else if path.exists() {
      if let Err(e) = archive.append_path(path) {
        eprintln!("Warning: failed to append path {:?}: {}", path, e);
      }
    } else {
      eprintln!("Warning: file {:?} does not exist, skipping", path);
    }
  }

  Ok(())
}

pub fn extract_tar_archive(tar_file: &str, output_dir: &str) -> io::Result<()> {
  let tar_gz = File::open(tar_file)?;
  let mut archive = tar::Archive::new(tar_gz);

  let mut final_output_dir = output_dir.to_string();
  if Path::new(output_dir).exists() {
    final_output_dir = format!("{}-{}", output_dir, uuid::Uuid::new_v4());
  }

  std::fs::create_dir_all(&final_output_dir)?;
  archive.unpack(&final_output_dir)?;
  Ok(())
}

pub fn compress_tar_file(
  tar_file: &str,
  compressed_file: &str,
) -> io::Result<()> {
  let tar = File::open(tar_file)?;
  let compressed = File::create(compressed_file)?;
  let mut tar_reader = tar;
  let mut compressed_writer = compressed;
  let compression_level = 18;

  let mut zstd = zstd::stream::write::Encoder::new(
    &mut compressed_writer,
    compression_level,
  )?;
  zstd.multithread(num_cpus::get() as u32)?;

  zstd.long_distance_matching(true)?;
  zstd.window_log(31)?;

  io::copy(&mut tar_reader, &mut zstd)?;
  zstd.finish()?;

  Ok(())
}

pub fn decompress_file_v1(
  compressed_file: &str,
  output_file: &str,
) -> io::Result<()> {
  let compressed = File::open(compressed_file)?;
  let decompressed_data = decode_all(compressed)?;
  let mut decompressed = File::create(output_file)?;
  decompressed.write_all(&decompressed_data)?;
  Ok(())
}

pub fn decompress_file(
  compressed_file: &str,
  output_file: &str,
) -> io::Result<()> {
  let compressed = File::open(compressed_file)?;
  let mut compressed_reader = compressed;
  let mut decompressed_writer = File::create(output_file)?;

  let mut zstd = zstd::stream::read::Decoder::new(&mut compressed_reader)?;
  zstd.window_log_max(31)?;

  io::copy(&mut zstd, &mut decompressed_writer)?;

  Ok(())
}