mirror of
https://github.com/ouch-org/ouch.git
synced 2025-06-06 11:35:45 +00:00
Docs improvements
+ Removed some dead code
This commit is contained in:
parent
ebe3918478
commit
d2d4a929e1
@ -71,7 +71,6 @@ where
|
||||
FinalError::with_title("Could not create archive")
|
||||
.detail("Unexpected error while trying to read file")
|
||||
.detail(format!("Error: {}.", err))
|
||||
.into_owned()
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
11
src/cli.rs
11
src/cli.rs
@ -1,6 +1,4 @@
|
||||
//! CLI configuration step, uses definitions from `opts.rs`.
|
||||
//!
|
||||
//! Also used to treat some inputs.
|
||||
//! CLI related functions, uses the clap argparsing definitions from `opts.rs`.
|
||||
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
@ -13,8 +11,11 @@ use fs_err as fs;
|
||||
use crate::{Error, Opts, QuestionPolicy, Subcommand};
|
||||
|
||||
impl Opts {
|
||||
/// A helper method that calls `clap::Parser::parse` and then translates relative paths to absolute.
|
||||
/// Also determines if the user wants to skip questions or not
|
||||
/// A helper method that calls `clap::Parser::parse`.
|
||||
///
|
||||
/// And:
|
||||
/// 1. Make paths absolute.
|
||||
/// 2. Checks the QuestionPolicy.
|
||||
pub fn parse_args() -> crate::Result<(Self, QuestionPolicy)> {
|
||||
let mut opts: Self = Self::parse();
|
||||
|
||||
|
@ -37,7 +37,7 @@ fn represents_several_files(files: &[PathBuf]) -> bool {
|
||||
}
|
||||
|
||||
/// Entrypoint of ouch, receives cli options and matches Subcommand
|
||||
/// to decide current operation
|
||||
/// to decide what to do
|
||||
pub fn run(args: Opts, question_policy: QuestionPolicy) -> crate::Result<()> {
|
||||
match args.cmd {
|
||||
Subcommand::Compress { files, output: output_path } => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Pretty (and colored) dialog for asking [Y/n] for the end user.
|
||||
//!
|
||||
//! Example:
|
||||
//! "Do you want to overwrite 'archive.targz'? [Y/n]"
|
||||
//! "Do you want to overwrite 'archive.tar.gz'? [Y/n]"
|
||||
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
@ -10,19 +10,21 @@ use std::{
|
||||
|
||||
use crate::utils::colors;
|
||||
|
||||
/// Represents a confirmation dialog
|
||||
/// Confirmation dialog for end user with [Y/n] question.
|
||||
///
|
||||
/// If the placeholder is found in the prompt text, it will be replaced to form the final message.
|
||||
pub struct Confirmation<'a> {
|
||||
/// Represents the message to the displayed
|
||||
/// The message to be displayed with the placeholder text in it.
|
||||
/// e.g.: "Do you want to overwrite 'FILE'?"
|
||||
pub prompt: &'a str,
|
||||
|
||||
/// Represents a placeholder to be changed at runtime
|
||||
/// The placeholder text that will be replaced in the `ask` function:
|
||||
/// e.g.: Some("FILE")
|
||||
pub placeholder: Option<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> Confirmation<'a> {
|
||||
/// New Confirmation
|
||||
/// Creates a new Confirmation.
|
||||
pub const fn new(prompt: &'a str, pattern: Option<&'a str>) -> Self {
|
||||
Self { prompt, placeholder: pattern }
|
||||
}
|
||||
@ -35,20 +37,17 @@ impl<'a> Confirmation<'a> {
|
||||
(Some(placeholder), Some(subs)) => Cow::Owned(self.prompt.replace(placeholder, subs)),
|
||||
};
|
||||
|
||||
// Ask the same question to end while no valid answers are given
|
||||
loop {
|
||||
print!("{} [{}Y{}/{}n{}] ", message, *colors::GREEN, *colors::RESET, *colors::RED, *colors::RESET);
|
||||
io::stdout().flush()?;
|
||||
|
||||
let mut answer = String::new();
|
||||
io::stdin().read_line(&mut answer)?;
|
||||
let trimmed_answer = answer.trim();
|
||||
|
||||
if trimmed_answer.is_empty() {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
match trimmed_answer.to_ascii_lowercase().as_ref() {
|
||||
"y" | "yes" => return Ok(true),
|
||||
answer.make_ascii_lowercase();
|
||||
match answer.trim() {
|
||||
"" | "y" | "yes" => return Ok(true),
|
||||
"n" | "no" => return Ok(false),
|
||||
_ => continue, // Try again
|
||||
}
|
||||
|
60
src/error.rs
60
src/error.rs
@ -1,12 +1,6 @@
|
||||
//! Error type definitions.
|
||||
//! Error types definitions.
|
||||
//!
|
||||
//! All the unexpected user-side errors should be treated in this file, that does not include
|
||||
//! errors made by devs in our implementation.
|
||||
//!
|
||||
//! TODO: wrap `FinalError` in a variant to keep all `FinalError::display_and_crash()` function
|
||||
//! calls inside of this module.
|
||||
|
||||
#![allow(missing_docs)]
|
||||
//! All usage errors will pass throught the Error enum, a lot of them in the Error::Custom.
|
||||
|
||||
use std::{
|
||||
fmt::{self, Display},
|
||||
@ -15,32 +9,64 @@ use std::{
|
||||
|
||||
use crate::utils::colors::*;
|
||||
|
||||
/// Custom Ouch Errors
|
||||
/// All errors that can be generated by `ouch`
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
/// Extension found is not supported and known to ouch
|
||||
UnknownExtensionError(String),
|
||||
/// TO BE REMOVED
|
||||
MissingExtensionError(PathBuf),
|
||||
IoError { reason: String },
|
||||
/// Not every IoError, some of them get filtered by `From<io::Error>` into other variants
|
||||
IoError {
|
||||
/// TODO
|
||||
reason: String,
|
||||
},
|
||||
/// Detected from io::Error if .kind() is io::ErrorKind::NotFound
|
||||
FileNotFound(PathBuf),
|
||||
/// TO BE REMOVED
|
||||
AlreadyExists,
|
||||
/// TO BE REMOVED
|
||||
InvalidZipArchive(&'static str),
|
||||
PermissionDenied { error_title: String },
|
||||
/// Detected from io::Error if .kind() is io::ErrorKind::PermissionDenied
|
||||
PermissionDenied {
|
||||
/// TODO
|
||||
error_title: String,
|
||||
},
|
||||
/// TO BE REMOVED
|
||||
UnsupportedZipArchive(&'static str),
|
||||
/// TO BE REMOVED
|
||||
InternalError,
|
||||
/// TO BE REMOVED
|
||||
CompressingRootFolder,
|
||||
/// TO BE REMOVED
|
||||
MissingArgumentsForCompression,
|
||||
/// TO BE REMOVED
|
||||
MissingArgumentsForDecompression,
|
||||
/// TO BE REMOVED
|
||||
CompressionTypo,
|
||||
WalkdirError { reason: String },
|
||||
Custom { reason: FinalError },
|
||||
/// Specialized walkdir's io::Error wrapper with additional information on the error
|
||||
WalkdirError {
|
||||
/// TODO
|
||||
reason: String,
|
||||
},
|
||||
/// Custom and unique errors are reported in this variant
|
||||
Custom {
|
||||
/// TODO
|
||||
reason: FinalError,
|
||||
},
|
||||
}
|
||||
|
||||
/// Alias to std's Result with ouch's Error
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Pretty final error message for end users, crashing the program after display.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct FinalError {
|
||||
/// Should be made of just one line, appears after the "[ERROR]" part
|
||||
title: String,
|
||||
/// Shown as a unnumbered list in yellow
|
||||
details: Vec<String>,
|
||||
/// Shown as green at the end to give hints on how to work around this error, if it's fixable
|
||||
hints: Vec<String>,
|
||||
}
|
||||
|
||||
@ -68,23 +94,22 @@ impl Display for FinalError {
|
||||
}
|
||||
|
||||
impl FinalError {
|
||||
/// Only constructor
|
||||
pub fn with_title(title: impl ToString) -> Self {
|
||||
Self { title: title.to_string(), details: vec![], hints: vec![] }
|
||||
}
|
||||
|
||||
/// Add one detail line, can have multiple
|
||||
pub fn detail(mut self, detail: impl ToString) -> Self {
|
||||
self.details.push(detail.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Add one hint line, can have multiple
|
||||
pub fn hint(mut self, hint: impl ToString) -> Self {
|
||||
self.hints.push(hint.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn into_owned(&mut self) -> Self {
|
||||
std::mem::take(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
@ -151,6 +176,7 @@ impl fmt::Display for Error {
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// TO BE REMOVED
|
||||
pub fn with_reason(reason: FinalError) -> Self {
|
||||
Self::Custom { reason }
|
||||
}
|
||||
|
@ -7,7 +7,9 @@ use self::CompressionFormat::*;
|
||||
/// A wrapper around `CompressionFormat` that allows combinations like `tgz`
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Extension {
|
||||
/// One extension like "tgz" can be made of multiple CompressionFormats ([Tar, Gz])
|
||||
pub compression_formats: Vec<CompressionFormat>,
|
||||
/// The input text for this extension, like "tgz", "tar" or "xz"
|
||||
pub display_text: String,
|
||||
}
|
||||
|
||||
@ -26,6 +28,7 @@ impl Extension {
|
||||
self.compression_formats[0].is_archive_format()
|
||||
}
|
||||
|
||||
/// Iteration to inner compression formats, useful for flat_mapping
|
||||
pub fn iter(&self) -> impl Iterator<Item = &CompressionFormat> {
|
||||
self.compression_formats.iter()
|
||||
}
|
||||
@ -37,16 +40,21 @@ impl fmt::Display for Extension {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
/// Accepted extensions for input and output
|
||||
pub enum CompressionFormat {
|
||||
Gzip, // .gz
|
||||
Bzip, // .bz
|
||||
Lzma, // .lzma
|
||||
Tar, // .tar (technically not a compression extension, but will do for now)
|
||||
Zstd, // .zst
|
||||
Zip, // .zip
|
||||
/// .gz
|
||||
Gzip,
|
||||
/// .bz .bz2
|
||||
Bzip,
|
||||
/// .xz .lzma .lz
|
||||
Lzma,
|
||||
/// tar, tgz, tbz, tbz2, txz, tlz, tlzma, tzst
|
||||
Tar,
|
||||
/// .zst
|
||||
Zstd,
|
||||
/// .zip
|
||||
Zip,
|
||||
}
|
||||
|
||||
impl CompressionFormat {
|
||||
|
10
src/lib.rs
10
src/lib.rs
@ -1,8 +1,4 @@
|
||||
//! This library is meant to be published, just used internally by our binary crate at `main.rs`.
|
||||
//!
|
||||
//! A module shall be public only if:
|
||||
//! 1. It's required by `main.rs`, or
|
||||
//! 2. It's required by some integration tests at tests/ folder.
|
||||
//! This library isn't meant to be published, but used internally by our binary crate `main.rs`.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
@ -17,12 +13,12 @@ pub mod error;
|
||||
pub mod extension;
|
||||
pub mod utils;
|
||||
|
||||
/// CLI configuration step, uses definitions from `opts.rs`, also used to treat some inputs.
|
||||
/// CLI argparsing definitions, using `clap`.
|
||||
pub mod opts;
|
||||
|
||||
pub use error::{Error, Result};
|
||||
pub use opts::{Opts, Subcommand};
|
||||
pub use utils::QuestionPolicy;
|
||||
|
||||
/// The status code ouch has when an error is encountered
|
||||
/// The status code returned from `ouch` on error
|
||||
pub const EXIT_FAILURE: i32 = libc::EXIT_FAILURE;
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Macros used on ouch.
|
||||
|
||||
/// Macro that prints message in INFO mode
|
||||
/// Macro that prints [INFO] messages, wraps [`println`].
|
||||
#[macro_export]
|
||||
macro_rules! info {
|
||||
($($arg:tt)*) => {
|
||||
@ -9,7 +9,7 @@ macro_rules! info {
|
||||
};
|
||||
}
|
||||
|
||||
/// Prints the `[Info]` tag
|
||||
/// Helper to display "[INFO]", colored yellow
|
||||
pub fn _info_helper() {
|
||||
use crate::utils::colors::{RESET, YELLOW};
|
||||
|
||||
|
30
src/opts.rs
30
src/opts.rs
@ -6,41 +6,51 @@ use std::path::PathBuf;
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(version, about)]
|
||||
pub struct Opts {
|
||||
/// Skip overwrite questions positively.
|
||||
/// Skip [Y/n] questions positively.
|
||||
#[clap(short, long, conflicts_with = "no")]
|
||||
pub yes: bool,
|
||||
|
||||
/// Skip overwrite questions negatively.
|
||||
/// Skip [Y/n] questions negatively.
|
||||
#[clap(short, long)]
|
||||
pub no: bool,
|
||||
|
||||
/// Action to take
|
||||
/// Ouch and claps subcommands
|
||||
#[clap(subcommand)]
|
||||
pub cmd: Subcommand,
|
||||
}
|
||||
|
||||
/// Actions to take
|
||||
// CAREFUL: this docs can accidentally become part of the --help message if they get too long
|
||||
// this was tested in clap 3.0.0-beta5.
|
||||
/// Repository: https://github.com/ouch-org/ouch
|
||||
//
|
||||
// Ouch commands:
|
||||
// - `compress`
|
||||
// - `decompress`
|
||||
// - `list`
|
||||
//
|
||||
// Clap commands:
|
||||
// - `help`
|
||||
#[derive(Parser, PartialEq, Eq, Debug)]
|
||||
pub enum Subcommand {
|
||||
/// Compress files. Alias: c
|
||||
/// Compress one or more files into one output file.
|
||||
#[clap(alias = "c")]
|
||||
Compress {
|
||||
/// Files to be compressed
|
||||
/// Files to be compressed.
|
||||
#[clap(required = true, min_values = 1)]
|
||||
files: Vec<PathBuf>,
|
||||
|
||||
/// The resulting file. Its extensions specify how the files will be compressed and they need to be supported
|
||||
/// The resulting file. It's extensions can be used to specify the compression formats.
|
||||
#[clap(required = true, value_hint = ValueHint::FilePath)]
|
||||
output: PathBuf,
|
||||
},
|
||||
/// Compress files. Alias: d
|
||||
/// Decompresses one or more files, optionally into another folder.
|
||||
#[clap(alias = "d")]
|
||||
Decompress {
|
||||
/// Files to be decompressed
|
||||
/// Files to be decompressed.
|
||||
#[clap(required = true, min_values = 1)]
|
||||
files: Vec<PathBuf>,
|
||||
|
||||
/// Decompress files in a directory other than the current
|
||||
/// Choose to files in a directory other than the current
|
||||
#[clap(short, long = "dir", value_hint = ValueHint::DirPath)]
|
||||
output_dir: Option<PathBuf>,
|
||||
},
|
||||
|
41
src/utils.rs
41
src/utils.rs
@ -1,4 +1,4 @@
|
||||
//! Utils used on ouch.
|
||||
//! Random stuff used on ouch.
|
||||
|
||||
use std::{
|
||||
cmp, env,
|
||||
@ -11,7 +11,7 @@ use fs_err as fs;
|
||||
|
||||
use crate::{dialogs::Confirmation, info};
|
||||
|
||||
/// Checks if the given path represents an empty directory.
|
||||
/// Checks given path points to an empty directory.
|
||||
pub fn dir_is_empty(dir_path: &Path) -> bool {
|
||||
let is_empty = |mut rd: std::fs::ReadDir| rd.next().is_none();
|
||||
|
||||
@ -37,21 +37,18 @@ pub fn strip_cur_dir(source_path: &Path) -> PathBuf {
|
||||
.unwrap_or_else(|_| source_path.to_path_buf())
|
||||
}
|
||||
|
||||
/// Changes the process' current directory to the directory that contains the
|
||||
/// file pointed to by `filename` and returns the directory that the process
|
||||
/// was in before this function was called.
|
||||
/// Returns current directory, but before change the process' directory to the
|
||||
/// one that contains the file pointed to by `filename`.
|
||||
pub fn cd_into_same_dir_as(filename: &Path) -> crate::Result<PathBuf> {
|
||||
let previous_location = env::current_dir()?;
|
||||
|
||||
let parent = filename.parent().ok_or(crate::Error::CompressingRootFolder)?;
|
||||
|
||||
env::set_current_dir(parent)?;
|
||||
|
||||
Ok(previous_location)
|
||||
}
|
||||
|
||||
/// Centralizes the decision of overwriting a file or not,
|
||||
/// whether the user has already passed a question_policy or not.
|
||||
/// Check if QuestionPolicy flags were set, otherwise, ask user if they want to overwrite.
|
||||
pub fn user_wants_to_overwrite(path: &Path, question_policy: QuestionPolicy) -> crate::Result<bool> {
|
||||
match question_policy {
|
||||
QuestionPolicy::AlwaysYes => Ok(true),
|
||||
@ -65,13 +62,17 @@ pub fn user_wants_to_overwrite(path: &Path, question_policy: QuestionPolicy) ->
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts an OsStr to utf8.
|
||||
/// Converts an OsStr to utf8 with custom formatting.
|
||||
///
|
||||
/// This is different from [`Path::display`].
|
||||
///
|
||||
/// See https://gist.github.com/marcospb19/ebce5572be26397cf08bbd0fd3b65ac1 for a comparison.
|
||||
pub fn to_utf(os_str: impl AsRef<OsStr>) -> String {
|
||||
let text = format!("{:?}", os_str.as_ref());
|
||||
text.trim_matches('"').to_string()
|
||||
}
|
||||
|
||||
/// Treats weird paths for better user messages.
|
||||
/// Display the directory name, but change to "current directory" when necessary.
|
||||
pub fn nice_directory_display(os_str: impl AsRef<OsStr>) -> String {
|
||||
let text = to_utf(os_str);
|
||||
if text == "." {
|
||||
@ -81,11 +82,6 @@ pub fn nice_directory_display(os_str: impl AsRef<OsStr>) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
/// Struct used to overload functionality onto Byte presentation.
|
||||
pub struct Bytes {
|
||||
bytes: f64,
|
||||
}
|
||||
|
||||
/// Module with a list of bright colors.
|
||||
#[allow(dead_code)]
|
||||
pub mod colors {
|
||||
@ -116,10 +112,15 @@ pub mod colors {
|
||||
color!(YELLOW = "\u{1b}[38;5;11m");
|
||||
}
|
||||
|
||||
/// Struct useful to printing bytes as kB, MB, GB, etc.
|
||||
pub struct Bytes {
|
||||
bytes: f64,
|
||||
}
|
||||
|
||||
impl Bytes {
|
||||
const UNIT_PREFIXES: [&'static str; 6] = ["", "k", "M", "G", "T", "P"];
|
||||
|
||||
/// New Byte structure
|
||||
/// Create a new Bytes.
|
||||
pub fn new(bytes: u64) -> Self {
|
||||
Self { bytes: bytes as f64 }
|
||||
}
|
||||
@ -141,13 +142,13 @@ impl std::fmt::Display for Bytes {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
/// How overwrite questions should be handled
|
||||
/// Determines if overwrite questions should be skipped or asked to the user
|
||||
pub enum QuestionPolicy {
|
||||
/// Ask everytime
|
||||
/// Ask the user every time
|
||||
Ask,
|
||||
/// Skip overwrite questions positively
|
||||
/// Set by `--yes`, will say 'Y' to all overwrite questions
|
||||
AlwaysYes,
|
||||
/// Skip overwrite questions negatively
|
||||
/// Set by `--no`, will say 'N' to all overwrite questions
|
||||
AlwaysNo,
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user