Skip to content

Commit

Permalink
Implement accessibility mode which reduces visual noise
Browse files Browse the repository at this point in the history
  • Loading branch information
AntonHermann committed Nov 13, 2021
1 parent 9af1106 commit 40cee89
Show file tree
Hide file tree
Showing 10 changed files with 65 additions and 21 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ edition = "2021"
readme = "README.md"
repository = "https://github.com/ouch-org/ouch"
license = "MIT"
keywords = ["decompression", "compression", "zip", "tar", "gzip"]
categories = ["command-line-utilities", "compression", "encoding"]
keywords = ["decompression", "compression", "zip", "tar", "gzip", "accessibility", "a11y"]
categories = ["command-line-utilities", "compression", "encoding", "accessibility"]
description = "A command-line utility for easily compressing and decompressing files and directories."

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@
# Features

1. Easy to use.
2. Automatic formats detection.
3. Same usage syntax for all formats.
4. Uses encoding and decoding streams to improve performance.
5. No runtime dependencies (for _Linux x86_64_).
6. Can list archive contents with pretty tree formatting.
7. Shell completions (soon!).
2. Accessibility (A11Y) mode via `--accessibility` or env var `ACCESSIBILITY`
3. Automatic formats detection.
4. Same usage syntax for all formats.
5. Uses encoding and decoding streams to improve performance.
6. No runtime dependencies (for _Linux x86_64_).
7. Can list archive contents with pretty tree formatting.
8. Shell completions (soon!).

# Usage

Expand Down
2 changes: 1 addition & 1 deletion src/archive/zip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ where

