Suppress all output to stderr when --quiet (#771)

Suppress all warnings and error messages when `--quiet` is passed.
This commit is contained in:
Casey Rodarmor 2021-03-25 16:51:29 -07:00 committed by GitHub
parent d398417de3
commit 86c2e52dc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 260 additions and 128 deletions

View File

@ -483,14 +483,14 @@ impl Config {
}
if let Completions { shell } = self.subcommand {
return Subcommand::completions(&shell);
return Subcommand::completions(self.verbosity, &shell);
}
let search =
Search::find(&self.search_config, &self.invocation_directory).eprint(self.color)?;
if self.subcommand == Edit {
return Self::edit(&search);
return self.edit(&search);
}
let src = fs::read_to_string(&search.justfile)
@ -502,11 +502,13 @@ impl Config {
let justfile = Compiler::compile(&src).eprint(self.color)?;
for warning in &justfile.warnings {
if self.color.stderr().active() {
eprintln!("{:#}", warning);
} else {
eprintln!("{}", warning);
if self.verbosity.loud() {
for warning in &justfile.warnings {
if self.color.stderr().active() {
eprintln!("{:#}", warning);
} else {
eprintln!("{}", warning);
}
}
}
@ -520,7 +522,7 @@ impl Config {
arguments,
overrides,
} => self.run(justfile, &search, overrides, arguments)?,
Show { ref name } => Self::show(&name, justfile)?,
Show { ref name } => self.show(&name, justfile)?,
Summary => self.summary(justfile),
Variables => Self::variables(justfile),
Completions { .. } | Edit | Init => unreachable!(),
@ -544,7 +546,9 @@ impl Config {
.collect::<Vec<&Recipe<Dependency>>>();
if recipes.is_empty() {
eprintln!("Justfile contains no choosable recipes.");
if self.verbosity.loud() {
eprintln!("Justfile contains no choosable recipes.");
}
return Err(EXIT_FAILURE);
}
@ -565,11 +569,13 @@ impl Config {
let mut child = match result {
Ok(child) => child,
Err(error) => {
eprintln!(
"Chooser `{}` invocation failed: {}",
chooser.to_string_lossy(),
error
);
if self.verbosity.loud() {
eprintln!(
"Chooser `{}` invocation failed: {}",
chooser.to_string_lossy(),
error
);
}
return Err(EXIT_FAILURE);
},
};
@ -581,11 +587,13 @@ impl Config {
.expect("Child was created with piped stdio")
.write_all(format!("{}\n", recipe.name).as_bytes())
{
eprintln!(
"Failed to write to chooser `{}`: {}",
chooser.to_string_lossy(),
error
);
if self.verbosity.loud() {
eprintln!(
"Failed to write to chooser `{}`: {}",
chooser.to_string_lossy(),
error
);
}
return Err(EXIT_FAILURE);
}
}
@ -593,21 +601,25 @@ impl Config {
let output = match child.wait_with_output() {
Ok(output) => output,
Err(error) => {
eprintln!(
"Failed to read output from chooser `{}`: {}",
chooser.to_string_lossy(),
error
);
if self.verbosity.loud() {
eprintln!(
"Failed to read output from chooser `{}`: {}",
chooser.to_string_lossy(),
error
);
}
return Err(EXIT_FAILURE);
},
};
if !output.status.success() {
eprintln!(
"Chooser `{}` returned error: {}",
chooser.to_string_lossy(),
output.status
);
if self.verbosity.loud() {
eprintln!(
"Chooser `{}` returned error: {}",
chooser.to_string_lossy(),
output.status
);
}
return Err(output.status.code().unwrap_or(EXIT_FAILURE));
}
@ -626,7 +638,7 @@ impl Config {
println!("{}", justfile);
}
pub(crate) fn edit(search: &Search) -> Result<(), i32> {
pub(crate) fn edit(&self, search: &Search) -> Result<(), i32> {
let editor = env::var_os("VISUAL")
.or_else(|| env::var_os("EDITOR"))
.unwrap_or_else(|| "vim".into());
@ -641,15 +653,19 @@ impl Config {
if status.success() {
Ok(())
} else {
eprintln!("Editor `{}` failed: {}", editor.to_string_lossy(), status);
if self.verbosity.loud() {
eprintln!("Editor `{}` failed: {}", editor.to_string_lossy(), status);
}
Err(status.code().unwrap_or(EXIT_FAILURE))
},
Err(error) => {
eprintln!(
"Editor `{}` invocation failed: {}",
editor.to_string_lossy(),
error
);
if self.verbosity.loud() {
eprintln!(
"Editor `{}` invocation failed: {}",
editor.to_string_lossy(),
error
);
}
Err(EXIT_FAILURE)
},
}
@ -660,17 +676,23 @@ impl Config {
Search::init(&self.search_config, &self.invocation_directory).eprint(self.color)?;
if search.justfile.exists() {
eprintln!("Justfile `{}` already exists", search.justfile.display());
if self.verbosity.loud() {
eprintln!("Justfile `{}` already exists", search.justfile.display());
}
Err(EXIT_FAILURE)
} else if let Err(err) = fs::write(&search.justfile, INIT_JUSTFILE) {
eprintln!(
"Failed to write justfile to `{}`: {}",
search.justfile.display(),
err
);
if self.verbosity.loud() {
eprintln!(
"Failed to write justfile to `{}`: {}",
search.justfile.display(),
err
);
}
Err(EXIT_FAILURE)
} else {
eprintln!("Wrote justfile to `{}`", search.justfile.display());
if self.verbosity.loud() {
eprintln!("Wrote justfile to `{}`", search.justfile.display());
}
Ok(())
}
}
@ -766,7 +788,7 @@ impl Config {
overrides: &BTreeMap<String, String>,
arguments: &[String],
) -> Result<(), i32> {
if let Err(error) = InterruptHandler::install() {
if let Err(error) = InterruptHandler::install(self.verbosity) {
warn!("Failed to set CTRL-C handler: {}", error)
}
@ -779,7 +801,7 @@ impl Config {
}
}
fn show(name: &str, justfile: Justfile) -> Result<(), i32> {
fn show(&self, name: &str, justfile: Justfile) -> Result<(), i32> {
if let Some(alias) = justfile.get_alias(name) {
let recipe = justfile.get_recipe(alias.target.name.lexeme()).unwrap();
println!("{}", alias);
@ -789,9 +811,11 @@ impl Config {
println!("{}", recipe);
Ok(())
} else {
eprintln!("Justfile does not contain recipe `{}`.", name);
if let Some(suggestion) = justfile.suggest(name) {
eprintln!("{}", suggestion);
if self.verbosity.loud() {
eprintln!("Justfile does not contain recipe `{}`.", name);
if let Some(suggestion) = justfile.suggest(name) {
eprintln!("{}", suggestion);
}
}
Err(EXIT_FAILURE)
}
@ -799,7 +823,9 @@ impl Config {
fn summary(&self, justfile: Justfile) {
if justfile.count() == 0 {
eprintln!("Justfile contains no recipes.");
if self.verbosity.loud() {
eprintln!("Justfile contains no recipes.");
}
} else {
let summary = justfile
.public_recipes(self.unsorted)

View File

@ -3,10 +3,13 @@ use crate::common::*;
pub(crate) struct InterruptHandler {
blocks: u32,
interrupted: bool,
verbosity: Verbosity,
}
impl InterruptHandler {
pub(crate) fn install() -> Result<(), ctrlc::Error> {
pub(crate) fn install(verbosity: Verbosity) -> Result<(), ctrlc::Error> {
let mut instance = Self::instance();
instance.verbosity = verbosity;
ctrlc::set_handler(|| Self::instance().interrupt())
}
@ -30,6 +33,7 @@ impl InterruptHandler {
Self {
blocks: 0,
interrupted: false,
verbosity: Verbosity::default(),
}
}
@ -53,9 +57,11 @@ impl InterruptHandler {
pub(crate) fn unblock(&mut self) {
if self.blocks == 0 {
eprintln!("{}", RuntimeError::Internal {
message: "attempted to unblock interrupt handler, but handler was not blocked".to_owned(),
});
if self.verbosity.loud() {
eprintln!("{}", RuntimeError::Internal {
message: "attempted to unblock interrupt handler, but handler was not blocked".to_owned(),
});
}
std::process::exit(EXIT_FAILURE);
}

View File

@ -96,7 +96,7 @@ impl<'src, D> Recipe<'src, D> {
evaluated_lines.push(evaluator.evaluate_line(line, false)?);
}
if config.dry_run || self.quiet {
if config.verbosity.loud() && (config.dry_run || self.quiet) {
for line in &evaluated_lines {
eprintln!("{}", line);
}

View File

@ -203,18 +203,25 @@ const BASH_COMPLETION_REPLACEMENTS: &[(&str, &str)] = &[(
)];
impl Subcommand {
pub(crate) fn completions(shell: &str) -> Result<(), i32> {
pub(crate) fn completions(verbosity: Verbosity, shell: &str) -> Result<(), i32> {
use clap::Shell;
fn replace(haystack: &mut String, needle: &str, replacement: &str) -> Result<(), i32> {
fn replace(
verbosity: Verbosity,
haystack: &mut String,
needle: &str,
replacement: &str,
) -> Result<(), i32> {
if let Some(index) = haystack.find(needle) {
haystack.replace_range(index..index + needle.len(), replacement);
Ok(())
} else {
eprintln!("Failed to find text:");
eprintln!("{}", needle);
eprintln!("…in completion script:");
eprintln!("{}", haystack);
if verbosity.loud() {
eprintln!("Failed to find text:");
eprintln!("{}", needle);
eprintln!("…in completion script:");
eprintln!("{}", haystack);
}
Err(EXIT_FAILURE)
}
}
@ -232,19 +239,19 @@ impl Subcommand {
match shell {
Shell::Bash =>
for (needle, replacement) in BASH_COMPLETION_REPLACEMENTS {
replace(&mut script, needle, replacement)?;
replace(verbosity, &mut script, needle, replacement)?;
},
Shell::Fish => {
script.insert_str(0, FISH_RECIPE_COMPLETIONS);
},
Shell::PowerShell =>
for (needle, replacement) in POWERSHELL_COMPLETION_REPLACEMENTS {
replace(&mut script, needle, replacement)?;
replace(verbosity, &mut script, needle, replacement)?;
},
Shell::Zsh =>
for (needle, replacement) in ZSH_COMPLETION_REPLACEMENTS {
replace(&mut script, needle, replacement)?;
replace(verbosity, &mut script, needle, replacement)?;
},
Shell::Elvish => {},
}

View File

@ -21,6 +21,10 @@ impl Verbosity {
matches!(self, Quiet)
}
pub(crate) fn loud(self) -> bool {
!self.quiet()
}
pub(crate) fn loquacious(self) -> bool {
match self {
Quiet | Taciturn => false,
@ -35,3 +39,9 @@ impl Verbosity {
}
}
}
impl Default for Verbosity {
fn default() -> Self {
Self::Taciturn
}
}

View File

@ -15,6 +15,7 @@ mod init;
mod interrupts;
mod invocation_directory;
mod misc;
mod quiet;
mod readme;
mod search;
mod shell;

View File

@ -666,71 +666,6 @@ test! {
status: EXIT_FAILURE,
}
test! {
name: quiet_flag_no_stdout,
justfile: r#"
default:
@echo hello
"#,
args: ("--quiet"),
stdout: "",
}
test! {
name: quiet_flag_no_stderr,
justfile: r#"
default:
@echo hello 1>&2
"#,
args: ("--quiet"),
stdout: "",
}
test! {
name: quiet_flag_no_command_echoing,
justfile: r#"
default:
exit
"#,
args: ("--quiet"),
stdout: "",
}
test! {
name: quiet_flag_no_error_messages,
justfile: r#"
default:
exit 100
"#,
args: ("--quiet"),
stdout: "",
status: 100,
}
test! {
name: quiet_flag_no_assignment_backtick_stderr,
justfile: r#"
a := `echo hello 1>&2`
default:
exit 100
"#,
args: ("--quiet"),
stdout: "",
status: 100,
}
test! {
name: quiet_flag_no_interpolation_backtick_stderr,
justfile: r#"
default:
echo `echo hello 1>&2`
exit 100
"#,
args: ("--quiet"),
stdout: "",
status: 100,
}
test! {
name: argument_single,
justfile: "

147
tests/quiet.rs Normal file
View File

@ -0,0 +1,147 @@
use crate::common::*;
test! {
name: no_stdout,
justfile: r#"
default:
@echo hello
"#,
args: ("--quiet"),
stdout: "",
}
test! {
name: stderr,
justfile: r#"
default:
@echo hello 1>&2
"#,
args: ("--quiet"),
stdout: "",
}
test! {
name: command_echoing,
justfile: r#"
default:
exit
"#,
args: ("--quiet"),
stdout: "",
}
test! {
name: error_messages,
justfile: r#"
default:
exit 100
"#,
args: ("--quiet"),
stdout: "",
status: 100,
}
test! {
name: assignment_backtick_stderr,
justfile: r#"
a := `echo hello 1>&2`
default:
exit 100
"#,
args: ("--quiet"),
stdout: "",
status: 100,
}
test! {
name: interpolation_backtick_stderr,
justfile: r#"
default:
echo `echo hello 1>&2`
exit 100
"#,
args: ("--quiet"),
stdout: "",
status: 100,
}
test! {
name: warning,
justfile: "
foo = 'bar'
baz:
",
args: ("--quiet"),
}
test! {
name: choose_none,
justfile: "",
args: ("--choose", "--quiet"),
status: EXIT_FAILURE,
}
test! {
name: choose_invocation,
justfile: "foo:",
args: ("--choose", "--quiet", "--shell", "asdfasdfasfdasdfasdfadsf"),
status: EXIT_FAILURE,
shell: false,
}
test! {
name: choose_status,
justfile: "foo:",
args: ("--choose", "--quiet", "--chooser", "/usr/bin/env false"),
status: EXIT_FAILURE,
}
test! {
name: edit_invocation,
justfile: "foo:",
args: ("--edit", "--quiet"),
env: {
"VISUAL": "adsfasdfasdfadsfadfsaf",
},
status: EXIT_FAILURE,
}
test! {
name: edit_status,
justfile: "foo:",
args: ("--edit", "--quiet"),
env: {
"VISUAL": "false",
},
status: EXIT_FAILURE,
}
test! {
name: init_exists,
justfile: "foo:",
args: ("--init", "--quiet"),
status: EXIT_FAILURE,
}
test! {
name: show_missing,
justfile: "foo:",
args: ("--show", "bar", "--quiet"),
status: EXIT_FAILURE,
}
test! {
name: summary_none,
justfile: "",
args: ("--summary", "--quiet"),
}
test! {
name: quiet_shebang,
justfile: "
@foo:
#!/bin/sh
",
args: ("--quiet"),
}