use easy_archive::{Fmt, human_size, mode_to_string};
use path_clean::PathClean;
use std::fs::{self};
use std::io;
use std::path::Path;
fn collect_files(input_path: &Path) -> io::Result<Vec<easy_archive::File>> {
let mut files = Vec::new();
let input_path = input_path.clean();
if input_path.is_file() {
let buffer = fs::read(&input_path)?;
let file_name = input_path
.file_name()
.map(|name| name.to_string_lossy().to_string())
.unwrap_or_default();
files.push(easy_archive::File {
path: file_name,
buffer,
is_dir: false,
mode: None,
last_modified: None,
});
return Ok(files);
}
if input_path.is_dir() {
collect_files_recursive(&input_path, &input_path, &mut files)?;
}
Ok(files)
}
fn collect_files_recursive(
base_path: &Path,
current_path: &Path,
files: &mut Vec<easy_archive::File>,
) -> io::Result<()> {
for entry in fs::read_dir(current_path)? {
let entry = entry?;
let path = entry.path();
if !path.is_file() && !path.is_dir() {
continue;
}
let rel_path = path
.strip_prefix(base_path)
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_else(|_| path.to_string_lossy().to_string());
if path.is_dir() {
files.push(easy_archive::File {
path: rel_path.clone(),
buffer: vec![],
is_dir: true,
mode: None,
last_modified: None,
});
collect_files_recursive(base_path, &path, files)?;
} else if path.is_file() {
let buffer = fs::read(&path)?;
files.push(easy_archive::File {
path: rel_path,
buffer,
is_dir: false,
mode: None,
last_modified: None,
});
}
}
Ok(())
}
fn main() {
let mut args = std::env::args().skip(1);
let input = args.next();
let output = args.next();
if input.is_none() || output.is_none() {
println!("usage:\nea <input> <output>");
println!("input and output parameters are required");
return;
}
let input = input.unwrap();
let output = output.unwrap();
let input_fmt = Fmt::guess(&input);
let output_fmt = Fmt::guess(&output);
match (input_fmt, output_fmt) {
(Some(fmt), None) => {
let buffer = std::fs::read(&input).expect("failed to read input file");
let files = fmt.decode(buffer).expect("failed to decode");
let mut info_list = vec![];
let mut total_size = 0;
let file_count = files.len();
for file in &files {
let size = file.buffer.len();
info_list.push((
mode_to_string(file.mode.unwrap_or(0), file.is_dir),
human_size(size),
&file.path,
));
total_size += size;
}
println!("{} of {} files", human_size(total_size), file_count);
println!("decompress to {output}");
for file in &files {
let output_path = Path::new(&output).clean();
let output_path = output_path.join(&file.path).clean();
let dir = output_path.parent().expect("failed to get parent dir");
if !dir.exists() {
std::fs::create_dir_all(dir).expect("failed to create dir");
}
if file.is_dir && !output_path.exists() {
std::fs::create_dir_all(&output_path).expect("failed to create dir");
}
let buffer = &file.buffer;
if !file.is_dir {
std::fs::write(&output_path, buffer).expect("failed to write file");
}
#[cfg(not(windows))]
if let Some(mode) = file.mode {
std::fs::set_permissions(
&output_path,
std::os::unix::fs::PermissionsExt::from_mode(mode),
)
.expect("failed to set permissions");
}
}
}
(None, Some(fmt)) => {
let input_path = Path::new(&input).clean();
if !input_path.exists() {
println!("input file or directory does not exist");
return;
}
let files = collect_files(&input_path).expect("failed to collect files");
let total_size: usize = files.iter().map(|f| f.buffer.len()).sum();
let file_count = files.len();
let buffer = fmt.encode(files).expect("failed to encode files");
std::fs::write(&output, &buffer).expect("failed to write archive");
println!(
"compressed {} files({}) to {}({})",
file_count,
human_size(total_size),
output,
human_size(buffer.len()),
);
}
(Some(_), Some(_)) => {
println!("both input and output are archive formats, please choose one as a directory");
}
(None, None) => {
println!(
"cannot identify input and output formats, at least one must be an archive format"
);
}
}
}