diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index 56e631dea6..1ea6461298 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -207,7 +207,6 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState { Ls, UMkdir, Mktemp, - Mv, UMv, UCp, Open, diff --git a/crates/nu-command/src/filesystem/mod.rs b/crates/nu-command/src/filesystem/mod.rs index bc62a39637..acfa54fee3 100644 --- a/crates/nu-command/src/filesystem/mod.rs +++ b/crates/nu-command/src/filesystem/mod.rs @@ -3,7 +3,6 @@ mod du; mod glob; mod ls; mod mktemp; -mod mv; mod open; mod rm; mod save; @@ -21,7 +20,6 @@ pub use du::Du; pub use glob::Glob; pub use ls::Ls; pub use mktemp::Mktemp; -pub use mv::Mv; pub use rm::Rm; pub use save::Save; pub use start::Start; diff --git a/crates/nu-command/src/filesystem/mv.rs b/crates/nu-command/src/filesystem/mv.rs deleted file mode 100644 index 83e257f6c1..0000000000 --- a/crates/nu-command/src/filesystem/mv.rs +++ /dev/null @@ -1,355 +0,0 @@ -use std::path::{Path, PathBuf}; - -use super::util::try_interaction; -use nu_engine::env::current_dir; -use nu_engine::CallExt; -use nu_protocol::ast::Call; -use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{ - Category, Example, IntoInterruptiblePipelineData, NuGlob, PipelineData, ShellError, Signature, - Span, Spanned, SyntaxShape, Type, Value, -}; - -#[derive(Clone)] -pub struct Mv; - -impl Command for Mv { - fn name(&self) -> &str { - "mv" - } - - fn usage(&self) -> &str { - "Move files or directories." - } - - fn search_terms(&self) -> Vec<&str> { - vec!["move"] - } - - fn signature(&self) -> nu_protocol::Signature { - Signature::build("mv") - .input_output_types(vec![(Type::Nothing, Type::Nothing)]) - .required( - "source", - SyntaxShape::GlobPattern, - "The location to move files/directories from.", - ) - .required( - "destination", - SyntaxShape::Filepath, - "The location to move files/directories to.", - ) - .switch( - "verbose", - "make mv to be verbose, showing files been moved.", - Some('v'), - ) - .switch("force", "overwrite the destination.", Some('f')) - .switch("interactive", "ask user to confirm action", Some('i')) - .switch("update", - "move only when the SOURCE file is newer than the destination file(with -f) or when the destination file is missing", - Some('u') - ) - // TODO: add back in additional features - .category(Category::FileSystem) - } - - fn run( - &self, - engine_state: &EngineState, - stack: &mut Stack, - call: &Call, - _input: PipelineData, - ) -> Result { - // TODO: handle invalid directory or insufficient permissions when moving - let mut spanned_source: Spanned = call.req(engine_state, stack, 0)?; - spanned_source.item = spanned_source.item.strip_ansi_string_unlikely(); - let spanned_destination: Spanned = call.req(engine_state, stack, 1)?; - let verbose = call.has_flag(engine_state, stack, "verbose")?; - let interactive = call.has_flag(engine_state, stack, "interactive")?; - let force = call.has_flag(engine_state, stack, "force")?; - let update_mode = call.has_flag(engine_state, stack, "update")?; - - let ctrlc = engine_state.ctrlc.clone(); - - let path = current_dir(engine_state, stack)?; - let destination = path.join(spanned_destination.item.as_str()); - - let mut sources = nu_engine::glob_from(&spanned_source, &path, call.head, None) - .map(|p| p.1) - .map_or_else(|_| Vec::new(), Iterator::collect); - - if sources.is_empty() { - return Err(ShellError::FileNotFound { - file: spanned_source.item.to_string(), - span: spanned_source.span, - }); - } - - // We have two possibilities. - // - // First, the destination exists. - // - If a directory, move everything into that directory, otherwise - // - if only a single source, and --force (or -f) is provided overwrite the file, - // - otherwise error. - // - // Second, the destination doesn't exist, so we can only rename a single source. Otherwise - // it's an error. - let source = path.join(spanned_source.item.as_ref()); - if destination.exists() && !force && !destination.is_dir() && !source.is_dir() { - return Err(ShellError::GenericError { - error: "Destination file already exists".into(), - // These messages all use to_string_lossy() because - // showing the full path reduces misinterpretation of the message. - // Also, this is preferable to {:?} because that renders Windows paths incorrectly. - msg: format!( - "Destination file '{}' already exists", - destination.to_string_lossy() - ), - span: Some(spanned_destination.span), - help: Some("you can use -f, --force to force overwriting the destination".into()), - inner: vec![], - }); - } - - if (destination.exists() && !destination.is_dir() && sources.len() > 1) - || (!destination.exists() && sources.len() > 1) - { - return Err(ShellError::GenericError { - error: "Can only move multiple sources if destination is a directory".into(), - msg: "destination must be a directory when moving multiple sources".into(), - span: Some(spanned_destination.span), - help: None, - inner: vec![], - }); - } - - // This is the case where you move a directory A to the interior of directory B, but directory B - // already has a non-empty directory named A. - if source.is_dir() && destination.is_dir() { - if let Some(name) = source.file_name() { - let dst = destination.join(name); - if dst.is_dir() { - return Err(ShellError::GenericError { - error: format!( - "Can't move '{}' to '{}'", - source.to_string_lossy(), - dst.to_string_lossy() - ), - msg: format!("Directory '{}' is not empty", destination.to_string_lossy()), - span: Some(spanned_destination.span), - help: None, - inner: vec![], - }); - } - } - } - - let some_if_source_is_destination = sources - .iter() - .find(|f| matches!(f, Ok(f) if destination.starts_with(f))); - if destination.exists() && destination.is_dir() && sources.len() == 1 { - if let Some(Ok(filename)) = some_if_source_is_destination { - return Err(ShellError::GenericError { - error: format!( - "Not possible to move '{}' to itself", - filename.to_string_lossy() - ), - msg: "cannot move to itself".into(), - span: Some(spanned_destination.span), - help: None, - inner: vec![], - }); - } - } - - if let Some(Ok(_filename)) = some_if_source_is_destination { - sources.retain(|f| matches!(f, Ok(f) if !destination.starts_with(f))); - } - - let span = call.head; - sources - .into_iter() - .flatten() - .filter_map(move |entry| { - let result = move_file( - Spanned { - item: entry.clone(), - span: spanned_source.span, - }, - Spanned { - item: destination.clone(), - span: spanned_destination.span, - }, - interactive, - update_mode, - ); - if let Err(error) = result { - Some(Value::error(error, spanned_source.span)) - } else if verbose { - let val = match result { - Ok(true) => format!( - "moved {:} to {:}", - entry.to_string_lossy(), - destination.to_string_lossy() - ), - _ => format!( - "{:} not moved to {:}", - entry.to_string_lossy(), - destination.to_string_lossy() - ), - }; - Some(Value::string(val, span)) - } else { - None - } - }) - .into_pipeline_data(ctrlc) - .print_not_formatted(engine_state, false, true)?; - Ok(PipelineData::empty()) - } - - fn examples(&self) -> Vec { - vec![ - Example { - description: "Rename a file", - example: "mv before.txt after.txt", - result: None, - }, - Example { - description: "Move a file into a directory", - example: "mv test.txt my/subdirectory", - result: None, - }, - Example { - description: "Move many files into a directory", - example: "mv *.txt my/subdirectory", - result: None, - }, - ] - } -} - -fn move_file( - spanned_from: Spanned, - spanned_to: Spanned, - interactive: bool, - update_mode: bool, -) -> Result { - let Spanned { - item: from, - span: from_span, - } = spanned_from; - let Spanned { - item: to, - span: to_span, - } = spanned_to; - - if to.exists() && from.is_dir() && to.is_file() { - return Err(ShellError::MoveNotPossible { - source_message: "Can't move a directory".to_string(), - source_span: spanned_from.span, - destination_message: "to a file".to_string(), - destination_span: spanned_to.span, - }); - } - - let destination_dir_exists = if to.is_dir() { - true - } else { - to.parent().map(Path::exists).unwrap_or(true) - }; - - if !destination_dir_exists { - return Err(ShellError::DirectoryNotFound { - dir: to.to_string_lossy().to_string(), - span: to_span, - }); - } - - // This can happen when changing case on a case-insensitive filesystem (ex: changing foo to Foo on Windows) - // When it does, we want to do a plain rename instead of moving `from` into `to` - let from_to_are_same_file = same_file::is_same_file(&from, &to).unwrap_or(false); - - let mut to = to; - if !from_to_are_same_file && to.is_dir() { - let from_file_name = match from.file_name() { - Some(name) => name, - None => { - return Err(ShellError::DirectoryNotFound { - dir: from.to_string_lossy().to_string(), - span: to_span, - }) - } - }; - - to.push(from_file_name); - } - - if interactive && to.exists() { - let (interaction, confirmed) = try_interaction( - interactive, - format!("mv: overwrite '{}'? ", to.to_string_lossy()), - ); - if let Err(e) = interaction { - return Err(ShellError::GenericError { - error: format!("Error during interaction: {e:}"), - msg: "could not move".into(), - span: None, - help: None, - inner: vec![], - }); - } else if !confirmed { - return Ok(false); - } - } - - if update_mode && super::util::is_older(&from, &to).unwrap_or(false) { - Ok(false) - } else { - match move_item(&from, from_span, &to) { - Ok(()) => Ok(true), - Err(e) => Err(e), - } - } -} - -fn move_item(from: &Path, from_span: Span, to: &Path) -> Result<(), ShellError> { - // We first try a rename, which is a quick operation. If that doesn't work, we'll try a copy - // and remove the old file/folder. This is necessary if we're moving across filesystems or devices. - std::fs::rename(from, to).or_else(|_| { - match if from.is_file() { - let mut options = fs_extra::file::CopyOptions::new(); - options.overwrite = true; - fs_extra::file::move_file(from, to, &options) - } else { - let mut options = fs_extra::dir::CopyOptions::new(); - options.overwrite = true; - options.copy_inside = true; - fs_extra::dir::move_dir(from, to, &options) - } { - Ok(_) => Ok(()), - Err(e) => { - let error_kind = match e.kind { - fs_extra::error::ErrorKind::Io(io) => { - format!("I/O error: {io}") - } - fs_extra::error::ErrorKind::StripPrefix(sp) => { - format!("Strip prefix error: {sp}") - } - fs_extra::error::ErrorKind::OsString(os) => { - format!("OsString error: {:?}", os.to_str()) - } - _ => e.to_string(), - }; - Err(ShellError::GenericError { - error: format!("Could not move {from:?} to {to:?}. Error Kind: {error_kind}"), - msg: "could not move".into(), - span: Some(from_span), - help: None, - inner: vec![], - }) - } - } - }) -} diff --git a/crates/nu-command/src/filesystem/umv.rs b/crates/nu-command/src/filesystem/umv.rs index e964eba1af..73079f80db 100644 --- a/crates/nu-command/src/filesystem/umv.rs +++ b/crates/nu-command/src/filesystem/umv.rs @@ -14,28 +14,28 @@ pub struct UMv; impl Command for UMv { fn name(&self) -> &str { - "umv" + "mv" } fn usage(&self) -> &str { - "Move files or directories." + "Move files or directories using uutils/coreutils mv." } fn examples(&self) -> Vec { vec![ Example { description: "Rename a file", - example: "umv before.txt after.txt", + example: "mv before.txt after.txt", result: None, }, Example { description: "Move a file into a directory", - example: "umv test.txt my/subdirectory", + example: "mv test.txt my/subdirectory", result: None, }, Example { description: "Move many files into a directory", - example: "umv *.txt my/subdirectory", + example: "mv *.txt my/subdirectory", result: None, }, ] @@ -46,7 +46,7 @@ impl Command for UMv { } fn signature(&self) -> nu_protocol::Signature { - Signature::build("umv") + Signature::build("mv") .input_output_types(vec![(Type::Nothing, Type::Nothing)]) .switch("force", "do not prompt before overwriting", Some('f')) .switch("verbose", "explain what is being done.", Some('v')) diff --git a/crates/nu-command/src/filesystem/util.rs b/crates/nu-command/src/filesystem/util.rs index e21e168bf6..1341142dfc 100644 --- a/crates/nu-command/src/filesystem/util.rs +++ b/crates/nu-command/src/filesystem/util.rs @@ -66,6 +66,7 @@ fn get_interactive_confirmation(prompt: String) -> Result> /// Return `Some(true)` if the last change time of the `src` old than the `dst`, /// otherwisie return `Some(false)`. Return `None` if the `src` or `dst` doesn't exist. +#[allow(dead_code)] pub fn is_older(src: &Path, dst: &Path) -> Option { if !dst.exists() || !src.exists() { return None; diff --git a/crates/nu-command/tests/commands/move_/mod.rs b/crates/nu-command/tests/commands/move_/mod.rs index cfaef6e60f..4f8fd98794 100644 --- a/crates/nu-command/tests/commands/move_/mod.rs +++ b/crates/nu-command/tests/commands/move_/mod.rs @@ -1,3 +1,2 @@ mod column; -mod mv; mod umv; diff --git a/crates/nu-command/tests/commands/move_/mv.rs b/crates/nu-command/tests/commands/move_/mv.rs deleted file mode 100644 index 03a495f068..0000000000 --- a/crates/nu-command/tests/commands/move_/mv.rs +++ /dev/null @@ -1,493 +0,0 @@ -use nu_test_support::fs::{files_exist_at, Stub::EmptyFile, Stub::FileWithContent}; -use nu_test_support::nu; -use nu_test_support::playground::Playground; - -#[test] -fn moves_a_file() { - Playground::setup("mv_test_1", |dirs, sandbox| { - sandbox - .with_files(vec![EmptyFile("andres.txt")]) - .mkdir("expected"); - - let original = dirs.test().join("andres.txt"); - let expected = dirs.test().join("expected/yehuda.txt"); - - nu!( - cwd: dirs.test(), - "mv andres.txt expected/yehuda.txt" - ); - - assert!(!original.exists()); - assert!(expected.exists()); - }) -} - -#[test] -fn overwrites_if_moving_to_existing_file_and_force_provided() { - Playground::setup("mv_test_2", |dirs, sandbox| { - sandbox.with_files(vec![EmptyFile("andres.txt"), EmptyFile("jttxt")]); - - let original = dirs.test().join("andres.txt"); - let expected = dirs.test().join("jttxt"); - - nu!( - cwd: dirs.test(), - "mv andres.txt -f jttxt" - ); - - assert!(!original.exists()); - assert!(expected.exists()); - }) -} - -#[test] -fn moves_a_directory() { - Playground::setup("mv_test_3", |dirs, sandbox| { - sandbox.mkdir("empty_dir"); - - let original_dir = dirs.test().join("empty_dir"); - let expected = dirs.test().join("renamed_dir"); - - nu!( - cwd: dirs.test(), - "mv empty_dir renamed_dir" - ); - - assert!(!original_dir.exists()); - assert!(expected.exists()); - }) -} - -#[test] -fn moves_the_file_inside_directory_if_path_to_move_is_existing_directory() { - Playground::setup("mv_test_4", |dirs, sandbox| { - sandbox - .with_files(vec![EmptyFile("jttxt")]) - .mkdir("expected"); - - let original_dir = dirs.test().join("jttxt"); - let expected = dirs.test().join("expected/jttxt"); - - nu!( - cwd: dirs.test(), - "mv jttxt expected" - ); - - assert!(!original_dir.exists()); - assert!(expected.exists()); - }) -} - -#[test] -fn moves_the_directory_inside_directory_if_path_to_move_is_existing_directory() { - Playground::setup("mv_test_5", |dirs, sandbox| { - sandbox - .within("contributors") - .with_files(vec![EmptyFile("jttxt")]) - .mkdir("expected"); - - let original_dir = dirs.test().join("contributors"); - let expected = dirs.test().join("expected/contributors"); - - nu!( - cwd: dirs.test(), - "mv contributors expected" - ); - - assert!(!original_dir.exists()); - assert!(expected.exists()); - assert!(files_exist_at(vec!["jttxt"], expected)) - }) -} - -#[test] -fn moves_using_path_with_wildcard() { - Playground::setup("mv_test_7", |dirs, sandbox| { - sandbox - .within("originals") - .with_files(vec![ - EmptyFile("andres.ini"), - EmptyFile("caco3_plastics.csv"), - EmptyFile("cargo_sample.toml"), - EmptyFile("jt.ini"), - EmptyFile("jt.xml"), - EmptyFile("sgml_description.json"), - EmptyFile("sample.ini"), - EmptyFile("utf16.ini"), - EmptyFile("yehuda.ini"), - ]) - .mkdir("work_dir") - .mkdir("expected"); - - let work_dir = dirs.test().join("work_dir"); - let expected = dirs.test().join("expected"); - - nu!(cwd: work_dir, "mv ../originals/*.ini ../expected"); - - assert!(files_exist_at( - vec!["yehuda.ini", "jt.ini", "sample.ini", "andres.ini",], - expected - )); - }) -} - -#[test] -fn moves_using_a_glob() { - Playground::setup("mv_test_8", |dirs, sandbox| { - sandbox - .within("meals") - .with_files(vec![ - EmptyFile("arepa.txt"), - EmptyFile("empanada.txt"), - EmptyFile("taquiza.txt"), - ]) - .mkdir("work_dir") - .mkdir("expected"); - - let meal_dir = dirs.test().join("meals"); - let work_dir = dirs.test().join("work_dir"); - let expected = dirs.test().join("expected"); - - nu!(cwd: work_dir, "mv ../meals/* ../expected"); - - assert!(meal_dir.exists()); - assert!(files_exist_at( - vec!["arepa.txt", "empanada.txt", "taquiza.txt",], - expected - )); - }) -} - -#[test] -fn moves_a_directory_with_files() { - Playground::setup("mv_test_9", |dirs, sandbox| { - sandbox - .mkdir("vehicles/car") - .mkdir("vehicles/bicycle") - .with_files(vec![ - EmptyFile("vehicles/car/car1.txt"), - EmptyFile("vehicles/car/car2.txt"), - ]) - .with_files(vec![ - EmptyFile("vehicles/bicycle/bicycle1.txt"), - EmptyFile("vehicles/bicycle/bicycle2.txt"), - ]); - - let original_dir = dirs.test().join("vehicles"); - let expected_dir = dirs.test().join("expected"); - - nu!( - cwd: dirs.test(), - "mv vehicles expected" - ); - - assert!(!original_dir.exists()); - assert!(expected_dir.exists()); - assert!(files_exist_at( - vec![ - "car/car1.txt", - "car/car2.txt", - "bicycle/bicycle1.txt", - "bicycle/bicycle2.txt" - ], - expected_dir - )); - }) -} - -#[test] -fn errors_if_source_doesnt_exist() { - Playground::setup("mv_test_10", |dirs, sandbox| { - sandbox.mkdir("test_folder"); - let actual = nu!( - cwd: dirs.test(), - "mv non-existing-file test_folder/" - ); - assert!(actual.err.contains("file not found")); - }) -} - -#[test] -fn error_if_moving_to_existing_file_without_force() { - Playground::setup("mv_test_10_0", |dirs, sandbox| { - sandbox.with_files(vec![EmptyFile("andres.txt"), EmptyFile("jttxt")]); - - let actual = nu!( - cwd: dirs.test(), - "mv andres.txt jttxt" - ); - assert!(actual.err.contains("file already exists")) - }) -} - -#[test] -fn errors_if_destination_doesnt_exist() { - Playground::setup("mv_test_10_1", |dirs, sandbox| { - sandbox.with_files(vec![EmptyFile("empty.txt")]); - - let actual = nu!( - cwd: dirs.test(), - "mv empty.txt does/not/exist" - ); - - assert!(actual.err.contains("directory not found")); - }) -} - -#[test] -fn errors_if_multiple_sources_but_destination_not_a_directory() { - Playground::setup("mv_test_10_2", |dirs, sandbox| { - sandbox.with_files(vec![ - EmptyFile("file1.txt"), - EmptyFile("file2.txt"), - EmptyFile("file3.txt"), - ]); - - let actual = nu!( - cwd: dirs.test(), - "mv file?.txt not_a_dir" - ); - - assert!(actual - .err - .contains("Can only move multiple sources if destination is a directory")); - }) -} - -#[test] -fn errors_if_renaming_directory_to_an_existing_file() { - Playground::setup("mv_test_10_3", |dirs, sandbox| { - sandbox - .mkdir("mydir") - .with_files(vec![EmptyFile("empty.txt")]); - - let actual = nu!( - cwd: dirs.test(), - "mv mydir empty.txt" - ); - - assert!(actual.err.contains("Can't move a directory"),); - assert!(actual.err.contains("to a file"),); - }) -} - -#[test] -fn errors_if_moving_to_itself() { - Playground::setup("mv_test_10_4", |dirs, sandbox| { - sandbox.mkdir("mydir").mkdir("mydir/mydir_2"); - - let actual = nu!( - cwd: dirs.test(), - "mv mydir mydir/mydir_2/" - ); - - assert!(actual.err.contains("cannot move to itself")); - }) -} - -#[test] -fn does_not_error_on_relative_parent_path() { - Playground::setup("mv_test_11", |dirs, sandbox| { - sandbox - .mkdir("first") - .with_files(vec![EmptyFile("first/william_hartnell.txt")]); - - let original = dirs.test().join("first/william_hartnell.txt"); - let expected = dirs.test().join("william_hartnell.txt"); - - nu!( - cwd: dirs.test().join("first"), - "mv william_hartnell.txt ./.." - ); - - assert!(!original.exists()); - assert!(expected.exists()); - }) -} - -#[test] -fn move_files_using_glob_two_parents_up_using_multiple_dots() { - Playground::setup("mv_test_12", |dirs, sandbox| { - sandbox.within("foo").within("bar").with_files(vec![ - EmptyFile("jtjson"), - EmptyFile("andres.xml"), - EmptyFile("yehuda.yaml"), - EmptyFile("kevin.txt"), - EmptyFile("many_more.ppl"), - ]); - - nu!( - cwd: dirs.test().join("foo/bar"), - r#" - mv * ... - "# - ); - - let files = vec![ - "yehuda.yaml", - "jtjson", - "andres.xml", - "kevin.txt", - "many_more.ppl", - ]; - - let original_dir = dirs.test().join("foo/bar"); - let destination_dir = dirs.test(); - - assert!(files_exist_at(files.clone(), destination_dir)); - assert!(!files_exist_at(files, original_dir)) - }) -} - -#[test] -fn move_file_from_two_parents_up_using_multiple_dots_to_current_dir() { - Playground::setup("cp_test_10", |dirs, sandbox| { - sandbox.with_files(vec![EmptyFile("hello_there")]); - sandbox.within("foo").mkdir("bar"); - - nu!( - cwd: dirs.test().join("foo/bar"), - r#" - mv .../hello_there . - "# - ); - - let expected = dirs.test().join("foo/bar/hello_there"); - let original = dirs.test().join("hello_there"); - - assert!(expected.exists()); - assert!(!original.exists()); - }) -} - -#[test] -fn does_not_error_when_some_file_is_moving_into_itself() { - Playground::setup("mv_test_13", |dirs, sandbox| { - sandbox.mkdir("11").mkdir("12"); - - let original_dir = dirs.test().join("11"); - let expected = dirs.test().join("12/11"); - nu!(cwd: dirs.test(), "mv 1* 12"); - - assert!(!original_dir.exists()); - assert!(expected.exists()); - }) -} - -#[test] -fn mv_ignores_ansi() { - Playground::setup("mv_test_ansi", |_dirs, sandbox| { - sandbox.with_files(vec![EmptyFile("test.txt")]); - let actual = nu!( - cwd: sandbox.cwd(), - r#" - ls | find test | mv $in.0.name success.txt; ls | $in.0.name - "# - ); - - assert_eq!(actual.out, "success.txt"); - }) -} - -#[test] -fn mv_directory_with_same_name() { - Playground::setup("mv_test_directory_with_same_name", |_dirs, sandbox| { - sandbox.mkdir("testdir"); - sandbox.mkdir("testdir/testdir"); - - let cwd = sandbox.cwd().join("testdir"); - let actual = nu!( - cwd: cwd, - r#" - mv testdir .. - "# - ); - - assert!(actual.err.contains("is not empty")); - }) -} - -#[test] -// Test that changing the case of a file/directory name works; -// this is an important edge case on Windows (and any other case-insensitive file systems). -// We were bitten badly by this once: https://github.com/nushell/nushell/issues/6583 -fn mv_change_case_of_directory() { - Playground::setup("mv_change_case_of_directory", |dirs, sandbox| { - sandbox - .mkdir("somedir") - .with_files(vec![EmptyFile("somedir/somefile.txt")]); - - let original_dir = String::from("somedir"); - let new_dir = String::from("SomeDir"); - - nu!( - cwd: dirs.test(), - format!("mv {original_dir} {new_dir}") - ); - - // Doing this instead of `Path::exists()` because we need to check file existence in - // a case-sensitive way. `Path::exists()` is understandably case-insensitive on NTFS - let files_in_test_directory: Vec = std::fs::read_dir(dirs.test()) - .unwrap() - .map(|de| de.unwrap().file_name().to_string_lossy().into_owned()) - .collect(); - assert!(!files_in_test_directory.contains(&original_dir)); - assert!(files_in_test_directory.contains(&new_dir)); - - assert!(files_exist_at( - vec!["somefile.txt",], - dirs.test().join(new_dir) - )); - }) -} - -#[test] -fn mv_change_case_of_file() { - Playground::setup("mv_change_case_of_file", |dirs, sandbox| { - sandbox.with_files(vec![EmptyFile("somefile.txt")]); - - let original_file_name = String::from("somefile.txt"); - let new_file_name = String::from("SomeFile.txt"); - - nu!( - cwd: dirs.test(), - format!("mv {original_file_name} -f {new_file_name}") - ); - - // Doing this instead of `Path::exists()` because we need to check file existence in - // a case-sensitive way. `Path::exists()` is understandably case-insensitive on NTFS - let files_in_test_directory: Vec = std::fs::read_dir(dirs.test()) - .unwrap() - .map(|de| de.unwrap().file_name().to_string_lossy().into_owned()) - .collect(); - assert!(!files_in_test_directory.contains(&original_file_name)); - assert!(files_in_test_directory.contains(&new_file_name)); - }) -} - -#[test] -fn mv_with_update_flag() { - Playground::setup("mv_with_update_flag", |_dirs, sandbox| { - sandbox.with_files(vec![ - EmptyFile("valid.txt"), - FileWithContent("newer_valid.txt", "body"), - ]); - - let actual = nu!( - cwd: sandbox.cwd(), - "mv -uf valid.txt newer_valid.txt; open newer_valid.txt", - ); - assert_eq!(actual.out, "body"); - - // create a file after assert to make sure that newest_valid.txt is newest - std::thread::sleep(std::time::Duration::from_secs(1)); - sandbox.with_files(vec![FileWithContent("newest_valid.txt", "newest_body")]); - let actual = nu!(cwd: sandbox.cwd(), "mv -uf newest_valid.txt valid.txt; open valid.txt"); - assert_eq!(actual.out, "newest_body"); - - // when destination doesn't exist - sandbox.with_files(vec![FileWithContent("newest_valid.txt", "newest_body")]); - let actual = nu!(cwd: sandbox.cwd(), "mv -uf newest_valid.txt des_missing.txt; open des_missing.txt"); - assert_eq!(actual.out, "newest_body"); - }); -} diff --git a/crates/nu-command/tests/commands/move_/umv.rs b/crates/nu-command/tests/commands/move_/umv.rs index 091ccc70b0..92ea760ba9 100644 --- a/crates/nu-command/tests/commands/move_/umv.rs +++ b/crates/nu-command/tests/commands/move_/umv.rs @@ -15,7 +15,7 @@ fn moves_a_file() { nu!( cwd: dirs.test(), - "umv andres.txt expected/yehuda.txt" + "mv andres.txt expected/yehuda.txt" ); assert!(!original.exists()); @@ -33,7 +33,7 @@ fn overwrites_if_moving_to_existing_file_and_force_provided() { nu!( cwd: dirs.test(), - "umv andres.txt -f jttxt" + "mv andres.txt -f jttxt" ); assert!(!original.exists()); @@ -51,7 +51,7 @@ fn moves_a_directory() { nu!( cwd: dirs.test(), - "umv empty_dir renamed_dir" + "mv empty_dir renamed_dir" ); assert!(!original_dir.exists()); @@ -71,7 +71,7 @@ fn moves_the_file_inside_directory_if_path_to_move_is_existing_directory() { nu!( cwd: dirs.test(), - "umv jttxt expected" + "mv jttxt expected" ); assert!(!original_dir.exists()); @@ -92,7 +92,7 @@ fn moves_the_directory_inside_directory_if_path_to_move_is_existing_directory() nu!( cwd: dirs.test(), - "umv contributors expected" + "mv contributors expected" ); assert!(!original_dir.exists()); @@ -123,7 +123,7 @@ fn moves_using_path_with_wildcard() { let work_dir = dirs.test().join("work_dir"); let expected = dirs.test().join("expected"); - nu!(cwd: work_dir, "umv ../originals/*.ini ../expected"); + nu!(cwd: work_dir, "mv ../originals/*.ini ../expected"); assert!(files_exist_at( vec!["yehuda.ini", "jt.ini", "sample.ini", "andres.ini",], @@ -149,7 +149,7 @@ fn moves_using_a_glob() { let work_dir = dirs.test().join("work_dir"); let expected = dirs.test().join("expected"); - nu!(cwd: work_dir, "umv ../meals/* ../expected"); + nu!(cwd: work_dir, "mv ../meals/* ../expected"); assert!(meal_dir.exists()); assert!(files_exist_at( @@ -179,7 +179,7 @@ fn moves_a_directory_with_files() { nu!( cwd: dirs.test(), - "umv vehicles expected" + "mv vehicles expected" ); assert!(!original_dir.exists()); @@ -202,7 +202,7 @@ fn errors_if_source_doesnt_exist() { sandbox.mkdir("test_folder"); let actual = nu!( cwd: dirs.test(), - "umv non-existing-file test_folder/" + "mv non-existing-file test_folder/" ); assert!(actual.err.contains("Directory not found")); }) @@ -216,7 +216,7 @@ fn error_if_moving_to_existing_file_without_force() { let actual = nu!( cwd: dirs.test(), - "umv andres.txt jttxt" + "mv andres.txt jttxt" ); assert!(actual.err.contains("file already exists")) }) @@ -229,7 +229,7 @@ fn errors_if_destination_doesnt_exist() { let actual = nu!( cwd: dirs.test(), - "umv empty.txt does/not/exist/" + "mv empty.txt does/not/exist/" ); assert!(actual.err.contains("failed to access")); @@ -248,7 +248,7 @@ fn errors_if_multiple_sources_but_destination_not_a_directory() { let actual = nu!( cwd: dirs.test(), - "umv file?.txt not_a_dir" + "mv file?.txt not_a_dir" ); assert!(actual @@ -266,7 +266,7 @@ fn errors_if_renaming_directory_to_an_existing_file() { let actual = nu!( cwd: dirs.test(), - "umv mydir empty.txt" + "mv mydir empty.txt" ); assert!(actual.err.contains("cannot overwrite non-directory"),); assert!(actual.err.contains("with directory"),); @@ -280,7 +280,7 @@ fn errors_if_moving_to_itself() { let actual = nu!( cwd: dirs.test(), - "umv mydir mydir/mydir_2/" + "mv mydir mydir/mydir_2/" ); assert!(actual.err.contains("cannot move")); assert!(actual.err.contains("to a subdirectory")); @@ -299,7 +299,7 @@ fn does_not_error_on_relative_parent_path() { nu!( cwd: dirs.test().join("first"), - "umv william_hartnell.txt ./.." + "mv william_hartnell.txt ./.." ); assert!(!original.exists()); @@ -321,7 +321,7 @@ fn move_files_using_glob_two_parents_up_using_multiple_dots() { nu!( cwd: dirs.test().join("foo/bar"), r#" - umv * ... + mv * ... "# ); @@ -350,7 +350,7 @@ fn move_file_from_two_parents_up_using_multiple_dots_to_current_dir() { nu!( cwd: dirs.test().join("foo/bar"), r#" - umv .../hello_there . + mv .../hello_there . "# ); @@ -369,7 +369,7 @@ fn does_not_error_when_some_file_is_moving_into_itself() { let original_dir = dirs.test().join("11"); let expected = dirs.test().join("12/11"); - nu!(cwd: dirs.test(), "umv 1* 12"); + nu!(cwd: dirs.test(), "mv 1* 12"); assert!(!original_dir.exists()); assert!(expected.exists()); @@ -383,7 +383,7 @@ fn mv_ignores_ansi() { let actual = nu!( cwd: sandbox.cwd(), r#" - ls | find test | umv $in.0.name success.txt; ls | $in.0.name + ls | find test | mv $in.0.name success.txt; ls | $in.0.name "# ); @@ -401,7 +401,7 @@ fn mv_directory_with_same_name() { let actual = nu!( cwd: cwd, r#" - umv testdir .. + mv testdir .. "# ); assert!(actual.err.contains("Directory not empty")); @@ -426,7 +426,7 @@ fn mv_change_case_of_directory() { let _actual = nu!( cwd: dirs.test(), - format!("umv {original_dir} {new_dir}") + format!("mv {original_dir} {new_dir}") ); // Doing this instead of `Path::exists()` because we need to check file existence in @@ -465,7 +465,7 @@ fn mv_change_case_of_file() { let _actual = nu!( cwd: dirs.test(), - format!("umv {original_file_name} -f {new_file_name}") + format!("mv {original_file_name} -f {new_file_name}") ); // Doing this instead of `Path::exists()` because we need to check file existence in @@ -487,7 +487,7 @@ fn mv_change_case_of_file() { #[test] #[ignore = "Update not supported..remove later"] fn mv_with_update_flag() { - Playground::setup("mv_with_update_flag", |_dirs, sandbox| { + Playground::setup("umv_with_update_flag", |_dirs, sandbox| { sandbox.with_files(vec![ EmptyFile("valid.txt"), FileWithContent("newer_valid.txt", "body"), @@ -495,19 +495,19 @@ fn mv_with_update_flag() { let actual = nu!( cwd: sandbox.cwd(), - "umv -uf valid.txt newer_valid.txt; open newer_valid.txt", + "mv -uf valid.txt newer_valid.txt; open newer_valid.txt", ); assert_eq!(actual.out, "body"); // create a file after assert to make sure that newest_valid.txt is newest std::thread::sleep(std::time::Duration::from_secs(1)); sandbox.with_files(vec![FileWithContent("newest_valid.txt", "newest_body")]); - let actual = nu!(cwd: sandbox.cwd(), "umv -uf newest_valid.txt valid.txt; open valid.txt"); + let actual = nu!(cwd: sandbox.cwd(), "mv -uf newest_valid.txt valid.txt; open valid.txt"); assert_eq!(actual.out, "newest_body"); // when destination doesn't exist sandbox.with_files(vec![FileWithContent("newest_valid.txt", "newest_body")]); - let actual = nu!(cwd: sandbox.cwd(), "umv -uf newest_valid.txt des_missing.txt; open des_missing.txt"); + let actual = nu!(cwd: sandbox.cwd(), "mv -uf newest_valid.txt des_missing.txt; open des_missing.txt"); assert_eq!(actual.out, "newest_body"); }); } @@ -522,7 +522,7 @@ fn test_mv_no_clobber() { let actual = nu!( cwd: dirs.test(), - "umv -n {} {}", + "mv -n {} {}", file_a, file_b, ); @@ -535,7 +535,7 @@ fn mv_with_no_arguments() { Playground::setup("umv_test_14", |dirs, _| { let actual = nu!( cwd: dirs.test(), - "umv", + "mv", ); assert!(actual.err.contains("Missing file operand")); }) @@ -546,7 +546,7 @@ fn mv_with_no_target() { Playground::setup("umv_test_15", |dirs, _| { let actual = nu!( cwd: dirs.test(), - "umv a", + "mv a", ); assert!(actual.err.contains( format!( @@ -574,7 +574,7 @@ fn mv_files_with_glob_metachars(#[case] src_name: &str) { let actual = nu!( cwd: dirs.test(), - "umv '{}' {}", + "mv '{}' {}", src.display(), "hello_world_dest" ); @@ -600,7 +600,7 @@ fn mv_files_with_glob_metachars_when_input_are_variables(#[case] src_name: &str) let actual = nu!( cwd: dirs.test(), - "let f = '{}'; umv $f {}", + "let f = '{}'; mv $f {}", src.display(), "hello_world_dest" ); @@ -629,7 +629,7 @@ fn mv_with_cd() { let actual = nu!( cwd: sandbox.cwd(), - r#"do { cd tmp_dir; let f = 'file.txt'; umv $f .. }; open file.txt"#, + r#"do { cd tmp_dir; let f = 'file.txt'; mv $f .. }; open file.txt"#, ); assert!(actual.out.contains("body")); });