Docs improvements

+ Removed some dead code
This commit is contained in:
João M. Bezerra 2021-11-02 04:57:26 -03:00
parent ebe3918478
commit d2d4a929e1
10 changed files with 122 additions and 82 deletions

View File

@ -71,7 +71,6 @@ where
FinalError::with_title("Could not create archive") FinalError::with_title("Could not create archive")
.detail("Unexpected error while trying to read file") .detail("Unexpected error while trying to read file")
.detail(format!("Error: {}.", err)) .detail(format!("Error: {}.", err))
.into_owned()
})?; })?;
} }
} }

View File

@ -1,6 +1,4 @@
//! CLI configuration step, uses definitions from `opts.rs`. //! CLI related functions, uses the clap argparsing definitions from `opts.rs`.
//!
//! Also used to treat some inputs.
use std::{ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
@ -13,8 +11,11 @@ use fs_err as fs;
use crate::{Error, Opts, QuestionPolicy, Subcommand}; use crate::{Error, Opts, QuestionPolicy, Subcommand};
impl Opts { impl Opts {
/// A helper method that calls `clap::Parser::parse` and then translates relative paths to absolute. /// A helper method that calls `clap::Parser::parse`.
/// Also determines if the user wants to skip questions or not ///
/// And:
/// 1. Make paths absolute.
/// 2. Checks the QuestionPolicy.
pub fn parse_args() -> crate::Result<(Self, QuestionPolicy)> { pub fn parse_args() -> crate::Result<(Self, QuestionPolicy)> {
let mut opts: Self = Self::parse(); let mut opts: Self = Self::parse();

View File

@ -37,7 +37,7 @@ fn represents_several_files(files: &[PathBuf]) -> bool {
} }
/// Entrypoint of ouch, receives cli options and matches Subcommand /// 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<()> { pub fn run(args: Opts, question_policy: QuestionPolicy) -> crate::Result<()> {
match args.cmd { match args.cmd {
Subcommand::Compress { files, output: output_path } => { Subcommand::Compress { files, output: output_path } => {

View File

@ -1,7 +1,7 @@
//! Pretty (and colored) dialog for asking [Y/n] for the end user. //! Pretty (and colored) dialog for asking [Y/n] for the end user.
//! //!
//! Example: //! Example:
//! "Do you want to overwrite 'archive.targz'? [Y/n]" //! "Do you want to overwrite 'archive.tar.gz'? [Y/n]"
use std::{ use std::{
borrow::Cow, borrow::Cow,
@ -10,19 +10,21 @@ use std::{
use crate::utils::colors; 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> { 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'?" /// e.g.: "Do you want to overwrite 'FILE'?"
pub prompt: &'a str, 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") /// e.g.: Some("FILE")
pub placeholder: Option<&'a str>, pub placeholder: Option<&'a str>,
} }
impl<'a> Confirmation<'a> { impl<'a> Confirmation<'a> {
/// New Confirmation /// Creates a new Confirmation.
pub const fn new(prompt: &'a str, pattern: Option<&'a str>) -> Self { pub const fn new(prompt: &'a str, pattern: Option<&'a str>) -> Self {
Self { prompt, placeholder: pattern } Self { prompt, placeholder: pattern }
} }
@ -35,20 +37,17 @@ impl<'a> Confirmation<'a> {
(Some(placeholder), Some(subs)) => Cow::Owned(self.prompt.replace(placeholder, subs)), (Some(placeholder), Some(subs)) => Cow::Owned(self.prompt.replace(placeholder, subs)),
}; };
// Ask the same question to end while no valid answers are given
loop { loop {
print!("{} [{}Y{}/{}n{}] ", message, *colors::GREEN, *colors::RESET, *colors::RED, *colors::RESET); print!("{} [{}Y{}/{}n{}] ", message, *colors::GREEN, *colors::RESET, *colors::RED, *colors::RESET);
io::stdout().flush()?; io::stdout().flush()?;
let mut answer = String::new(); let mut answer = String::new();
io::stdin().read_line(&mut answer)?; io::stdin().read_line(&mut answer)?;
let trimmed_answer = answer.trim();
if trimmed_answer.is_empty() { answer.make_ascii_lowercase();
return Ok(true); match answer.trim() {
} "" | "y" | "yes" => return Ok(true),
match trimmed_answer.to_ascii_lowercase().as_ref() {
"y" | "yes" => return Ok(true),
"n" | "no" => return Ok(false), "n" | "no" => return Ok(false),
_ => continue, // Try again _ => continue, // Try again
} }

View File

@ -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 //! All usage errors will pass throught the Error enum, a lot of them in the Error::Custom.
//! 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)]
use std::{ use std::{
fmt::{self, Display}, fmt::{self, Display},
@ -15,32 +9,64 @@ use std::{
use crate::utils::colors::*; use crate::utils::colors::*;
/// Custom Ouch Errors /// All errors that can be generated by `ouch`
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Error { pub enum Error {
/// Extension found is not supported and known to ouch
UnknownExtensionError(String), UnknownExtensionError(String),
/// TO BE REMOVED
MissingExtensionError(PathBuf), 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), FileNotFound(PathBuf),
/// TO BE REMOVED
AlreadyExists, AlreadyExists,
/// TO BE REMOVED
InvalidZipArchive(&'static str), 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), UnsupportedZipArchive(&'static str),
/// TO BE REMOVED
InternalError, InternalError,
/// TO BE REMOVED
CompressingRootFolder, CompressingRootFolder,
/// TO BE REMOVED
MissingArgumentsForCompression, MissingArgumentsForCompression,
/// TO BE REMOVED
MissingArgumentsForDecompression, MissingArgumentsForDecompression,
/// TO BE REMOVED
CompressionTypo, CompressionTypo,
WalkdirError { reason: String }, /// Specialized walkdir's io::Error wrapper with additional information on the error
Custom { reason: FinalError }, 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>; 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)] #[derive(Clone, Debug, Default, PartialEq)]
pub struct FinalError { pub struct FinalError {
/// Should be made of just one line, appears after the "[ERROR]" part
title: String, title: String,
/// Shown as a unnumbered list in yellow
details: Vec<String>, 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>, hints: Vec<String>,
} }
@ -68,23 +94,22 @@ impl Display for FinalError {
} }
impl FinalError { impl FinalError {
/// Only constructor
pub fn with_title(title: impl ToString) -> Self { pub fn with_title(title: impl ToString) -> Self {
Self { title: title.to_string(), details: vec![], hints: vec![] } 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 { pub fn detail(mut self, detail: impl ToString) -> Self {
self.details.push(detail.to_string()); self.details.push(detail.to_string());
self self
} }
/// Add one hint line, can have multiple
pub fn hint(mut self, hint: impl ToString) -> Self { pub fn hint(mut self, hint: impl ToString) -> Self {
self.hints.push(hint.to_string()); self.hints.push(hint.to_string());
self self
} }
pub fn into_owned(&mut self) -> Self {
std::mem::take(self)
}
} }
impl fmt::Display for Error { impl fmt::Display for Error {
@ -151,6 +176,7 @@ impl fmt::Display for Error {
} }
impl Error { impl Error {
/// TO BE REMOVED
pub fn with_reason(reason: FinalError) -> Self { pub fn with_reason(reason: FinalError) -> Self {
Self::Custom { reason } Self::Custom { reason }
} }

View File

@ -7,7 +7,9 @@ use self::CompressionFormat::*;
/// A wrapper around `CompressionFormat` that allows combinations like `tgz` /// A wrapper around `CompressionFormat` that allows combinations like `tgz`
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct Extension { pub struct Extension {
/// One extension like "tgz" can be made of multiple CompressionFormats ([Tar, Gz])
pub compression_formats: Vec<CompressionFormat>, pub compression_formats: Vec<CompressionFormat>,
/// The input text for this extension, like "tgz", "tar" or "xz"
pub display_text: String, pub display_text: String,
} }
@ -26,6 +28,7 @@ impl Extension {
self.compression_formats[0].is_archive_format() self.compression_formats[0].is_archive_format()
} }
/// Iteration to inner compression formats, useful for flat_mapping
pub fn iter(&self) -> impl Iterator<Item = &CompressionFormat> { pub fn iter(&self) -> impl Iterator<Item = &CompressionFormat> {
self.compression_formats.iter() self.compression_formats.iter()
} }
@ -37,16 +40,21 @@ impl fmt::Display for Extension {
} }
} }
#[allow(missing_docs)]
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
/// Accepted extensions for input and output /// Accepted extensions for input and output
pub enum CompressionFormat { pub enum CompressionFormat {
Gzip, // .gz /// .gz
Bzip, // .bz Gzip,
Lzma, // .lzma /// .bz .bz2
Tar, // .tar (technically not a compression extension, but will do for now) Bzip,
Zstd, // .zst /// .xz .lzma .lz
Zip, // .zip Lzma,
/// tar, tgz, tbz, tbz2, txz, tlz, tlzma, tzst
Tar,
/// .zst
Zstd,
/// .zip
Zip,
} }
impl CompressionFormat { impl CompressionFormat {

View File

@ -1,8 +1,4 @@
//! This library is meant to be published, just used internally by our binary crate at `main.rs`. //! This library isn't meant to be published, but used internally by our binary crate `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.
#![warn(missing_docs)] #![warn(missing_docs)]
@ -17,12 +13,12 @@ pub mod error;
pub mod extension; pub mod extension;
pub mod utils; 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 mod opts;
pub use error::{Error, Result}; pub use error::{Error, Result};
pub use opts::{Opts, Subcommand}; pub use opts::{Opts, Subcommand};
pub use utils::QuestionPolicy; 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; pub const EXIT_FAILURE: i32 = libc::EXIT_FAILURE;

View File

@ -1,6 +1,6 @@
//! Macros used on ouch. //! Macros used on ouch.
/// Macro that prints message in INFO mode /// Macro that prints [INFO] messages, wraps [`println`].
#[macro_export] #[macro_export]
macro_rules! info { macro_rules! info {
($($arg:tt)*) => { ($($arg:tt)*) => {
@ -9,7 +9,7 @@ macro_rules! info {
}; };
} }
/// Prints the `[Info]` tag /// Helper to display "[INFO]", colored yellow
pub fn _info_helper() { pub fn _info_helper() {
use crate::utils::colors::{RESET, YELLOW}; use crate::utils::colors::{RESET, YELLOW};

View File

@ -6,41 +6,51 @@ use std::path::PathBuf;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[clap(version, about)] #[clap(version, about)]
pub struct Opts { pub struct Opts {
/// Skip overwrite questions positively. /// Skip [Y/n] questions positively.
#[clap(short, long, conflicts_with = "no")] #[clap(short, long, conflicts_with = "no")]
pub yes: bool, pub yes: bool,
/// Skip overwrite questions negatively. /// Skip [Y/n] questions negatively.
#[clap(short, long)] #[clap(short, long)]
pub no: bool, pub no: bool,
/// Action to take /// Ouch and claps subcommands
#[clap(subcommand)] #[clap(subcommand)]
pub cmd: 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)] #[derive(Parser, PartialEq, Eq, Debug)]
pub enum Subcommand { pub enum Subcommand {
/// Compress files. Alias: c /// Compress one or more files into one output file.
#[clap(alias = "c")] #[clap(alias = "c")]
Compress { Compress {
/// Files to be compressed /// Files to be compressed.
#[clap(required = true, min_values = 1)] #[clap(required = true, min_values = 1)]
files: Vec<PathBuf>, 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)] #[clap(required = true, value_hint = ValueHint::FilePath)]
output: PathBuf, output: PathBuf,
}, },
/// Compress files. Alias: d /// Decompresses one or more files, optionally into another folder.
#[clap(alias = "d")] #[clap(alias = "d")]
Decompress { Decompress {
/// Files to be decompressed /// Files to be decompressed.
#[clap(required = true, min_values = 1)] #[clap(required = true, min_values = 1)]
files: Vec<PathBuf>, 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)] #[clap(short, long = "dir", value_hint = ValueHint::DirPath)]
output_dir: Option<PathBuf>, output_dir: Option<PathBuf>,
}, },

View File

@ -1,4 +1,4 @@
//! Utils used on ouch. //! Random stuff used on ouch.
use std::{ use std::{
cmp, env, cmp, env,
@ -11,7 +11,7 @@ use fs_err as fs;
use crate::{dialogs::Confirmation, info}; 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 { pub fn dir_is_empty(dir_path: &Path) -> bool {
let is_empty = |mut rd: std::fs::ReadDir| rd.next().is_none(); 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()) .unwrap_or_else(|_| source_path.to_path_buf())
} }
/// Changes the process' current directory to the directory that contains the /// Returns current directory, but before change the process' directory to the
/// file pointed to by `filename` and returns the directory that the process /// one that contains the file pointed to by `filename`.
/// was in before this function was called.
pub fn cd_into_same_dir_as(filename: &Path) -> crate::Result<PathBuf> { pub fn cd_into_same_dir_as(filename: &Path) -> crate::Result<PathBuf> {
let previous_location = env::current_dir()?; let previous_location = env::current_dir()?;
let parent = filename.parent().ok_or(crate::Error::CompressingRootFolder)?; let parent = filename.parent().ok_or(crate::Error::CompressingRootFolder)?;
env::set_current_dir(parent)?; env::set_current_dir(parent)?;
Ok(previous_location) Ok(previous_location)
} }
/// Centralizes the decision of overwriting a file or not, /// Check if QuestionPolicy flags were set, otherwise, ask user if they want to overwrite.
/// whether the user has already passed a question_policy or not.
pub fn user_wants_to_overwrite(path: &Path, question_policy: QuestionPolicy) -> crate::Result<bool> { pub fn user_wants_to_overwrite(path: &Path, question_policy: QuestionPolicy) -> crate::Result<bool> {
match question_policy { match question_policy {
QuestionPolicy::AlwaysYes => Ok(true), 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 { pub fn to_utf(os_str: impl AsRef<OsStr>) -> String {
let text = format!("{:?}", os_str.as_ref()); let text = format!("{:?}", os_str.as_ref());
text.trim_matches('"').to_string() 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 { pub fn nice_directory_display(os_str: impl AsRef<OsStr>) -> String {
let text = to_utf(os_str); let text = to_utf(os_str);
if text == "." { 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. /// Module with a list of bright colors.
#[allow(dead_code)] #[allow(dead_code)]
pub mod colors { pub mod colors {
@ -116,10 +112,15 @@ pub mod colors {
color!(YELLOW = "\u{1b}[38;5;11m"); color!(YELLOW = "\u{1b}[38;5;11m");
} }
/// Struct useful to printing bytes as kB, MB, GB, etc.
pub struct Bytes {
bytes: f64,
}
impl Bytes { impl Bytes {
const UNIT_PREFIXES: [&'static str; 6] = ["", "k", "M", "G", "T", "P"]; const UNIT_PREFIXES: [&'static str; 6] = ["", "k", "M", "G", "T", "P"];
/// New Byte structure /// Create a new Bytes.
pub fn new(bytes: u64) -> Self { pub fn new(bytes: u64) -> Self {
Self { bytes: bytes as f64 } Self { bytes: bytes as f64 }
} }
@ -141,13 +142,13 @@ impl std::fmt::Display for Bytes {
} }
#[derive(Debug, PartialEq, Clone, Copy)] #[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 { pub enum QuestionPolicy {
/// Ask everytime /// Ask the user every time
Ask, Ask,
/// Skip overwrite questions positively /// Set by `--yes`, will say 'Y' to all overwrite questions
AlwaysYes, AlwaysYes,
/// Skip overwrite questions negatively /// Set by `--no`, will say 'N' to all overwrite questions
AlwaysNo, AlwaysNo,
} }