mirror of
https://github.com/ouch-org/ouch.git
synced 2025-06-07 12:05:46 +00:00
Merge branch 'accessibility'
This commit is contained in:
commit
4e0dbeb85b
@ -6,7 +6,7 @@ edition = "2021"
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/ouch-org/ouch"
|
repository = "https://github.com/ouch-org/ouch"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
keywords = ["decompression", "compression", "zip", "tar", "gzip"]
|
keywords = ["decompression", "compression", "zip", "tar", "gzip", "accessibility", "a11y"]
|
||||||
categories = ["command-line-utilities", "compression", "encoding"]
|
categories = ["command-line-utilities", "compression", "encoding"]
|
||||||
description = "A command-line utility for easily compressing and decompressing files and directories."
|
description = "A command-line utility for easily compressing and decompressing files and directories."
|
||||||
|
|
||||||
|
13
README.md
13
README.md
@ -24,12 +24,13 @@
|
|||||||
# Features
|
# Features
|
||||||
|
|
||||||
1. Easy to use.
|
1. Easy to use.
|
||||||
2. Automatic formats detection.
|
2. Accessibility mode (A11Y) via `--accessibility` or `ACCESSIBILITY` env var (see [wiki page](https://github.com/ouch-org/ouch/wiki/Accessibility)).
|
||||||
3. Same usage syntax for all formats.
|
3. Automatic formats detection.
|
||||||
4. Uses encoding and decoding streams to improve performance.
|
4. Same usage syntax for all formats.
|
||||||
5. No runtime dependencies (for _Linux x86_64_).
|
5. Uses encoding and decoding streams to improve performance.
|
||||||
6. Can list archive contents with pretty tree formatting.
|
6. No runtime dependencies (for _Linux x86_64_).
|
||||||
7. Shell completions (soon!).
|
7. Can list archive contents with pretty tree formatting.
|
||||||
|
8. Shell completions (soon!).
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
|
11
install.sh
11
install.sh
@ -7,6 +7,10 @@ DOWNLOAD_LOCATION="/tmp/ouch-binary"
|
|||||||
INSTALLATION_LOCATION="/usr/local/bin/ouch"
|
INSTALLATION_LOCATION="/usr/local/bin/ouch"
|
||||||
REPO_URL="https://github.com/ouch-org/ouch"
|
REPO_URL="https://github.com/ouch-org/ouch"
|
||||||
|
|
||||||
|
# If env var ACCESSIBLE is set (to a nonempty value), suppress output of
|
||||||
|
# `curl` or `wget`
|
||||||
|
|
||||||
|
|
||||||
# Panics script if anything fails
|
# Panics script if anything fails
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
@ -55,12 +59,15 @@ install() {
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Set $downloader
|
# Set $downloader
|
||||||
|
downloader_quiet_flag=""
|
||||||
if [ $(which curl) ]; then
|
if [ $(which curl) ]; then
|
||||||
downloader="curl"
|
downloader="curl"
|
||||||
downloader_command="curl -fSL $binary_url -o $DOWNLOAD_LOCATION"
|
if [ "$ACCESSIBLE" ]; then downloader_quiet_flag="--silent"; fi
|
||||||
|
downloader_command="curl $downloader_quiet_flag -fSL $binary_url -o $DOWNLOAD_LOCATION"
|
||||||
elif [ $(which wget) ]; then
|
elif [ $(which wget) ]; then
|
||||||
downloader="wget"
|
downloader="wget"
|
||||||
downloader_command="wget $binary_url -O $DOWNLOAD_LOCATION"
|
if [ "$ACCESSIBLE" ]; then downloader_quiet_flag="--quiet"; fi
|
||||||
|
downloader_command="wget $downloader_quiet_flag $binary_url -O $DOWNLOAD_LOCATION"
|
||||||
else
|
else
|
||||||
echo "ERROR: have not found 'curl' nor 'wget' to donwload ouch binary."
|
echo "ERROR: have not found 'curl' nor 'wget' to donwload ouch binary."
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -38,7 +38,11 @@ pub fn unpack_archive(
|
|||||||
|
|
||||||
file.unpack_in(output_folder)?;
|
file.unpack_in(output_folder)?;
|
||||||
|
|
||||||
info!("{:?} extracted. ({})", output_folder.join(file.path()?), Bytes::new(file.size()));
|
// This is printed for every file in the archive and has little
|
||||||
|
// importance for most users, but would generate lots of
|
||||||
|
// spoken text for users using screen readers, braille displays
|
||||||
|
// and so on
|
||||||
|
info!(inaccessible, "{:?} extracted. ({})", output_folder.join(file.path()?), Bytes::new(file.size()));
|
||||||
|
|
||||||
files_unpacked.push(file_path);
|
files_unpacked.push(file_path);
|
||||||
}
|
}
|
||||||
@ -80,7 +84,11 @@ where
|
|||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
|
|
||||||
info!("Compressing '{}'.", utils::to_utf(path));
|
// This is printed for every file in `input_filenames` and has
|
||||||
|
// little importance for most users, but would generate lots of
|
||||||
|
// spoken text for users using screen readers, braille displays
|
||||||
|
// and so on
|
||||||
|
info!(inaccessible, "Compressing '{}'.", utils::to_utf(path));
|
||||||
|
|
||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
builder.append_dir(path, path)?;
|
builder.append_dir(path, path)?;
|
||||||
|
@ -48,7 +48,11 @@ where
|
|||||||
|
|
||||||
match (&*file.name()).ends_with('/') {
|
match (&*file.name()).ends_with('/') {
|
||||||
_is_dir @ true => {
|
_is_dir @ true => {
|
||||||
println!("File {} extracted to \"{}\"", idx, file_path.display());
|
// This is printed for every file in the archive and has little
|
||||||
|
// importance for most users, but would generate lots of
|
||||||
|
// spoken text for users using screen readers, braille displays
|
||||||
|
// and so on
|
||||||
|
info!(inaccessible, "File {} extracted to \"{}\"", idx, file_path.display());
|
||||||
fs::create_dir_all(&file_path)?;
|
fs::create_dir_all(&file_path)?;
|
||||||
}
|
}
|
||||||
_is_file @ false => {
|
_is_file @ false => {
|
||||||
@ -59,7 +63,8 @@ where
|
|||||||
}
|
}
|
||||||
let file_path = strip_cur_dir(file_path.as_path());
|
let file_path = strip_cur_dir(file_path.as_path());
|
||||||
|
|
||||||
info!("{:?} extracted. ({})", file_path.display(), Bytes::new(file.size()));
|
// same reason is in _is_dir: long, often not needed text
|
||||||
|
info!(inaccessible, "{:?} extracted. ({})", file_path.display(), Bytes::new(file.size()));
|
||||||
|
|
||||||
let mut output_file = fs::File::create(&file_path)?;
|
let mut output_file = fs::File::create(&file_path)?;
|
||||||
io::copy(&mut file, &mut output_file)?;
|
io::copy(&mut file, &mut output_file)?;
|
||||||
@ -125,7 +130,11 @@ where
|
|||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
|
|
||||||
info!("Compressing '{}'.", to_utf(path));
|
// This is printed for every file in `input_filenames` and has
|
||||||
|
// little importance for most users, but would generate lots of
|
||||||
|
// spoken text for users using screen readers, braille displays
|
||||||
|
// and so on
|
||||||
|
info!(inaccessible, "Compressing '{}'.", to_utf(path));
|
||||||
|
|
||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
if dir_is_empty(path) {
|
if dir_is_empty(path) {
|
||||||
@ -149,7 +158,17 @@ where
|
|||||||
fn check_for_comments(file: &ZipFile) {
|
fn check_for_comments(file: &ZipFile) {
|
||||||
let comment = file.comment();
|
let comment = file.comment();
|
||||||
if !comment.is_empty() {
|
if !comment.is_empty() {
|
||||||
info!("Found comment in {}: {}", file.name(), comment);
|
// Zip file comments seem to be pretty rare, but if they are used,
|
||||||
|
// they may contain important information, so better show them
|
||||||
|
//
|
||||||
|
// "The .ZIP file format allows for a comment containing up to 65,535 (216−1) bytes
|
||||||
|
// of data to occur at the end of the file after the central directory."
|
||||||
|
//
|
||||||
|
// If there happen to be cases of very long and unnecessary comments in
|
||||||
|
// the future, maybe asking the user if he wants to display the comment
|
||||||
|
// (informing him of its size) would be sensible for both normal and
|
||||||
|
// accessibility mode..
|
||||||
|
info!(accessible, "Found comment in {}: {}", file.name(), comment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,9 +8,14 @@ use std::{
|
|||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use fs_err as fs;
|
use fs_err as fs;
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
|
|
||||||
use crate::{Opts, QuestionPolicy, Subcommand};
|
use crate::{Opts, QuestionPolicy, Subcommand};
|
||||||
|
|
||||||
|
/// Whether to enable accessible output (removes info output and reduces other
|
||||||
|
/// output, removes visual markers like '[' and ']')
|
||||||
|
pub static ACCESSIBLE: OnceCell<bool> = OnceCell::new();
|
||||||
|
|
||||||
impl Opts {
|
impl Opts {
|
||||||
/// A helper method that calls `clap::Parser::parse`.
|
/// A helper method that calls `clap::Parser::parse`.
|
||||||
///
|
///
|
||||||
@ -20,6 +25,8 @@ impl Opts {
|
|||||||
pub fn parse_args() -> crate::Result<(Self, QuestionPolicy)> {
|
pub fn parse_args() -> crate::Result<(Self, QuestionPolicy)> {
|
||||||
let mut opts = Self::parse();
|
let mut opts = Self::parse();
|
||||||
|
|
||||||
|
ACCESSIBLE.set(opts.accessible).unwrap();
|
||||||
|
|
||||||
let (Subcommand::Compress { files, .. }
|
let (Subcommand::Compress { files, .. }
|
||||||
| Subcommand::Decompress { files, .. }
|
| Subcommand::Decompress { files, .. }
|
||||||
| Subcommand::List { archives: files, .. }) = &mut opts.cmd;
|
| Subcommand::List { archives: files, .. }) = &mut opts.cmd;
|
||||||
|
@ -148,6 +148,7 @@ pub fn run(args: Opts, question_policy: QuestionPolicy) -> crate::Result<()> {
|
|||||||
// Path::extension says: "if there is no file_name, then there is no extension".
|
// Path::extension says: "if there is no file_name, then there is no extension".
|
||||||
// Contrapositive statement: "if there is extension, then there is file_name".
|
// Contrapositive statement: "if there is extension, then there is file_name".
|
||||||
info!(
|
info!(
|
||||||
|
accessible, // important information
|
||||||
"Partial compression detected. Compressing {} into {}",
|
"Partial compression detected. Compressing {} into {}",
|
||||||
to_utf(files[0].as_path().file_name().unwrap()),
|
to_utf(files[0].as_path().file_name().unwrap()),
|
||||||
to_utf(&output_path)
|
to_utf(&output_path)
|
||||||
@ -168,7 +169,11 @@ pub fn run(args: Opts, question_policy: QuestionPolicy) -> crate::Result<()> {
|
|||||||
eprintln!(" Error:{reset} {}{red}.{reset}\n", err, reset = *colors::RESET, red = *colors::RED);
|
eprintln!(" Error:{reset} {}{red}.{reset}\n", err, reset = *colors::RESET, red = *colors::RED);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
info!("Successfully compressed '{}'.", to_utf(output_path));
|
// this is only printed once, so it doesn't result in much text. On the other hand,
|
||||||
|
// having a final status message is important especially in an accessibility context
|
||||||
|
// as screen readers may not read a commands exit code, making it hard to reason
|
||||||
|
// about whether the command succeeded without such a message
|
||||||
|
info!(accessible, "Successfully compressed '{}'.", to_utf(output_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
compress_result?;
|
compress_result?;
|
||||||
@ -366,8 +371,18 @@ fn decompress_file(
|
|||||||
} else {
|
} else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
info!("Successfully decompressed archive in {}.", nice_directory_display(output_dir));
|
|
||||||
info!("Files unpacked: {}", files.len());
|
// this is only printed once, so it doesn't result in much text. On the other hand,
|
||||||
|
// having a final status message is important especially in an accessibility context
|
||||||
|
// as screen readers may not read a commands exit code, making it hard to reason
|
||||||
|
// about whether the command succeeded without such a message
|
||||||
|
info!(
|
||||||
|
accessible,
|
||||||
|
"Successfully decompressed archive in {} ({} files).",
|
||||||
|
nice_directory_display(output_dir),
|
||||||
|
files.len()
|
||||||
|
);
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,8 +461,12 @@ fn decompress_file(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Successfully decompressed archive in {}.", nice_directory_display(output_dir));
|
// this is only printed once, so it doesn't result in much text. On the other hand,
|
||||||
info!("Files unpacked: {}", files_unpacked.len());
|
// having a final status message is important especially in an accessibility context
|
||||||
|
// as screen readers may not read a commands exit code, making it hard to reason
|
||||||
|
// about whether the command succeeded without such a message
|
||||||
|
info!(accessible, "Successfully decompressed archive in {}.", nice_directory_display(output_dir));
|
||||||
|
info!(accessible, "Files unpacked: {}", files_unpacked.len());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -531,7 +550,11 @@ fn smart_unpack(
|
|||||||
assert!(output_dir.exists());
|
assert!(output_dir.exists());
|
||||||
let temp_dir = tempfile::tempdir_in(output_dir)?;
|
let temp_dir = tempfile::tempdir_in(output_dir)?;
|
||||||
let temp_dir_path = temp_dir.path();
|
let temp_dir_path = temp_dir.path();
|
||||||
info!("Created temporary directory {} to hold decompressed elements.", nice_directory_display(temp_dir_path));
|
info!(
|
||||||
|
accessible,
|
||||||
|
"Created temporary directory {} to hold decompressed elements.",
|
||||||
|
nice_directory_display(temp_dir_path)
|
||||||
|
);
|
||||||
|
|
||||||
// unpack the files
|
// unpack the files
|
||||||
let files = unpack_fn(temp_dir_path)?;
|
let files = unpack_fn(temp_dir_path)?;
|
||||||
@ -550,6 +573,7 @@ fn smart_unpack(
|
|||||||
}
|
}
|
||||||
fs::rename(&file_path, &correct_path)?;
|
fs::rename(&file_path, &correct_path)?;
|
||||||
info!(
|
info!(
|
||||||
|
accessible,
|
||||||
"Successfully moved {} to {}.",
|
"Successfully moved {} to {}.",
|
||||||
nice_directory_display(&file_path),
|
nice_directory_display(&file_path),
|
||||||
nice_directory_display(&correct_path)
|
nice_directory_display(&correct_path)
|
||||||
@ -563,6 +587,7 @@ fn smart_unpack(
|
|||||||
}
|
}
|
||||||
fs::rename(&temp_dir_path, &output_file_path)?;
|
fs::rename(&temp_dir_path, &output_file_path)?;
|
||||||
info!(
|
info!(
|
||||||
|
accessible,
|
||||||
"Successfully moved {} to {}.",
|
"Successfully moved {} to {}.",
|
||||||
nice_directory_display(&temp_dir_path),
|
nice_directory_display(&temp_dir_path),
|
||||||
nice_directory_display(&output_file_path)
|
nice_directory_display(&output_file_path)
|
||||||
@ -581,7 +606,9 @@ fn check_mime_type(
|
|||||||
// File with no extension
|
// File with no extension
|
||||||
// Try to detect it automatically and prompt the user about it
|
// Try to detect it automatically and prompt the user about it
|
||||||
if let Some(detected_format) = try_infer_extension(path) {
|
if let Some(detected_format) = try_infer_extension(path) {
|
||||||
info!("Detected file: `{}` extension as `{}`", path.display(), detected_format);
|
// Infering the file extension can have unpredicted consequences (e.g. the user just
|
||||||
|
// mistyped, ...) which we should always inform the user about.
|
||||||
|
info!(accessible, "Detected file: `{}` extension as `{}`", path.display(), detected_format);
|
||||||
if user_wants_to_continue_decompressing(path, question_policy)? {
|
if user_wants_to_continue_decompressing(path, question_policy)? {
|
||||||
format.push(detected_format);
|
format.push(detected_format);
|
||||||
} else {
|
} else {
|
||||||
@ -605,7 +632,7 @@ fn check_mime_type(
|
|||||||
} else {
|
} else {
|
||||||
// NOTE: If this actually produces no false positives, we can upgrade it in the future
|
// NOTE: If this actually produces no false positives, we can upgrade it in the future
|
||||||
// to a warning and ask the user if he wants to continue decompressing.
|
// to a warning and ask the user if he wants to continue decompressing.
|
||||||
info!("Could not detect the extension of `{}`", path.display());
|
info!(accessible, "Could not detect the extension of `{}`", path.display());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(ControlFlow::Continue(()))
|
Ok(ControlFlow::Continue(()))
|
||||||
|
15
src/error.rs
15
src/error.rs
@ -49,7 +49,13 @@ pub struct FinalError {
|
|||||||
impl Display for FinalError {
|
impl Display for FinalError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
// Title
|
// Title
|
||||||
|
//
|
||||||
|
// When in ACCESSIBLE mode, the square brackets are suppressed
|
||||||
|
if *crate::cli::ACCESSIBLE.get().unwrap_or(&false) {
|
||||||
|
write!(f, "{}ERROR{}: {}", *RED, *RESET, self.title)?;
|
||||||
|
} else {
|
||||||
write!(f, "{}[ERROR]{} {}", *RED, *RESET, self.title)?;
|
write!(f, "{}[ERROR]{} {}", *RED, *RESET, self.title)?;
|
||||||
|
}
|
||||||
|
|
||||||
// Details
|
// Details
|
||||||
for detail in &self.details {
|
for detail in &self.details {
|
||||||
@ -60,10 +66,19 @@ impl Display for FinalError {
|
|||||||
if !self.hints.is_empty() {
|
if !self.hints.is_empty() {
|
||||||
// Separate by one blank line.
|
// Separate by one blank line.
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
|
// to reduce redundant output for text-to-speach systems, braille
|
||||||
|
// displays and so on, only print "hints" once in ACCESSIBLE mode
|
||||||
|
if *crate::cli::ACCESSIBLE.get().unwrap_or(&false) {
|
||||||
|
write!(f, "\n{}hints:{}", *GREEN, *RESET)?;
|
||||||
|
for hint in &self.hints {
|
||||||
|
write!(f, "\n{}", hint)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
for hint in &self.hints {
|
for hint in &self.hints {
|
||||||
write!(f, "\n{}hint:{} {}", *GREEN, *RESET, hint)?;
|
write!(f, "\n{}hint:{} {}", *GREEN, *RESET, hint)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ pub struct FileInArchive {
|
|||||||
|
|
||||||
/// Actually print the files
|
/// Actually print the files
|
||||||
pub fn list_files(archive: &Path, files: Vec<FileInArchive>, list_options: ListOptions) {
|
pub fn list_files(archive: &Path, files: Vec<FileInArchive>, list_options: ListOptions) {
|
||||||
println!("{}:", archive.display());
|
println!("Archive: {}", archive.display());
|
||||||
if list_options.tree {
|
if list_options.tree {
|
||||||
let tree: Tree = files.into_iter().collect();
|
let tree: Tree = files.into_iter().collect();
|
||||||
tree.print();
|
tree.print();
|
||||||
@ -43,6 +43,11 @@ fn print_entry(name: impl std::fmt::Display, is_dir: bool) {
|
|||||||
// if colors are deactivated, print final / to mark directories
|
// if colors are deactivated, print final / to mark directories
|
||||||
if BLUE.is_empty() {
|
if BLUE.is_empty() {
|
||||||
println!("{}/", name);
|
println!("{}/", name);
|
||||||
|
// if in ACCESSIBLE mode, use colors but print final / in case colors
|
||||||
|
// aren't read out aloud with a screen reader or aren't printed on a
|
||||||
|
// braille reader
|
||||||
|
} else if *crate::cli::ACCESSIBLE.get().unwrap() {
|
||||||
|
println!("{}{}{}/{}", *BLUE, *STYLE_BOLD, name, *ALL_RESET);
|
||||||
} else {
|
} else {
|
||||||
println!("{}{}{}{}", *BLUE, *STYLE_BOLD, name, *ALL_RESET);
|
println!("{}{}{}{}", *BLUE, *STYLE_BOLD, name, *ALL_RESET);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,37 @@
|
|||||||
//! Macros used on ouch.
|
//! Macros used on ouch.
|
||||||
|
|
||||||
/// Macro that prints \[INFO\] messages, wraps [`println`].
|
/// Macro that prints \[INFO\] messages, wraps [`println`].
|
||||||
|
///
|
||||||
|
/// There are essentially two different versions of the `info!()` macro:
|
||||||
|
/// - `info!(accessible, ...)` should only be used for short, important
|
||||||
|
/// information which is expected to be useful for e.g. blind users whose
|
||||||
|
/// text-to-speach systems read out every output line, which is why we
|
||||||
|
/// should reduce nonessential output to a minimum when running in
|
||||||
|
/// ACCESSIBLE mode
|
||||||
|
/// - `info!(inaccessible, ...)` can be used more carelessly / for less
|
||||||
|
/// important information. A seeing user can easily skim through more lines
|
||||||
|
/// of output, so e.g. reporting every single processed file can be helpful,
|
||||||
|
/// while it would generate long and hard to navigate text for blind people
|
||||||
|
/// who have to have each line of output read to them aloud, whithout to
|
||||||
|
/// ability to skip some lines deemed not important like a seeing person would.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! info {
|
macro_rules! info {
|
||||||
($($arg:tt)*) => {
|
// Accessible (short/important) info message.
|
||||||
|
// Show info message even in ACCESSIBLE mode
|
||||||
|
(accessible, $($arg:tt)*) => {
|
||||||
|
// if in ACCESSIBLE mode, suppress the "[INFO]" and just print the message
|
||||||
|
if (!$crate::cli::ACCESSIBLE.get().unwrap()) {
|
||||||
|
$crate::macros::_info_helper();
|
||||||
|
}
|
||||||
|
println!($($arg)*);
|
||||||
|
};
|
||||||
|
// Inccessible (long/no important) info message.
|
||||||
|
// Print info message if ACCESSIBLE is not turned on
|
||||||
|
(inaccessible, $($arg:tt)*) => {
|
||||||
|
if (!$crate::cli::ACCESSIBLE.get().unwrap()) {
|
||||||
$crate::macros::_info_helper();
|
$crate::macros::_info_helper();
|
||||||
println!($($arg)*);
|
println!($($arg)*);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,5 +55,9 @@ macro_rules! warning {
|
|||||||
pub fn _warning_helper() {
|
pub fn _warning_helper() {
|
||||||
use crate::utils::colors::{ORANGE, RESET};
|
use crate::utils::colors::{ORANGE, RESET};
|
||||||
|
|
||||||
|
if !crate::cli::ACCESSIBLE.get().unwrap() {
|
||||||
|
print!("{}Warning:{} ", *ORANGE, *RESET);
|
||||||
|
} else {
|
||||||
print!("{}[WARNING]{} ", *ORANGE, *RESET);
|
print!("{}[WARNING]{} ", *ORANGE, *RESET);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -19,6 +19,10 @@ pub struct Opts {
|
|||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
pub no: bool,
|
pub no: bool,
|
||||||
|
|
||||||
|
/// Activate accessibility mode, reducing visual noise
|
||||||
|
#[clap(short = 'A', long, env = "ACCESSIBLE")]
|
||||||
|
pub accessible: bool,
|
||||||
|
|
||||||
/// Ouch and claps subcommands
|
/// Ouch and claps subcommands
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
pub cmd: Subcommand,
|
pub cmd: Subcommand,
|
||||||
|
@ -42,7 +42,9 @@ pub fn clear_path(path: &Path, question_policy: QuestionPolicy) -> crate::Result
|
|||||||
pub fn create_dir_if_non_existent(path: &Path) -> crate::Result<()> {
|
pub fn create_dir_if_non_existent(path: &Path) -> crate::Result<()> {
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
fs::create_dir_all(path)?;
|
fs::create_dir_all(path)?;
|
||||||
info!("directory {} created.", to_utf(path));
|
// creating a directory is an important change to the file system we
|
||||||
|
// should always inform the user about
|
||||||
|
info!(accessible, "directory {} created.", to_utf(path));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,11 @@ impl<'a> Confirmation<'a> {
|
|||||||
|
|
||||||
// Ask the same question to end while no valid answers are given
|
// Ask the same question to end while no valid answers are given
|
||||||
loop {
|
loop {
|
||||||
|
if *crate::cli::ACCESSIBLE.get().unwrap() {
|
||||||
|
print!("{} {}yes{}/{}no{}: ", message, *colors::GREEN, *colors::RESET, *colors::RED, *colors::RESET);
|
||||||
|
} else {
|
||||||
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();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user