Skip to content

Commit

Permalink
Add delete flag (#7)
Browse files Browse the repository at this point in the history
* Remove prints

* Work on adding delete flag

* WIP Work on adding delete support

* WIP Add delete support

* Fix compile errors

* Delete unused import

* Make copies and renames for new tests

* Swap test order

* Add delete patch

* Setup simple delete preview test

* WIP Work on making replacer optional

* Fix a compile error

* Fix a compile error

* Fix a compile error

* Fix a compile error

* Fix a compile error

* Fix a warning

* Fix a compile error

* Fix compile errors

* Make delete run without replace

* Work on delete patch

* Fix test

* Add delete patch

* Setup nested delete preview

* Write simple delete

* Finish nested delete
  • Loading branch information
robenkleene authored Aug 6, 2023
1 parent 9120f52 commit 2dfe669
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 66 deletions.
6 changes: 6 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ fn create_man_page() {
.long("--preview")
.help("Emit the replacement to STDOUT"),
)
.flag(
Flag::new()
.short("-d")
.long("--delete")
.help("Delete files."),
)
.flag(
Flag::new()
.short("-s")
Expand Down
4 changes: 4 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ environment variable REN_PAGER can be used to override the pager.
*/
pub write: bool,

#[structopt(short = "d", long = "delete")]
/// Delete files
pub delete: bool,

#[structopt(short = "s", long = "string-mode")]
/// Treat expressions as non-regex strings
pub literal_mode: bool,
Expand Down
43 changes: 22 additions & 21 deletions src/input.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
use crate::{edit::Edit, output::OutputType, writer::Writer, Replacer, Result};
use std::io::prelude::*;
use std::path::PathBuf;
use indexmap::IndexMap;

pub(crate) struct App {
replacer: Replacer,
replacer: Option<Replacer>
}

impl App {
pub(crate) fn new(replacer: Replacer) -> Self {
pub(crate) fn new(replacer: Option<Replacer>) -> Self {
Self { replacer }
}

pub(crate) fn run(&self, preview: bool, color: bool, pager: Option<String>) -> Result<()> {
pub(crate) fn run(&self, preview: bool, delete: bool, color: bool, pager: Option<String>) -> Result<()> {
{
let stdin = std::io::stdin();
let handle = stdin.lock();
Expand Down Expand Up @@ -43,24 +44,24 @@ impl App {
sorted_paths.push(key);
}
sorted_paths.sort_by(|a, b| b.to_str().unwrap().len().cmp(&a.to_str().unwrap().len()));
let edit = Edit::new(&self.replacer);
match edit.parse(&sorted_paths) {
Ok(src_to_dst) => {
if preview {
let writer = Writer::new(sorted_paths, src_to_dst);
let text = match writer.patch_preview(color) {
Ok(text) => text,
Err(_) => return Ok(()), // FIXME:
};
write!(write, "{}", text)?;
} else {
let writer = Writer::new(sorted_paths, src_to_dst);
if let Err(_) = writer.write_file() {
return Ok(()); // FIXME:
}
}
}
Err(_) => {
let mut src_to_dst: Option<IndexMap<PathBuf, PathBuf>> = None;
if let Some(replacer) = &self.replacer {
let edit = Edit::new(&replacer);
src_to_dst = match edit.parse(&sorted_paths) {
Ok(src_to_dst) => Some(src_to_dst),
Err(_) => return Ok(()), // FIXME:
};
}
if preview {
let writer = Writer::new(sorted_paths, src_to_dst);
let text = match writer.patch_preview(color, delete) {
Ok(text) => text,
Err(_) => return Ok(()), // FIXME:
};
write!(write, "{}", text)?;
} else {
let writer = Writer::new(sorted_paths, src_to_dst);
if let Err(_) = writer.write_file(delete) {
return Ok(()); // FIXME:
}
}
Expand Down
21 changes: 13 additions & 8 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,19 @@ fn main() -> Result<()> {
let pager = env::var("REN_PAGER").ok();

if let (Some(find), Some(replace_with)) = (options.find, options.replace_with) {
App::new(Replacer::new(
find,
replace_with,
options.literal_mode,
options.flags,
options.replacements,
)?)
.run(!options.write, color, pager)?;
App::new(
Some(Replacer::new(
find,
replace_with,
options.literal_mode,
options.flags,
options.replacements,
)?)
)
.run(!options.write, options.delete, color, pager)?;
} else if options.delete {
App::new(None)
.run(!options.write, options.delete, color, pager)?;
}
process::exit(0);
}
79 changes: 50 additions & 29 deletions src/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub type Result<T, E = Error> = std::result::Result<T, E>;

pub(crate) struct Writer {
paths: Vec<PathBuf>,
src_to_dst: IndexMap<PathBuf, PathBuf>,
src_to_dst: Option<IndexMap<PathBuf, PathBuf>>,
}

#[derive(Debug, thiserror::Error)]
Expand All @@ -20,34 +20,41 @@ pub enum Error {
}

impl Writer {
pub(crate) fn new(paths: Vec<PathBuf>, src_to_dst: IndexMap<PathBuf, PathBuf>) -> Self {
pub(crate) fn new(paths: Vec<PathBuf>, src_to_dst: Option<IndexMap<PathBuf, PathBuf>>) -> Self {
Self { paths, src_to_dst }
}

pub(crate) fn patch_preview(&self, color: bool) -> Result<String, crate::writer::Error> {
pub(crate) fn patch_preview(&self, color: bool, delete: bool) -> Result<String, crate::writer::Error> {
let mut modified_paths: Vec<String> = Vec::new();
let mut print_diff = false;
for path in &self.paths {
let dst = &self.src_to_dst[path];
if path == dst || (path != dst && !Self::check(&path.to_path_buf(), &dst)) {
let path_string = path.to_string_lossy();
modified_paths.push(path_string.to_string());
continue;
}
print_diff = true;
modified_paths.push(dst.to_string_lossy().to_string());
}
if !print_diff {
return Ok("".to_string());
}
let modified = modified_paths.join("\n");
let mut modified = "".to_string();
let original: String = self
.paths
.clone()
.into_iter()
.map(|p| p.to_string_lossy().to_string())
.collect::<Vec<String>>()
.join("\n");
if !delete {
let src_to_dst = match &self.src_to_dst {
Some(src_to_dst) => src_to_dst,
None => panic!("Missing source to destination"),
};
for path in &self.paths {
let dst = &src_to_dst[path];
if path == dst || (path != dst && !Self::check(&path.to_path_buf(), &dst)) {
let path_string = path.to_string_lossy();
modified_paths.push(path_string.to_string());
continue;
}
print_diff = true;
modified_paths.push(dst.to_string_lossy().to_string());
}
if !print_diff {
return Ok("".to_string());
}
modified = modified_paths.join("\n").to_string();
}
let patch = create_patch(&original, &modified);
let f = match color {
true => PatchFormatter::new().with_color(),
Expand All @@ -56,19 +63,33 @@ impl Writer {
return Ok(f.fmt_patch(&patch).to_string());
}

pub(crate) fn write_file(&self) -> Result<()> {
pub(crate) fn write_file(&self, delete: bool) -> Result<()> {
for path in &self.paths {
let dst = &self.src_to_dst[path];
if path == dst || !Self::check(&path.to_path_buf(), &dst) {
continue;
}
if let Err(err) = fs::rename(path, &dst) {
eprintln!(
"Error: failed to move '{}' to '{}', underlying error: {}",
path.display(),
&dst.display(),
err
);
if delete {
if let Err(err) = fs::remove_file(path) {
eprintln!(
"Error: failed to remove file '{}': {}",
path.display(),
err
);
}
} else {
let src_to_dst = match &self.src_to_dst {
Some(src_to_dst) => src_to_dst,
None => panic!("Missing source to destination"),
};
let dst = &src_to_dst[path];
if path == dst || !Self::check(&path.to_path_buf(), &dst) {
continue;
}
if let Err(err) = fs::rename(path, &dst) {
eprintln!(
"Error: failed to move '{}' to '{}', underlying error: {}",
path.display(),
&dst.display(),
err
);
}
}
}
Ok(())
Expand Down
78 changes: 71 additions & 7 deletions tests/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,23 +114,87 @@ mod cli {
let file_path_dst = tmp_dir_path.join(file_path_component);
let prefix = file_path_dst.parent().unwrap();
std::fs::create_dir_all(prefix).unwrap();
assert!(Path::exists(&file_path));
fs::copy(file_path, &file_path_dst).expect("Error copying file");
assert!(Path::exists(&file_path_dst));
let command = ren()
ren()
.current_dir(tmp_dir_path)
.write_stdin(input)
.args(&["changes", "altered", "-w"])
.assert()
.success();
let output = command.get_output();
println!("stdout = {:?}", String::from_utf8_lossy(&output.stdout));
println!("stderr = {:?}", String::from_utf8_lossy(&output.stderr));
assert!(!Path::exists(&file_path_dst));
let file_path_component_moved = "altered dir with spaces/stays dir with spaces two/altered file with spaces";
let file_path_moved = tmp_dir_path.join(file_path_component_moved);
println!("file_path_moved = {:?}", file_path_moved);
assert!(Path::exists(&file_path_moved));
Ok(())
}

#[test]
fn simple_delete_preview() -> Result<()> {
let input = fs::read_to_string("tests/data/simple/find.txt").expect("Error reading input");
let result = fs::read_to_string("tests/data/simple/delete.patch").expect("Error reading input");
ren()
.current_dir("tests/data/simple")
.write_stdin(input)
.args(&["-d"])
.assert()
.success()
.stdout(result);
Ok(())
}

#[test]
fn nested_delete_preview() -> Result<()> {
let input = fs::read_to_string("tests/data/nested/find.txt").expect("Error reading input");
let result = fs::read_to_string("tests/data/nested/delete.patch").expect("Error reading input");
ren()
.current_dir("tests/data/nested")
.write_stdin(input)
.args(&["-d"])
.assert()
.success()
.stdout(result);
Ok(())
}

#[test]
fn simple_delete() -> Result<()> {
let input = fs::read_to_string("tests/data/simple/find.txt").expect("Error reading input");
let file_path_component = "changes";
let file_path = Path::new("tests/data/simple").join(file_path_component);
let tmp_dir = tempfile::tempdir()?;
let tmp_dir_path = tmp_dir.path();
let file_path_dst = tmp_dir_path.join(file_path_component);
let prefix = file_path_dst.parent().unwrap();
std::fs::create_dir_all(prefix).unwrap();
fs::copy(file_path, &file_path_dst).expect("Error copying file");
ren()
.current_dir(tmp_dir_path)
.write_stdin(input)
.args(&["-d", "-w"])
.assert()
.success();
assert!(!Path::exists(&file_path_dst));
Ok(())
}

#[test]
fn nested_delete() -> Result<()> {
let input = fs::read_to_string("tests/data/nested/find.txt").expect("Error reading input");
let file_path_component = "changes dir with spaces/stays dir with spaces two/changes file with spaces";
let file_path = Path::new("tests/data/nested").join(file_path_component);
let tmp_dir = tempfile::tempdir()?;
let tmp_dir_path = tmp_dir.path();
let file_path_dst = tmp_dir_path.join(file_path_component);
let prefix = file_path_dst.parent().unwrap();
std::fs::create_dir_all(prefix).unwrap();
fs::copy(file_path, &file_path_dst).expect("Error copying file");
ren()
.current_dir(tmp_dir_path)
.write_stdin(input)
.args(&["-d", "-w"])
.assert()
.success();
assert!(!Path::exists(&file_path_dst));
Ok(())
}
}
6 changes: 6 additions & 0 deletions tests/data/nested/delete.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
--- original
+++ modified
@@ -1,2 +0,0 @@
-changes dir with spaces/stays dir with spaces two/changes file with spaces
-changes dir with spaces
\ No newline at end of file
13 changes: 13 additions & 0 deletions tests/data/nested/generate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,16 @@ line_fix='$a\
sed -i.bak "${line_fix}" patch.patch

rm patch.patch.bak

# Delete

diff --unified <(echo "$sorted") <(printf "") > delete.patch || true

sed -i.bak '1s/.*/--- original/' delete.patch
sed -i.bak '2s/.*/+++ modified/' delete.patch
line_fix='$a\
\\ No newline at end of file
'
sed -i.bak "${line_fix}" delete.patch

rm delete.patch.bak
5 changes: 5 additions & 0 deletions tests/data/simple/delete.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
--- original
+++ modified
@@ -1 +0,0 @@
-changes
\ No newline at end of file
14 changes: 13 additions & 1 deletion tests/data/simple/generate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,17 @@ line_fix='$a\
\\ No newline at end of file
'
sed -i.bak "${line_fix}" patch.patch

rm patch.patch.bak

# Delete

diff --unified find.txt <(printf "") > delete.patch || true

sed -i.bak '1s/.*/--- original/' delete.patch
sed -i.bak '2s/.*/+++ modified/' delete.patch
line_fix='$a\
\\ No newline at end of file
'
sed -i.bak "${line_fix}" delete.patch

rm delete.patch.bak

0 comments on commit 2dfe669

Please sign in to comment.