diff --git a/src/app.rs b/src/app.rs
index 4515d2a..e27e757 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -5,7 +5,7 @@ use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::process;
-use crate::{replace, DirectoryPatcher, Query, Settings, Stats};
+use crate::{console::Verbosity, replace, Console, DirectoryPatcher, Query, Settings};
#[derive(Debug)]
enum ColorWhen {
@@ -52,6 +52,12 @@ struct Options {
#[clap(long = "go", help = "Write the changes to the filesystem")]
go: bool,
+ #[clap(
+ long = "quiet",
+ help = "Don't show any output (except in case of errors)"
+ )]
+ quiet: bool,
+
#[clap(help = "The pattern to search for")]
pattern: String,
@@ -148,15 +154,6 @@ fn configure_color(when: &ColorWhen) {
}
}
-fn print_stats(stats: &Stats, dry_run: bool) {
- if dry_run {
- print!("Would perform ")
- } else {
- print!("Performed ")
- }
- println!("{}", stats)
-}
-
fn on_type_list() {
println!("Known file types:");
let mut types_builder = ignore::types::TypesBuilder::new();
@@ -174,6 +171,7 @@ pub fn run() -> Result<()> {
color_when,
file_type_list,
go,
+ quiet,
hidden,
ignored,
ignored_file_types,
@@ -192,6 +190,12 @@ pub fn run() -> Result<()> {
}
let dry_run = !go;
+ let verbosity = if quiet {
+ Verbosity::Quiet
+ } else {
+ Verbosity::Normal
+ };
+ let console = Console::with_verbosity(verbosity);
let color_when = &color_when.unwrap_or(ColorWhen::Auto);
configure_color(color_when);
@@ -205,6 +209,7 @@ pub fn run() -> Result<()> {
};
let settings = Settings {
+ verbosity,
dry_run,
hidden,
ignored,
@@ -216,7 +221,7 @@ pub fn run() -> Result<()> {
if path == PathBuf::from("-") {
run_on_stdin(query)
} else {
- run_on_directory(path, settings, query)
+ run_on_directory(console, path, settings, query)
}
}
@@ -234,21 +239,36 @@ fn run_on_stdin(query: Query) -> Result<()> {
Ok(())
}
-fn run_on_directory(path: PathBuf, settings: Settings, query: Query) -> Result<()> {
+fn run_on_directory(
+ console: Console,
+ path: PathBuf,
+ settings: Settings,
+ query: Query,
+) -> Result<()> {
let dry_run = settings.dry_run;
- let mut directory_patcher = DirectoryPatcher::new(&path, &settings);
+ let mut directory_patcher = DirectoryPatcher::new(&console, &path, &settings);
directory_patcher.run(&query)?;
let stats = directory_patcher.stats();
if stats.total_replacements() == 0 {
- #[allow(clippy::print_literal)]
- {
- eprintln!("{}: {}", "Error".bold().red(), "nothing found to replace");
- }
+ console.print_error(&format!(
+ "{}: {}",
+ "Error".bold().red(),
+ "nothing found to replace"
+ ));
process::exit(2);
}
- print_stats(&stats, dry_run);
+
+ let stats = &stats;
+ let message = if dry_run {
+ "Would perform "
+ } else {
+ "Performed "
+ };
+ console.print_message(message);
+ console.print_message(&format!("{stats}\n"));
+
if dry_run {
- println!("Re-run ruplacer with --go to write these changes to the filesystem");
+ console.print_message("Re-run ruplacer with --go to write these changes to the filesystem");
}
Ok(())
}
diff --git a/src/console.rs b/src/console.rs
new file mode 100644
index 0000000..a2aaf86
--- /dev/null
+++ b/src/console.rs
@@ -0,0 +1,37 @@
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Verbosity {
+ Quiet,
+ Normal,
+}
+
+impl Default for Verbosity {
+ fn default() -> Self {
+ Verbosity::Normal
+ }
+}
+
+#[derive(Debug, Default)]
+pub struct Console {
+ verbosity: Verbosity,
+}
+
+impl Console {
+ pub fn with_verbosity(verbosity: Verbosity) -> Self {
+ Self { verbosity }
+ }
+
+ pub fn new() -> Self {
+ Default::default()
+ }
+
+ pub fn print_message(&self, message: &str) {
+ if matches!(self.verbosity, Verbosity::Quiet) {
+ return;
+ }
+ print!("{message}");
+ }
+
+ pub fn print_error(&self, error: &str) {
+ eprintln!("{error}");
+ }
+}
diff --git a/src/directory_patcher.rs b/src/directory_patcher.rs
index 8334586..f88b1d5 100644
--- a/src/directory_patcher.rs
+++ b/src/directory_patcher.rs
@@ -1,6 +1,7 @@
use anyhow::{Context, Result};
use std::path::Path;
+use crate::console::Console;
use crate::file_patcher::FilePatcher;
use crate::query::Query;
use crate::settings::Settings;
@@ -9,7 +10,7 @@ use crate::stats::Stats;
#[derive(Debug)]
/// Used to run replacement query on every text file present in a given path
/// ```rust
-/// use ruplacer::{DirectoryPatcher, Query, Settings, Stats};
+/// use ruplacer::{Console, DirectoryPatcher, Query, Settings, Stats};
/// use std::path::PathBuf;
///
/// let settings = Settings{
@@ -17,7 +18,8 @@ use crate::stats::Stats;
/// .. Default::default()
/// };
/// let path = PathBuf::from("tests/data");
-/// let mut directory_patcher = DirectoryPatcher::new(&path, &settings);
+/// let console = Console::new();
+/// let mut directory_patcher = DirectoryPatcher::new(&console, &path, &settings);
///
/// let query = Query::substring("old", "new");
/// directory_patcher.run(&query).unwrap();
@@ -29,13 +31,19 @@ use crate::stats::Stats;
pub struct DirectoryPatcher<'a> {
path: &'a Path,
settings: &'a Settings,
+ console: &'a Console,
stats: Stats,
}
impl<'a> DirectoryPatcher<'a> {
- pub fn new(path: &'a Path, settings: &'a Settings) -> DirectoryPatcher<'a> {
+ pub fn new(
+ console: &'a Console,
+ path: &'a Path,
+ settings: &'a Settings,
+ ) -> DirectoryPatcher<'a> {
let stats = Stats::default();
DirectoryPatcher {
+ console,
path,
settings,
stats,
@@ -61,14 +69,14 @@ impl<'a> DirectoryPatcher<'a> {
}
pub(crate) fn patch_file(&mut self, entry: &Path, query: &Query) -> Result<()> {
- let file_patcher = FilePatcher::new(entry, query)?;
+ let file_patcher = FilePatcher::new(self.console, entry, query)?;
let file_patcher = match file_patcher {
None => return Ok(()),
Some(f) => f,
};
let num_replacements = file_patcher.num_replacements();
if num_replacements != 0 {
- println!();
+ self.console.print_message("\n");
}
let num_lines = file_patcher.num_lines();
self.stats.update(num_lines, num_replacements);
diff --git a/src/file_patcher.rs b/src/file_patcher.rs
index b5c4ec4..53a209f 100644
--- a/src/file_patcher.rs
+++ b/src/file_patcher.rs
@@ -5,18 +5,20 @@ use std::path::{Path, PathBuf};
use crate::query::Query;
use crate::replace;
+use crate::Console;
/// Run replacement query on a given file
///
/// Example, assuming the `data.txt` file contains 'This is my old car'
/// ```rust
-/// use ruplacer::{FilePatcher, Query};
+/// use ruplacer::{Console, FilePatcher, Query};
/// use std::path::PathBuf;
///
/// # std::fs::write("data.txt", "This is my old car.").unwrap();
/// let file = PathBuf::from("data.txt");
/// let query = Query::substring("old", "new");
-/// let file_patcher = FilePatcher::new(&file, &query).unwrap();
+/// let console = Console::new();
+/// let file_patcher = FilePatcher::new(&console, &file, &query).unwrap();
/// file_patcher.unwrap().run().unwrap();
///
/// let new_contents = std::fs::read_to_string("data.txt").unwrap();
@@ -30,7 +32,7 @@ pub struct FilePatcher {
}
impl FilePatcher {
- pub fn new(path: &Path, query: &Query) -> Result