From 278de0e5177f3299c27c51264d23f16b7ce1b2e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Wed, 14 Aug 2019 15:04:44 -0500 Subject: [PATCH 1/7] cp refactoring. --- src/commands/cp.rs | 159 +++++--------------------------------- tests/command_cp_tests.rs | 14 ++-- 2 files changed, 27 insertions(+), 146 deletions(-) diff --git a/src/commands/cp.rs b/src/commands/cp.rs index c0cb365444..d3f2e913cd 100644 --- a/src/commands/cp.rs +++ b/src/commands/cp.rs @@ -2,11 +2,12 @@ use crate::errors::ShellError; use crate::parser::hir::SyntaxType; use crate::parser::registry::{CommandRegistry, Signature}; use crate::prelude::*; -use std::path::{Path, PathBuf}; +use crate::utils::FileStructure; +use std::path::PathBuf; -pub struct Copycp; +pub struct Cpy; -impl StaticCommand for Copycp { +impl StaticCommand for Cpy { fn run( &self, args: CommandArgs, @@ -26,75 +27,6 @@ impl StaticCommand for Copycp { } } -#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct Res { - pub loc: PathBuf, - pub at: usize, -} - -impl Res {} - -pub struct FileStructure { - root: PathBuf, - resources: Vec, -} - -impl FileStructure { - pub fn new() -> FileStructure { - FileStructure { - root: PathBuf::new(), - resources: Vec::::new(), - } - } - - pub fn set_root(&mut self, path: &Path) { - self.root = path.to_path_buf(); - } - - pub fn paths_applying_with(&mut self, to: F) -> Vec<(PathBuf, PathBuf)> - where - F: Fn((PathBuf, usize)) -> (PathBuf, PathBuf), - { - self.resources - .iter() - .map(|f| (PathBuf::from(&f.loc), f.at)) - .map(|f| to(f)) - .collect() - } - - pub fn walk_decorate(&mut self, start_path: &Path) { - self.set_root(&dunce::canonicalize(start_path).unwrap()); - self.resources = Vec::::new(); - self.build(start_path, 0); - self.resources.sort(); - } - - fn build(&mut self, src: &'a Path, lvl: usize) { - let source = dunce::canonicalize(src).unwrap(); - - if source.is_dir() { - for entry in std::fs::read_dir(&source).unwrap() { - let entry = entry.unwrap(); - let path = entry.path(); - - if path.is_dir() { - self.build(&path, lvl + 1); - } - - self.resources.push(Res { - loc: path.to_path_buf(), - at: lvl, - }); - } - } else { - self.resources.push(Res { - loc: source, - at: lvl, - }); - } - } -} - pub fn cp(args: CommandArgs, registry: &CommandRegistry) -> Result { let mut source = PathBuf::from(args.shell_manager.path()); let mut destination = PathBuf::from(args.shell_manager.path()); @@ -306,10 +238,10 @@ pub fn cp(args: CommandArgs, registry: &CommandRegistry) -> Result Result { - return Err(ShellError::labeled_error( - e.to_string(), - e.to_string(), - name_span, - )); - } - Ok(o) => o, - }; + if entry.is_file() { + match std::fs::copy(&entry, &to) { + Err(e) => { + return Err(ShellError::labeled_error( + e.to_string(), + e.to_string(), + args.nth(0).unwrap().span(), + )); + } + Ok(o) => o, + }; + } } } } else { @@ -348,58 +282,3 @@ pub fn cp(args: CommandArgs, registry: &CommandRegistry) -> Result PathBuf { - let mut sdx = PathBuf::new(); - sdx.push("tests"); - sdx.push("fixtures"); - sdx.push("formats"); - dunce::canonicalize(sdx).unwrap() - } - - #[test] - fn prepares_and_decorates_source_files_for_copying() { - let mut res = FileStructure::new(); - res.walk_decorate(fixtures().as_path()); - - assert_eq!( - res.resources, - vec![ - Res { - loc: fixtures().join("appveyor.yml"), - at: 0 - }, - Res { - loc: fixtures().join("caco3_plastics.csv"), - at: 0 - }, - Res { - loc: fixtures().join("cargo_sample.toml"), - at: 0 - }, - Res { - loc: fixtures().join("jonathan.xml"), - at: 0 - }, - Res { - loc: fixtures().join("sample.ini"), - at: 0 - }, - Res { - loc: fixtures().join("sgml_description.json"), - at: 0 - }, - Res { - loc: fixtures().join("utf16.ini"), - at: 0 - } - ] - ); - } -} diff --git a/tests/command_cp_tests.rs b/tests/command_cp_tests.rs index 2907155e88..85c17f057e 100644 --- a/tests/command_cp_tests.rs +++ b/tests/command_cp_tests.rs @@ -7,7 +7,7 @@ use std::path::{Path, PathBuf}; #[test] fn copies_a_file() { - let sandbox = Playground::setup_for("cp_test").test_dir_name(); + let sandbox = Playground::setup_for("cp_test_1").test_dir_name(); let full_path = format!("{}/{}", Playground::root(), sandbox); let expected_file = format!("{}/{}", full_path, "sample.ini"); @@ -15,7 +15,7 @@ fn copies_a_file() { nu!( _output, cwd(&Playground::root()), - "cp ../formats/sample.ini cp_test/sample.ini" + "cp ../formats/sample.ini cp_test_1/sample.ini" ); assert!(h::file_exists_at(PathBuf::from(expected_file))); @@ -56,7 +56,7 @@ fn copies_the_directory_inside_directory_if_path_to_copy_is_directory_and_with_r EmptyFile("jonathan.txt"), EmptyFile("andres.txt"), ]) - .within("copies_expected") + .mkdir("copies_expected") .test_dir_name(); let full_path = format!("{}/{}", Playground::root(), sandbox); @@ -114,7 +114,7 @@ fn deep_copies_with_recursive_flag() { .with_files(vec![EmptyFile("coverage.txt"), EmptyFile("commands.txt")]) .within("originals/contributors/yehuda") .with_files(vec![EmptyFile("defer-evaluation.txt")]) - .within("copies_expected") + .mkdir("copies_expected") .test_dir_name(); let full_path = format!("{}/{}", Playground::root(), sandbox); @@ -162,7 +162,8 @@ fn copies_using_path_with_wildcard() { Path::new("cargo_sample.toml"), Path::new("jonathan.xml"), Path::new("sample.ini"), - Path::new("sgml_description.json") + Path::new("sgml_description.json"), + Path::new("utf16.ini"), ], PathBuf::from(&expected_copies_path) )); @@ -185,7 +186,8 @@ fn copies_using_a_glob() { Path::new("cargo_sample.toml"), Path::new("jonathan.xml"), Path::new("sample.ini"), - Path::new("sgml_description.json") + Path::new("sgml_description.json"), + Path::new("utf16.ini"), ], PathBuf::from(&expected_copies_path) )); From bd71773b5b2c3bd11907a8eacc2faf2e0b528ae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Wed, 14 Aug 2019 15:05:27 -0500 Subject: [PATCH 2/7] Extracted File traversal utility for use in other commands. --- src/utils.rs | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/src/utils.rs b/src/utils.rs index 4177451234..95642d1d7a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -70,3 +70,129 @@ impl> Div for &RelativePath { RelativePath::new(result) } } + + +#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Res { + pub loc: PathBuf, + pub at: usize, +} + +impl Res {} + +pub struct FileStructure { + root: PathBuf, + pub resources: Vec, +} + +impl FileStructure { + pub fn new() -> FileStructure { + FileStructure { + root: PathBuf::new(), + resources: Vec::::new(), + } + } + + pub fn set_root(&mut self, path: &Path) { + self.root = path.to_path_buf(); + } + + pub fn paths_applying_with(&mut self, to: F) -> Vec<(PathBuf, PathBuf)> + where + F: Fn((PathBuf, usize)) -> (PathBuf, PathBuf), + { + self.resources + .iter() + .map(|f| (PathBuf::from(&f.loc), f.at)) + .map(|f| to(f)) + .collect() + } + + pub fn walk_decorate(&mut self, start_path: &Path) { + self.set_root(&dunce::canonicalize(start_path).unwrap()); + self.resources = Vec::::new(); + self.build(start_path, 0); + self.resources.sort(); + } + + fn build(&mut self, src: &'a Path, lvl: usize) { + let source = dunce::canonicalize(src).unwrap(); + + if source.is_dir() { + for entry in std::fs::read_dir(&source).unwrap() { + let entry = entry.unwrap(); + let path = entry.path(); + + if path.is_dir() { + self.build(&path, lvl + 1); + } + + self.resources.push(Res { + loc: path.to_path_buf(), + at: lvl, + }); + } + } else { + self.resources.push(Res { + loc: source, + at: lvl, + }); + } + } +} + +#[cfg(test)] +mod tests { + + use super::{FileStructure, Res}; + use std::path::PathBuf; + + fn fixtures() -> PathBuf { + let mut sdx = PathBuf::new(); + sdx.push("tests"); + sdx.push("fixtures"); + sdx.push("formats"); + dunce::canonicalize(sdx).unwrap() + } + + #[test] + fn prepares_and_decorates_source_files_for_copying() { + let mut res = FileStructure::new(); + res.walk_decorate(fixtures().as_path()); + + assert_eq!( + res.resources, + vec![ + Res { + loc: fixtures().join("appveyor.yml"), + at: 0 + }, + Res { + loc: fixtures().join("caco3_plastics.csv"), + at: 0 + }, + Res { + loc: fixtures().join("cargo_sample.toml"), + at: 0 + }, + Res { + loc: fixtures().join("jonathan.xml"), + at: 0 + }, + Res { + loc: fixtures().join("sample.ini"), + at: 0 + }, + Res { + loc: fixtures().join("sgml_description.json"), + at: 0 + }, + Res { + loc: fixtures().join("utf16.ini"), + at: 0 + } + ] + ); + } +} + From dfcbaed1c695801b19ff1f4dfdff5d6f9a09b04f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Wed, 14 Aug 2019 15:07:08 -0500 Subject: [PATCH 3/7] More descriptive test playground names for commands other than the basic ones. --- tests/command_cd_tests.rs | 7 +----- tests/command_mkdir_tests.rs | 2 +- tests/commands_test.rs | 20 +++++++++--------- tests/filter_inc_tests.rs | 41 +++++++++--------------------------- tests/helpers/mod.rs | 7 ++++-- 5 files changed, 27 insertions(+), 50 deletions(-) diff --git a/tests/command_cd_tests.rs b/tests/command_cd_tests.rs index 626a59f795..c0de7cb06f 100644 --- a/tests/command_cd_tests.rs +++ b/tests/command_cd_tests.rs @@ -1,15 +1,10 @@ mod helpers; use helpers::in_directory as cwd; -use helpers::Playground; #[test] fn cd_directory_not_found() { - let sandbox = Playground::setup_for("cd_directory_not_found_test").test_dir_name(); - - let full_path = format!("{}/{}", Playground::root(), sandbox); - - nu_error!(output, cwd(&full_path), "cd dir_that_does_not_exist"); + nu_error!(output, cwd("tests/fixtures"), "cd dir_that_does_not_exist"); assert!(output.contains("dir_that_does_not_exist")); assert!(output.contains("directory not found")); diff --git a/tests/command_mkdir_tests.rs b/tests/command_mkdir_tests.rs index 9cbb10755c..ebb8c4adf8 100644 --- a/tests/command_mkdir_tests.rs +++ b/tests/command_mkdir_tests.rs @@ -6,7 +6,7 @@ use std::path::PathBuf; #[test] fn creates_directory() { - let sandbox = Playground::setup_for("mkdir_test").test_dir_name(); + let sandbox = Playground::setup_for("mkdir_test_1").test_dir_name(); let full_path = format!("{}/{}", Playground::root(), sandbox); diff --git a/tests/commands_test.rs b/tests/commands_test.rs index 54c178aa51..748214c83c 100644 --- a/tests/commands_test.rs +++ b/tests/commands_test.rs @@ -125,7 +125,7 @@ fn save_figures_out_intelligently_where_to_write_out_with_metadata() { #[test] fn save_can_write_out_csv() { - let sandbox = Playground::setup_for("save_test").test_dir_name(); + let sandbox = Playground::setup_for("save_writes_out_csv_test").test_dir_name(); let full_path = format!("{}/{}", Playground::root(), sandbox); let expected_file = format!("{}/{}", full_path, "cargo_sample.csv"); @@ -133,7 +133,7 @@ fn save_can_write_out_csv() { nu!( _output, cwd(&Playground::root()), - "open ../formats/cargo_sample.toml | inc package.version --minor | get package | save save_test/cargo_sample.csv" + "open ../formats/cargo_sample.toml | inc package.version --minor | get package | save save_writes_out_csv_test/cargo_sample.csv" ); let actual = h::file_contents(&expected_file); @@ -142,14 +142,14 @@ fn save_can_write_out_csv() { #[test] fn rm_removes_a_file() { - let sandbox = Playground::setup_for("rm_test") + let sandbox = Playground::setup_for("rm_regular_file_test") .with_files(vec![EmptyFile("i_will_be_deleted.txt")]) .test_dir_name(); nu!( _output, cwd(&Playground::root()), - "rm rm_test/i_will_be_deleted.txt" + "rm rm_regular_file_test/i_will_be_deleted.txt" ); let path = &format!( @@ -180,7 +180,7 @@ fn rm_removes_files_with_wildcard() { src/parser/hir/baseline_parse_tokens.rs "#; - let sandbox = Playground::setup_for("rm_test_wildcard") + let sandbox = Playground::setup_for("rm_wildcard_test") .within("src") .with_files(vec![ EmptyFile("cli.rs"), @@ -210,7 +210,7 @@ fn rm_removes_files_with_wildcard() { nu!( _output, - cwd("tests/fixtures/nuplayground/rm_test_wildcard"), + cwd("tests/fixtures/nuplayground/rm_wildcard_test"), "rm \"src/*/*/*.rs\"" ); @@ -231,7 +231,7 @@ fn rm_removes_files_with_wildcard() { #[test] fn rm_removes_directory_contents_with_recursive_flag() { - let sandbox = Playground::setup_for("rm_test_recursive") + let sandbox = Playground::setup_for("rm_directory_removal_recursively_test") .with_files(vec![ EmptyFile("yehuda.txt"), EmptyFile("jonathan.txt"), @@ -242,7 +242,7 @@ fn rm_removes_directory_contents_with_recursive_flag() { nu!( _output, cwd("tests/fixtures/nuplayground"), - "rm rm_test_recursive --recursive" + "rm rm_directory_removal_recursively_test --recursive" ); let expected = format!("{}/{}", Playground::root(), sandbox); @@ -252,10 +252,10 @@ fn rm_removes_directory_contents_with_recursive_flag() { #[test] fn rm_errors_if_attempting_to_delete_a_directory_without_recursive_flag() { - let sandbox = Playground::setup_for("rm_test_2").test_dir_name(); + let sandbox = Playground::setup_for("rm_prevent_directory_removal_without_flag_test").test_dir_name(); let full_path = format!("{}/{}", Playground::root(), sandbox); - nu_error!(output, cwd(&Playground::root()), "rm rm_test_2"); + nu_error!(output, cwd(&Playground::root()), "rm rm_prevent_directory_removal_without_flag_test"); assert!(h::file_exists_at(PathBuf::from(full_path))); assert!(output.contains("is a directory")); diff --git a/tests/filter_inc_tests.rs b/tests/filter_inc_tests.rs index d6f1aff8cf..7c16255e48 100644 --- a/tests/filter_inc_tests.rs +++ b/tests/filter_inc_tests.rs @@ -16,7 +16,7 @@ fn can_only_apply_one() { #[test] fn regular_field_by_one() { - Playground::setup_for("plugin_inc_test_1") + Playground::setup_for("plugin_inc_by_one_test") .with_files(vec![FileWithContent( "sample.toml", r#" @@ -27,37 +27,16 @@ fn regular_field_by_one() { nu!( output, - cwd("tests/fixtures/nuplayground/plugin_inc_test_1"), + cwd("tests/fixtures/nuplayground/plugin_inc_by_one_test"), "open sample.toml | inc package.edition | get package.edition | echo $it" ); assert_eq!(output, "2019"); } - -#[test] -fn by_one_without_passing_field() { - Playground::setup_for("plugin_inc_test_2") - .with_files(vec![FileWithContent( - "sample.toml", - r#" - [package] - contributors = "2" - "#, - )]); - - nu!( - output, - cwd("tests/fixtures/nuplayground/plugin_inc_test_2"), - "open sample.toml | get package.contributors | inc | echo $it" - ); - - assert_eq!(output, "3"); -} - #[test] fn semversion_major_inc() { - Playground::setup_for("plugin_inc_test_3") + Playground::setup_for("plugin_inc_major_semversion_test") .with_files(vec![FileWithContent( "sample.toml", r#" @@ -68,7 +47,7 @@ fn semversion_major_inc() { nu!( output, - cwd("tests/fixtures/nuplayground/plugin_inc_test_3"), + cwd("tests/fixtures/nuplayground/plugin_inc_major_semversion_test"), "open sample.toml | inc package.version --major | get package.version | echo $it" ); @@ -77,7 +56,7 @@ fn semversion_major_inc() { #[test] fn semversion_minor_inc() { - Playground::setup_for("plugin_inc_test_4") + Playground::setup_for("plugin_inc_minor_semversion_test") .with_files(vec![FileWithContent( "sample.toml", r#" @@ -88,7 +67,7 @@ fn semversion_minor_inc() { nu!( output, - cwd("tests/fixtures/nuplayground/plugin_inc_test_4"), + cwd("tests/fixtures/nuplayground/plugin_inc_minor_semversion_test"), "open sample.toml | inc package.version --minor | get package.version | echo $it" ); @@ -97,7 +76,7 @@ fn semversion_minor_inc() { #[test] fn semversion_patch_inc() { - Playground::setup_for("plugin_inc_test_5") + Playground::setup_for("plugin_inc_patch_semversion_test") .with_files(vec![FileWithContent( "sample.toml", r#" @@ -108,7 +87,7 @@ fn semversion_patch_inc() { nu!( output, - cwd("tests/fixtures/nuplayground/plugin_inc_test_5"), + cwd("tests/fixtures/nuplayground/plugin_inc_patch_semversion_test"), "open sample.toml | inc package.version --patch | get package.version | echo $it" ); @@ -117,7 +96,7 @@ fn semversion_patch_inc() { #[test] fn semversion_without_passing_field() { - Playground::setup_for("plugin_inc_test_6") + Playground::setup_for("plugin_inc_semversion_without_passing_field_test") .with_files(vec![FileWithContent( "sample.toml", r#" @@ -128,7 +107,7 @@ fn semversion_without_passing_field() { nu!( output, - cwd("tests/fixtures/nuplayground/plugin_inc_test_6"), + cwd("tests/fixtures/nuplayground/plugin_inc_semversion_without_passing_field_test"), "open sample.toml | get package.version | inc --patch | echo $it" ); diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs index aa60adc4a8..5defb1356a 100644 --- a/tests/helpers/mod.rs +++ b/tests/helpers/mod.rs @@ -116,8 +116,10 @@ impl Playground { } } - pub fn cd(&mut self, path: &str) -> &mut Self { - self.cwd.push(path); + pub fn mkdir(&mut self, directory: &str) -> &mut Self { + self.cwd.push(directory); + std::fs::create_dir_all(&self.cwd).expect("can not create directory"); + self.back_to_playground(); self } @@ -148,6 +150,7 @@ impl Playground { self } + pub fn glob_vec(pattern: &str) -> Vec { glob(pattern).unwrap().map(|r| r.unwrap()).collect() } From 154063013f10015952e65fdfe4fd95c19c2a6c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Wed, 14 Aug 2019 15:08:10 -0500 Subject: [PATCH 4/7] =?UTF-8?q?mv=20introduced.=20\=C2=A1Viva\!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + src/cli.rs | 3 +- src/commands.rs | 4 +- src/commands/mv.rs | 279 ++++++++++++++++++++++++++++++++++++++ tests/command_mv_tests.rs | 222 ++++++++++++++++++++++++++++++ 5 files changed, 507 insertions(+), 2 deletions(-) create mode 100644 src/commands/mv.rs create mode 100644 tests/command_mv_tests.rs diff --git a/README.md b/README.md index f7075f00a1..a46f84851d 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | cp source path | Copy files | | ls (path) | View the contents of the current or given path | | mkdir path | Make directories, creates intermediary directories as required. | +| mv source target | Move files or directories. | | date (--utc) | Get the current datetime | | ps | View current processes | | sys | View information about the current system | diff --git a/src/cli.rs b/src/cli.rs index 93fccbf185..fe8836a6ae 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -189,9 +189,10 @@ pub async fn cli() -> Result<(), Box> { static_command(Exit), static_command(Clip), static_command(Autoview), - static_command(Copycp), + static_command(Cpy), static_command(Date), static_command(Mkdir), + static_command(Move), static_command(Save), static_command(Table), static_command(VTable), diff --git a/src/commands.rs b/src/commands.rs index e2c3aebd22..b04dd3df6a 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -25,6 +25,7 @@ crate mod get; crate mod lines; crate mod ls; crate mod mkdir; +crate mod mv; crate mod next; crate mod nth; crate mod open; @@ -61,12 +62,13 @@ crate use command::{ RawCommandArgs, StaticCommand, UnevaluatedCallInfo, }; crate use config::Config; -crate use cp::Copycp; +crate use cp::Cpy; crate use date::Date; crate use enter::Enter; crate use exit::Exit; crate use get::Get; crate use mkdir::Mkdir; +crate use mv::Move; crate use open::Open; crate use rm::Remove; crate use save::Save; diff --git a/src/commands/mv.rs b/src/commands/mv.rs new file mode 100644 index 0000000000..c5b6e7095a --- /dev/null +++ b/src/commands/mv.rs @@ -0,0 +1,279 @@ +use crate::errors::ShellError; +use crate::parser::hir::SyntaxType; +use crate::parser::registry::{CommandRegistry, Signature}; +use crate::prelude::*; +use std::path::PathBuf; + +#[cfg(windows)] +use crate::utils::FileStructure; + +pub struct Move; + +impl StaticCommand for Move { + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + mv(args, registry) + } + + fn name(&self) -> &str { + "mv" + } + + fn signature(&self) -> Signature { + Signature::build("mv").named("file", SyntaxType::Any) + } +} + +pub fn mv(args: CommandArgs, registry: &CommandRegistry) -> Result { + let mut source = PathBuf::from(args.shell_manager.path()); + let mut destination = PathBuf::from(args.shell_manager.path()); + let span = args.name_span(); + let args = args.evaluate_once(registry)?; + + match args + .nth(0) + .ok_or_else(|| ShellError::string(&format!("No file or directory specified")))? + .as_string()? + .as_str() + { + file => { + source.push(file); + } + } + + match args + .nth(1) + .ok_or_else(|| ShellError::string(&format!("No file or directory specified")))? + .as_string()? + .as_str() + { + file => { + destination.push(file); + } + } + + let sources = glob::glob(&source.to_string_lossy()); + + if sources.is_err() { + return Err(ShellError::labeled_error( + "Invalid pattern.", + "Invalid pattern.", + args.nth(0).unwrap().span(), + )); + } + + let sources: Vec<_> = sources.unwrap().collect(); + + if sources.len() == 1 { + if let Ok(entry) = &sources[0] { + if destination.exists() && destination.is_dir() { + destination = dunce::canonicalize(&destination).unwrap(); + destination.push(source.file_name().unwrap()); + } + + if entry.is_file() { + match std::fs::rename(&entry, &destination) { + Err(e) => { + return Err(ShellError::labeled_error( + format!( + "Rename {:?} to {:?} aborted. {:}", + entry.file_name().unwrap(), + destination.file_name().unwrap(), + e.to_string(), + ), + format!( + "Rename {:?} to {:?} aborted. {:}", + entry.file_name().unwrap(), + destination.file_name().unwrap(), + e.to_string(), + ), + span, + )); + } + Ok(o) => o, + }; + } + + if entry.is_dir() { + match std::fs::create_dir_all(&destination) { + Err(e) => { + return Err(ShellError::labeled_error( + format!( + "Rename {:?} to {:?} aborted. {:}", + entry.file_name().unwrap(), + destination.file_name().unwrap(), + e.to_string(), + ), + format!( + "Rename {:?} to {:?} aborted. {:}", + entry.file_name().unwrap(), + destination.file_name().unwrap(), + e.to_string(), + ), + span, + )); + } + Ok(o) => o, + }; + #[cfg(not(windows))] + { + match std::fs::rename(&entry, &destination) { + Err(e) => { + return Err(ShellError::labeled_error( + format!( + "Rename {:?} to {:?} aborted. {:}", + entry.file_name().unwrap(), + destination.file_name().unwrap(), + e.to_string(), + ), + format!( + "Rename {:?} to {:?} aborted. {:}", + entry.file_name().unwrap(), + destination.file_name().unwrap(), + e.to_string(), + ), + span, + )); + } + Ok(o) => o, + }; + } + #[cfg(windows)] + { + let mut sources: FileStructure = FileStructure::new(); + + sources.walk_decorate(&entry); + + let strategy = |(source_file, depth_level)| { + let mut new_dst = destination.clone(); + let path = dunce::canonicalize(&source_file).unwrap(); + + let mut comps: Vec<_> = path + .components() + .map(|fragment| fragment.as_os_str()) + .rev() + .take(1 + depth_level) + .collect(); + + comps.reverse(); + + for fragment in comps.iter() { + new_dst.push(fragment); + } + + (PathBuf::from(&source_file), PathBuf::from(new_dst)) + }; + + for (ref src, ref dst) in sources.paths_applying_with(strategy) { + if src.is_dir() { + if !dst.exists() { + match std::fs::create_dir_all(dst) { + Err(e) => { + return Err(ShellError::labeled_error( + format!( + "Rename {:?} to {:?} aborted. {:}", + entry.file_name().unwrap(), + destination.file_name().unwrap(), + e.to_string(), + ), + format!( + "Rename {:?} to {:?} aborted. {:}", + entry.file_name().unwrap(), + destination.file_name().unwrap(), + e.to_string(), + ), + span, + )); + } + Ok(o) => o, + }; + } + } + + if src.is_file() { + match std::fs::rename(src, dst) { + Err(e) => { + return Err(ShellError::labeled_error( + format!( + "Rename {:?} to {:?} aborted. {:}", + entry.file_name().unwrap(), + destination.file_name().unwrap(), + e.to_string(), + ), + format!( + "Rename {:?} to {:?} aborted. {:}", + entry.file_name().unwrap(), + destination.file_name().unwrap(), + e.to_string(), + ), + span, + )); + } + Ok(o) => o, + }; + } + } + + std::fs::remove_dir_all(entry).expect("can not remove directory"); + } + } + } + } else { + if destination.exists() { + if !sources.iter().all(|x| (x.as_ref().unwrap()).is_file()) { + return Err(ShellError::labeled_error( + "Rename aborted (directories found).", + "Rename aborted (directories found).", + args.nth(0).unwrap().span(), + )); + } + + for entry in sources { + if let Ok(entry) = entry { + let mut to = PathBuf::from(&destination); + to.push(&entry.file_name().unwrap()); + + if entry.is_file() { + match std::fs::rename(&entry, &to) { + Err(e) => { + return Err(ShellError::labeled_error( + format!( + "Rename {:?} to {:?} aborted. {:}", + entry.file_name().unwrap(), + destination.file_name().unwrap(), + e.to_string(), + ), + format!( + "Rename {:?} to {:?} aborted. {:}", + entry.file_name().unwrap(), + destination.file_name().unwrap(), + e.to_string(), + ), + span, + )); + } + Ok(o) => o, + }; + } + } + } + } else { + return Err(ShellError::labeled_error( + format!( + "Rename aborted. (Does {:?} exist?)", + &destination.file_name().unwrap() + ), + format!( + "Rename aborted. (Does {:?} exist?)", + &destination.file_name().unwrap() + ), + args.nth(1).unwrap().span(), + )); + } + } + + Ok(OutputStream::empty()) +} diff --git a/tests/command_mv_tests.rs b/tests/command_mv_tests.rs new file mode 100644 index 0000000000..62132c7f2a --- /dev/null +++ b/tests/command_mv_tests.rs @@ -0,0 +1,222 @@ +mod helpers; + +use h::{in_directory as cwd, Playground, Stub::*}; +use helpers as h; + +use std::path::{Path, PathBuf}; + +#[test] +fn moves_a_file() { + let sandbox = Playground::setup_for("mv_test_1") + .with_files(vec![ + EmptyFile("andres.txt"), + ]) + .mkdir("expected") + .test_dir_name(); + + let full_path = format!("{}/{}", Playground::root(), sandbox); + let original = format!("{}/{}", full_path, "andres.txt"); + let expected = format!("{}/{}", full_path, "expected/yehuda.txt"); + + nu!( + _output, + cwd(&full_path), + "mv andres.txt expected/yehuda.txt" + ); + + assert!(!h::file_exists_at(PathBuf::from(original))); + assert!(h::file_exists_at(PathBuf::from(expected))); +} + +#[test] +fn overwrites_if_moving_to_existing_file() { + let sandbox = Playground::setup_for("mv_test_2") + .with_files(vec![ + EmptyFile("andres.txt"), + EmptyFile("jonathan.txt"), + ]) + .test_dir_name(); + + let full_path = format!("{}/{}", Playground::root(), sandbox); + let original = format!("{}/{}", full_path, "andres.txt"); + let expected = format!("{}/{}", full_path, "jonathan.txt"); + + nu!( + _output, + cwd(&full_path), + "mv andres.txt jonathan.txt" + ); + + assert!(!h::file_exists_at(PathBuf::from(original))); + assert!(h::file_exists_at(PathBuf::from(expected))); +} + +#[test] +fn moves_a_directory() { + let sandbox = Playground::setup_for("mv_test_3") + .mkdir("empty_dir") + .test_dir_name(); + + let full_path = format!("{}/{}", Playground::root(), sandbox); + let original_dir = format!("{}/{}", full_path, "empty_dir"); + let expected = format!("{}/{}", full_path, "renamed_dir"); + + nu!( + _output, + cwd(&full_path), + "mv empty_dir renamed_dir" + ); + + assert!(!h::dir_exists_at(PathBuf::from(original_dir))); + assert!(h::dir_exists_at(PathBuf::from(expected))); +} + +#[test] +fn moves_the_file_inside_directory_if_path_to_move_is_existing_directory() { + let sandbox = Playground::setup_for("mv_test_4") + .with_files(vec![ + EmptyFile("jonathan.txt"), + ]) + .mkdir("expected") + .test_dir_name(); + + let full_path = format!("{}/{}", Playground::root(), sandbox); + let original_dir = format!("{}/{}", full_path, "jonathan.txt"); + let expected = format!("{}/{}", full_path, "expected/jonathan.txt"); + + nu!( + _output, + cwd(&full_path), + "mv jonathan.txt expected" + ); + + + assert!(!h::file_exists_at(PathBuf::from(original_dir))); + assert!(h::file_exists_at(PathBuf::from(expected))); +} + +#[test] +fn moves_the_directory_inside_directory_if_path_to_move_is_existing_directory() { + let sandbox = Playground::setup_for("mv_test_5") + .within("contributors") + .with_files(vec![ + EmptyFile("jonathan.txt"), + ]) + .mkdir("expected") + .test_dir_name(); + + let full_path = format!("{}/{}", Playground::root(), sandbox); + let original_dir = format!("{}/{}", full_path, "contributors"); + let expected = format!("{}/{}", full_path, "expected/contributors"); + + nu!( + _output, + cwd(&full_path), + "mv contributors expected" + ); + + + assert!(!h::dir_exists_at(PathBuf::from(original_dir))); + assert!(h::file_exists_at(PathBuf::from(expected))); +} + +#[test] +fn moves_the_directory_inside_directory_if_path_to_move_is_nonexistent_directory() { + let sandbox = Playground::setup_for("mv_test_6") + .within("contributors") + .with_files(vec![ + EmptyFile("jonathan.txt"), + ]) + .mkdir("expected") + .test_dir_name(); + + let full_path = format!("{}/{}", Playground::root(), sandbox); + let original_dir = format!("{}/{}", full_path, "contributors"); + + nu!( + _output, + cwd(&full_path), + "mv contributors expected/this_dir_exists_now/los_tres_amigos" + ); + + let expected = format!("{}/{}", full_path, "expected/this_dir_exists_now/los_tres_amigos"); + + assert!(!h::dir_exists_at(PathBuf::from(original_dir))); + assert!(h::file_exists_at(PathBuf::from(expected))); +} + +#[test] +fn moves_using_path_with_wildcard() { + let sandbox = Playground::setup_for("mv_test_7") + .within("originals") + .with_files(vec![ + EmptyFile("andres.ini"), + EmptyFile("caco3_plastics.csv"), + EmptyFile("cargo_sample.toml"), + EmptyFile("jonathan.ini"), + EmptyFile("jonathan.xml"), + EmptyFile("sgml_description.json"), + EmptyFile("sample.ini"), + EmptyFile("utf16.ini"), + EmptyFile("yehuda.ini"), + ]) + .mkdir("work_dir") + .mkdir("expected") + .test_dir_name(); + + let full_path = format!("{}/{}", Playground::root(), sandbox); + let work_dir = format!("{}/{}", full_path, "work_dir"); + let expected_copies_path = format!("{}/{}", full_path, "expected"); + + nu!( + _output, + cwd(&work_dir), + "mv ../originals/*.ini ../expected" + ); + + assert!(h::files_exist_at( + vec![ + Path::new("yehuda.ini"), + Path::new("jonathan.ini"), + Path::new("sample.ini"), + Path::new("andres.ini"), + ], + PathBuf::from(&expected_copies_path) + )); +} + + +#[test] +fn moves_using_a_glob() { + let sandbox = Playground::setup_for("mv_test_8") + .within("meals") + .with_files(vec![ + EmptyFile("arepa.txt"), + EmptyFile("empanada.txt"), + EmptyFile("taquiza.txt"), + ]) + .mkdir("work_dir") + .mkdir("expected") + .test_dir_name(); + + let full_path = format!("{}/{}", Playground::root(), sandbox); + let meal_dir = format!("{}/{}", full_path, "meals"); + let work_dir = format!("{}/{}", full_path, "work_dir"); + let expected_copies_path = format!("{}/{}", full_path, "expected"); + + nu!( + _output, + cwd(&work_dir), + "mv ../meals/* ../expected" + ); + + assert!(h::dir_exists_at(PathBuf::from(meal_dir))); + assert!(h::files_exist_at( + vec![ + Path::new("arepa.txt"), + Path::new("empanada.txt"), + Path::new("taquiza.txt"), + ], + PathBuf::from(&expected_copies_path) + )); +} \ No newline at end of file From 121237ee4b0db24c180c8c4ec6c3a99080da6048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Wed, 14 Aug 2019 15:55:26 -0500 Subject: [PATCH 5/7] Reverted test removal from (dfcbaed) --- tests/filter_inc_tests.rs | 27 ++++++++++++++++++++++++--- tests/helpers/mod.rs | 1 - 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/tests/filter_inc_tests.rs b/tests/filter_inc_tests.rs index 7c16255e48..430c3076c1 100644 --- a/tests/filter_inc_tests.rs +++ b/tests/filter_inc_tests.rs @@ -15,8 +15,8 @@ fn can_only_apply_one() { } #[test] -fn regular_field_by_one() { - Playground::setup_for("plugin_inc_by_one_test") +fn by_one_with_field_passed() { + Playground::setup_for("plugin_inc_by_one_with_field_passed_test") .with_files(vec![FileWithContent( "sample.toml", r#" @@ -27,13 +27,34 @@ fn regular_field_by_one() { nu!( output, - cwd("tests/fixtures/nuplayground/plugin_inc_by_one_test"), + cwd("tests/fixtures/nuplayground/plugin_inc_by_one_with_field_passed_test"), "open sample.toml | inc package.edition | get package.edition | echo $it" ); assert_eq!(output, "2019"); } +#[test] +fn by_one_with_no_field_passed() { + Playground::setup_for("plugin_inc_by_one_with_no_field_passed_test") + .with_files(vec![FileWithContent( + "sample.toml", + r#" + [package] + contributors = "2" + "#, + )]); + + nu!( + output, + cwd("tests/fixtures/nuplayground/plugin_inc_by_one_with_no_field_passed_test"), + "open sample.toml | get package.contributors | inc | echo $it" + ); + + assert_eq!(output, "3"); +} + + #[test] fn semversion_major_inc() { Playground::setup_for("plugin_inc_major_semversion_test") diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs index 5defb1356a..0df348f946 100644 --- a/tests/helpers/mod.rs +++ b/tests/helpers/mod.rs @@ -150,7 +150,6 @@ impl Playground { self } - pub fn glob_vec(pattern: &str) -> Vec { glob(pattern).unwrap().map(|r| r.unwrap()).collect() } From 75e3c6f547adb43d3da2dfa600c3632dcc09799b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Wed, 14 Aug 2019 16:05:35 -0500 Subject: [PATCH 6/7] copies_expected -> expected. Use expected as test dirname expectation outcomes from now on. --- tests/command_cp_tests.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/command_cp_tests.rs b/tests/command_cp_tests.rs index 85c17f057e..cf2037770e 100644 --- a/tests/command_cp_tests.rs +++ b/tests/command_cp_tests.rs @@ -56,16 +56,16 @@ fn copies_the_directory_inside_directory_if_path_to_copy_is_directory_and_with_r EmptyFile("jonathan.txt"), EmptyFile("andres.txt"), ]) - .mkdir("copies_expected") + .mkdir("expected") .test_dir_name(); let full_path = format!("{}/{}", Playground::root(), sandbox); - let expected_dir = format!("{}/{}", full_path, "copies_expected/originals"); + let expected_dir = format!("{}/{}", full_path, "expected/originals"); nu!( _output, cwd(&full_path), - "cp originals copies_expected --recursive" + "cp originals expected --recursive" ); assert!(h::dir_exists_at(PathBuf::from(&expected_dir))); @@ -114,11 +114,11 @@ fn deep_copies_with_recursive_flag() { .with_files(vec![EmptyFile("coverage.txt"), EmptyFile("commands.txt")]) .within("originals/contributors/yehuda") .with_files(vec![EmptyFile("defer-evaluation.txt")]) - .mkdir("copies_expected") + .mkdir("expected") .test_dir_name(); let full_path = format!("{}/{}", Playground::root(), sandbox); - let expected_dir = format!("{}/{}", full_path, "copies_expected/originals"); + let expected_dir = format!("{}/{}", full_path, "expected/originals"); let jonathans_expected_copied_dir = format!("{}/contributors/jonathan", expected_dir); let andres_expected_copied_dir = format!("{}/contributors/andres", expected_dir); @@ -127,7 +127,7 @@ fn deep_copies_with_recursive_flag() { nu!( _output, cwd(&full_path), - "cp originals copies_expected --recursive" + "cp originals expected --recursive" ); assert!(h::dir_exists_at(PathBuf::from(&expected_dir))); From cbf6eed2d70edf9ed986e363c33e26ea62eca3f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Wed, 14 Aug 2019 16:44:23 -0500 Subject: [PATCH 7/7] Communicates unsupported feature the case where directories are found in globs whether we cp or mv --- src/commands/cp.rs | 4 ++-- src/commands/mv.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/commands/cp.rs b/src/commands/cp.rs index d3f2e913cd..17fef68651 100644 --- a/src/commands/cp.rs +++ b/src/commands/cp.rs @@ -240,8 +240,8 @@ pub fn cp(args: CommandArgs, registry: &CommandRegistry) -> Result Result