Skip to content

Commit

Permalink
Add a Console struct and implement --quiet command line option
Browse files Browse the repository at this point in the history
Also, cleanup src/lib.rs - lexical order FTW
  • Loading branch information
dmerejkowsky committed May 13, 2022
1 parent a9c9d4b commit 6af0001
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 46 deletions.
58 changes: 39 additions & 19 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,

Expand Down Expand Up @@ -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();
Expand All @@ -174,6 +171,7 @@ pub fn run() -> Result<()> {
color_when,
file_type_list,
go,
quiet,
hidden,
ignored,
ignored_file_types,
Expand All @@ -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);
Expand All @@ -205,6 +209,7 @@ pub fn run() -> Result<()> {
};

let settings = Settings {
verbosity,
dry_run,
hidden,
ignored,
Expand All @@ -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)
}
}

Expand All @@ -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(())
}
37 changes: 37 additions & 0 deletions src/console.rs
Original file line number Diff line number Diff line change
@@ -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}");
}
}
18 changes: 13 additions & 5 deletions src/directory_patcher.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -9,15 +10,16 @@ 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{
/// dry_run: true,
/// .. 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();
Expand All @@ -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,
Expand All @@ -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);
Expand Down
15 changes: 9 additions & 6 deletions src/file_patcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -30,7 +32,7 @@ pub struct FilePatcher {
}

impl FilePatcher {
pub fn new(path: &Path, query: &Query) -> Result<Option<FilePatcher>> {
pub fn new(console: &Console, path: &Path, query: &Query) -> Result<Option<FilePatcher>> {
let mut num_replacements = 0;
let mut num_lines = 0;
let file =
Expand All @@ -55,7 +57,7 @@ impl FilePatcher {
let lineno = num + 1;
let prefix = format!("{}:{} ", path.display(), lineno);
let new_line = replacement.output();
replacement.print_self(&prefix);
replacement.print_self(console, &prefix);
new_contents.push_str(new_line);
}
}
Expand Down Expand Up @@ -131,7 +133,8 @@ mod tests {
let file_path = temp_dir.path().join("without-trailing-newline.txt");
fs::write(&file_path, "first line\nI say: old is nice\nlast line").unwrap();
let query = Query::substring("old", "new");
let file_patcher = FilePatcher::new(&file_path, &query).unwrap();
let console = Console::new();
let file_patcher = FilePatcher::new(&console, &file_path, &query).unwrap();
file_patcher.unwrap().run().unwrap();
let actual = fs::read_to_string(&file_path).unwrap();
let expected = "first line\nI say: new is nice\nlast line";
Expand All @@ -140,7 +143,7 @@ mod tests {
let file_path = temp_dir.path().join("with-trailing-newline.txt");
fs::write(&file_path, "first line\nI say: old is nice\nlast line\n").unwrap();
let query = Query::substring("old", "new");
let file_patcher = FilePatcher::new(&file_path, &query).unwrap();
let file_patcher = FilePatcher::new(&console, &file_path, &query).unwrap();
file_patcher.unwrap().run().unwrap();
let actual = fs::read_to_string(&file_path).unwrap();
let expected = "first line\nI say: new is nice\nlast line\n";
Expand Down
11 changes: 7 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
mod app;
mod console;
mod directory_patcher;
mod file_patcher;
mod query;
mod replacer;
mod settings;
pub use settings::Settings;
mod stats;
pub use crate::replacer::{replace, Replacement};

pub use app::run;
pub use console::{Console, Verbosity};
pub use directory_patcher::DirectoryPatcher;
pub use file_patcher::FilePatcher;
pub use query::Query;
pub use replacer::{replace, Replacement};
pub use settings::Settings;
pub use stats::Stats;
mod app;
pub use app::run;
33 changes: 22 additions & 11 deletions src/replacer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::query::Query;
use crate::Console;
use colored::*;
use regex::Regex;

Expand Down Expand Up @@ -59,25 +60,33 @@ impl<'a> Replacement<'a> {

/// Print the replacement as two lines (red then green)
/// ```
/// use ruplacer::{Query, replace};
/// use ruplacer::{Console, Query, replace};
/// let input = "let foo_bar = FooBar::new();";
/// let query = Query::subvert("foo_bar", "spam_eggs");
/// let console = Console::new();
/// let replacement = replace(input, &query).unwrap();
/// replacement.print_self("foo.rs:3");
/// replacement.print_self(&console, "foo.rs:3");
/// // outputs:
/// // foo.rs:3 let foo_bar = FooBar::new()
/// // foo.rs:3 let spam_eggs = SpamEggs::new()
/// ```
pub fn print_self(&self, prefix: &str) {
pub fn print_self(&self, console: &Console, prefix: &str) {
let red_underline = { |x: &str| x.red().underline() };
let input_fragments = self.fragments.into_iter().map(|x| &x.0);
let red_prefix = format!("{}{}", prefix, "- ".red());
Self::print_fragments(&red_prefix, red_underline, self.input, input_fragments);
Self::print_fragments(
console,
&red_prefix,
red_underline,
self.input,
input_fragments,
);

let green_underline = { |x: &str| x.green().underline() };
let green_prefix = format!("{}{}", prefix, "+ ".green());
let output_fragments = self.fragments.into_iter().map(|x| &x.1);
Self::print_fragments(
console,
&green_prefix,
green_underline,
&self.output,
Expand All @@ -86,29 +95,30 @@ impl<'a> Replacement<'a> {
}

fn print_fragments<'f, C>(
console: &Console,
prefix: &str,
color: C,
line: &str,
fragments: impl Iterator<Item = &'f Fragment>,
) where
C: Fn(&str) -> ColoredString,
{
print!("{}", prefix);
console.print_message(prefix);
let mut current_index = 0;
for (i, fragment) in fragments.enumerate() {
let Fragment { index, text } = fragment;
// Whitespace between prefix and the first fragment does not matter
if i == 0 {
print!("{}", &line[current_index..*index].trim_start());
console.print_message((&line[current_index..*index].trim_start()).as_ref());
} else {
print!("{}", &line[current_index..*index]);
console.print_message(&line[current_index..*index]);
}
print!("{}", color(text));
console.print_message(&format!("{}", color(text)));
current_index = index + text.len();
}
print!("{}", &line[current_index..]);
console.print_message(&line[current_index..]);
if !line.ends_with('\n') {
println!()
console.print_message("\n");
}
}
}
Expand Down Expand Up @@ -370,7 +380,8 @@ mod tests {
let replacement = "new";
let query = Query::substring(pattern, replacement);
let replacement = replace(input, &query).unwrap();
replacement.print_self("foo.txt:3 ");
let console = Console::new();
replacement.print_self(&console, "foo.txt:3 ");
}

#[test]
Expand Down
Loading

0 comments on commit 6af0001

Please sign in to comment.