pub mod args;
pub mod extract;
pub mod help;
pub mod inspect;
pub mod pack;
pub mod path;
pub mod ui;
pub mod walk;
use std::{
fmt::Display,
fs::File,
io::{self, BufReader},
};
use zarust::ArchiveReader;
use crate::cli::{
args::{Command, Invocation, UsageError},
ui::Reporter,
};
pub const READ_BUFFER: usize = 256 * 1024;
#[derive(Debug)]
pub struct Failure(pub String);
impl Failure {
pub fn new(message: impl Into<String>) -> Self {
Self(message.into())
}
pub fn path(verb: &str, path: &std::path::Path, error: impl Display) -> Self {
Self(format!(
"cannot {verb} `{}`: {error}",
self::path::display(path)
))
}
}
impl From<io::Error> for Failure {
fn from(error: io::Error) -> Self {
Self(error.to_string())
}
}
impl From<zarust::Error> for Failure {
fn from(error: zarust::Error) -> Self {
Self(error.to_string())
}
}
pub type Outcome = Result<(), Failure>;
pub mod exit {
pub const SUCCESS: u8 = 0;
pub const FAILURE: u8 = 1;
pub const USAGE: u8 = 2;
}
pub fn open_archive(path: &std::path::Path) -> Result<ArchiveReader<BufReader<File>>, Failure> {
let file = File::open(path).map_err(|error| Failure::path("open", path, error))?;
ArchiveReader::new(BufReader::with_capacity(READ_BUFFER, file))
.map_err(|error| Failure::path("read", path, error))
}
pub fn run(invocation: Invocation) -> u8 {
let Invocation { command, options } = invocation;
let mut reporter = Reporter::new(options.color, options.verbosity, options.progress);
let outcome = match command {
Command::Pack { input, output } => pack::run(&mut reporter, &input, output, &options),
Command::Extract { archive, output } => {
extract::run(&mut reporter, &archive, output, &options)
}
Command::List { archive, path } => inspect::list(&mut reporter, &archive, path, &options),
Command::Info { archive } => inspect::info(&mut reporter, &archive),
Command::Verify { archive } => inspect::verify(&mut reporter, &archive),
Command::Help => help::help(&mut reporter),
Command::Version => help::version(&mut reporter),
};
let flushed = reporter.flush();
match outcome.and_then(|()| flushed.map_err(Failure::from)) {
Ok(()) => exit::SUCCESS,
Err(failure) => {
report_error(&failure.0);
exit::FAILURE
}
}
}
pub fn report_usage(error: &UsageError) -> u8 {
report_error(&error.0);
eprintln!("Try `zarust --help` for the list of commands.");
exit::USAGE
}
fn report_error(message: &str) {
let palette = ui::stderr_palette(ui::ColorChoice::Auto);
eprintln!(
"{}{}error{}: {message}",
palette.red, palette.bold, palette.reset
);
}