match (&*file.name()).ends_with('/') {
_is_dir @ true => {
println!("File {} extracted to \"{}\"", idx, file_path.display());
info!("File {} extracted to \"{}\"", idx, file_path.display());
fs::create_dir_all(&file_path)?;
}
_is_file @ false => {
Expand Down
7 changes: 7 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@ use std::{

use clap::Parser;
use fs_err as fs;
use once_cell::sync::OnceCell;

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 {
/// A helper method that calls `clap::Parser::parse`.
///
Expand All @@ -20,6 +25,8 @@ impl Opts {
pub fn parse_args() -> crate::Result<(Self, QuestionPolicy)> {
let mut opts = Self::parse();

ACCESSIBLE.set(opts.accessible).unwrap();

let (Subcommand::Compress { files, .. }
| Subcommand::Decompress { files, .. }
| Subcommand::List { archives: files, .. }) = &mut opts.cmd;
Expand Down
13 changes: 7 additions & 6 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,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".
// Contrapositive statement: "if there is extension, then there is file_name".
info!(
a11y_show,
"Partial compression detected. Compressing {} into {}",
to_utf(files[0].as_path().file_name().unwrap()),
to_utf(&output_path)
Expand All @@ -159,7 +160,7 @@ pub fn run(args: Opts, question_policy: QuestionPolicy) -> crate::Result<()> {
eprintln!(" Error:{reset} {}{red}.{reset}\n", err, reset = *colors::RESET, red = *colors::RED);
}
} else {
info!("Successfully compressed '{}'.", to_utf(output_path));
info!(a11y_show, "Successfully compressed '{}'.", to_utf(output_path));
}

compress_result?;
Expand Down Expand Up @@ -349,7 +350,7 @@ fn decompress_file(
utils::create_dir_if_non_existent(output_dir)?;
let zip_archive = zip::ZipArchive::new(reader)?;
let _files = crate::archive::zip::unpack_archive(zip_archive, output_dir, question_policy)?;
info!("Successfully decompressed archive in {}.", nice_directory_display(output_dir));
info!(a11y_show, "Successfully decompressed archive in {}.", nice_directory_display(output_dir));
return Ok(());
}

Expand Down Expand Up @@ -415,8 +416,8 @@ fn decompress_file(
}
}

info!("Successfully decompressed archive in {}.", nice_directory_display(output_dir));
info!("Files unpacked: {}", files_unpacked.len());
info!(a11y_show, "Successfully decompressed archive in {}.", nice_directory_display(output_dir));
info!(a11y_show, "Files unpacked: {}", files_unpacked.len());

Ok(())
}
Expand Down Expand Up @@ -497,7 +498,7 @@ fn check_mime_type(
// File with no extension
// Try to detect it automatically and prompt the user about it
if let Some(detected_format) = try_infer_extension(path) {
info!("Detected file: `{}` extension as `{}`", path.display(), detected_format);
info!(a11y_show, "Detected file: `{}` extension as `{}`", path.display(), detected_format);
if user_wants_to_continue_decompressing(path, question_policy)? {
format.push(detected_format);
} else {
Expand All @@ -521,7 +522,7 @@ fn check_mime_type(
} else {
// 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.
info!("Could not detect the extension of `{}`", path.display());
info!(a11y_show, "Could not detect the extension of `{}`", path.display());
}
}
Ok(ControlFlow::Continue(()))
Expand Down
6 changes: 5 additions & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ pub struct FinalError {
impl Display for FinalError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Title
write!(f, "{}[ERROR]{} {}", *RED, *RESET, self.title)?;
if *crate::cli::ACCESSIBLE.get().unwrap_or(&false) {
write!(f, "{}ERROR{}: {}", *RED, *RESET, self.title)?;
} else {
write!(f, "{}[ERROR]{} {}", *RED, *RESET, self.title)?;
}

// Details
for detail in &self.details {
Expand Down
7 changes: 6 additions & 1 deletion src/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub struct FileInArchive {

/// Actually print the files
pub fn list_files(archive: &Path, files: Vec<FileInArchive>, list_options: ListOptions) {
println!("{}:", archive.display());
println!("Archiv: {}", archive.display());
if list_options.tree {
let tree: Tree = files.into_iter().collect();
tree.print();
Expand All @@ -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 BLUE.is_empty() {
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 {
println!("{}{}{}{}", *BLUE, *STYLE_BOLD, name, *ALL_RESET);
}
Expand Down
24 changes: 21 additions & 3 deletions src/macros.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
//! Macros used on ouch.

/// Macro that prints \[INFO\] messages, wraps [`println`].
///
/// Normally, this prints nothing if ACCESSIBLE mode is turned on,
/// except when called as `info!(a11y_show, "..", ..)`
#[macro_export]
macro_rules! info {
($($arg:tt)*) => {
$crate::macros::_info_helper();
// Show info message even in ACCESSIBLE mode
(a11y_show, $($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)*);
};
// Print info message if ACCESSIBLE is not turned on
($($arg:tt)*) => {
if (!$crate::cli::ACCESSIBLE.get().unwrap()) {
$crate::macros::_info_helper();
println!($($arg)*);
}
};
}

/// Helper to display "\[INFO\]", colored yellow
Expand All @@ -29,5 +43,9 @@ macro_rules! warning {
pub fn _warning_helper() {
use crate::utils::colors::{ORANGE, RESET};

print!("{}[WARNING]{} ", *ORANGE, *RESET);
if !crate::cli::ACCESSIBLE.get().unwrap() {
print!("{}Warning:{} ", *ORANGE, *RESET);
} else {
print!("{}[WARNING]{} ", *ORANGE, *RESET);
}
}
4 changes: 4 additions & 0 deletions src/opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ pub struct Opts {
#[clap(short, long)]
pub no: bool,

/// Activate accessibility mode, reducing visual noise
#[clap(short = 'A', long, env = "ACCESSIBLE")]
pub accessible: bool,

/// Ouch and claps subcommands
#[clap(subcommand)]
pub cmd: Subcommand,
Expand Down
6 changes: 5 additions & 1 deletion src/utils/question.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,11 @@ impl<'a> Confirmation<'a> {

// 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);
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);
}
io::stdout().flush()?;

let mut answer = String::new();
Expand Down

0 comments on commit 40cee89

Please sign in to comment.