Skip to content

Commit

Permalink
feat(cli): Dry run mode ( --dry-run flag) (#156)
Browse files Browse the repository at this point in the history
This change adds a new flag `--dry-run` that when
given, prevents otherwise destructive file
overwrite operations and instead prints a rich
diff. The diff contains names of files which would
be modified, and a git-like (word) diff of what
modifications *would* be made.

The diff is human-readable, and not designed to be
easily machine-readable. It leverages the existing
search mode.

Closes #152.
  • Loading branch information
alexpovel authored Nov 3, 2024
1 parent 3beac24 commit c2a6d2a
Show file tree
Hide file tree
Showing 16 changed files with 497 additions and 42 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1457,6 +1457,13 @@ Options (global):
Processing no files is not an error condition in itself, but might be an
unexpected outcome in some contexts. This flag makes the condition explicit.

--dry-run
Do not destructively overwrite files, instead print rich diff only.
The diff details the names of files which would be modified, alongside all
changes inside those files which would be performed outside of dry running.
It is similar to git diff with word diffing enabled.

-i, --invert
Undo the effects of passed actions, where applicable.
Expand Down
22 changes: 22 additions & 0 deletions src/actions/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,28 @@ pub struct Style {
pub styles: Vec<Styles>,
}

impl Style {
/// Creates a style with red foreground color and bold font weight.
#[must_use]
pub fn red_bold() -> Self {
Self {
fg: Some(Color::Red),
bg: None,
styles: vec![Styles::Bold],
}
}

/// Creates a style with green foreground color and bold font weight.
#[must_use]
pub fn green_bold() -> Self {
Self {
fg: Some(Color::Green),
bg: None,
styles: vec![Styles::Bold],
}
}
}

impl Action for Style {
fn act(&self, input: &str) -> String {
const NEWLINE: char = '\n';
Expand Down
105 changes: 105 additions & 0 deletions src/iterext.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/// Extension trait that adds parallel zipping functionality to iterators over iterators.
pub trait ParallelZipExt: Iterator {
/// Zips multiple iterators in parallel, such that the nth invocation yields a
/// [`Vec`] of all nth items of the subiterators.
fn parallel_zip(self) -> ParallelZip<Self::Item>
where
Self: Sized,
Self::Item: Iterator;
}

/// An iterator similar to [`std::iter::zip`], but instead it zips over *multiple
/// iterators* in parallel, such that the nth invocation yields a [`Vec`] of all
/// nth items of its subiterators.
#[derive(Debug)]
pub struct ParallelZip<I>(Vec<I>);

impl<I: Iterator> Iterator for ParallelZip<I> {
type Item = Vec<I::Item>;

fn next(&mut self) -> Option<Self::Item> {
if self.0.is_empty() {
return None;
}

self.0.iter_mut().map(Iterator::next).collect()
}
}

// Implement the extension trait for any iterator whose items are themselves iterators
impl<T> ParallelZipExt for T
where
T: Iterator,
T::Item: Iterator,
{
fn parallel_zip(self) -> ParallelZip<T::Item> {
ParallelZip(self.collect())
}
}

#[cfg(test)]
mod tests {
use itertools::Itertools;
use rstest::rstest;

use super::*;

#[rstest]
#[case::empty_once(
Vec::<Vec<i32>>::new(),
Vec::new(),
)]
#[case::empty_twice(
vec![
vec![],
vec![],
],
vec![],
)]
#[case::zips_to_shortest(
vec![
vec![0, 1, 2],
vec![3, 4],
],
vec![
vec![0, 3],
vec![1, 4],
]
)]
#[case::base_case(
vec![
vec![1, 2],
vec![3, 4],
vec![5, 6]
],
vec![
vec![1, 3, 5],
vec![2, 4, 6]
]
)]
#[case::transpose_horizontal(
vec![
vec![1, 2, 3],
],
vec![
vec![1],
vec![2],
vec![3],
]
)]
#[case::transpose_vertical(
vec![
vec![1],
vec![2],
vec![3]
],
vec![
vec![1, 2, 3]
]
)]
fn test_parallel_zip(#[case] input: Vec<Vec<i32>>, #[case] expected: Vec<Vec<i32>>) {
let iters = input.into_iter().map(IntoIterator::into_iter).collect_vec();
let res = iters.into_iter().parallel_zip().collect_vec();
assert_eq!(res, expected);
}
}
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,6 @@ pub const GLOBAL_SCOPE: &str = r".*";
/// The type of regular expression used throughout the crate. Abstracts away the
/// underlying implementation.
pub use fancy_regex::Regex as RegexPattern;

/// Custom iterator extensions.
pub mod iterext;
Loading

0 comments on commit c2a6d2a

Please sign in to comment.