diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index df704fe369..d4b08d80c1 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -241,7 +241,6 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState { Start, Rm, Save, - Touch, UTouch, Glob, Watch, diff --git a/crates/nu-command/src/filesystem/mod.rs b/crates/nu-command/src/filesystem/mod.rs index 089e899dda..5ceb8aa0b6 100644 --- a/crates/nu-command/src/filesystem/mod.rs +++ b/crates/nu-command/src/filesystem/mod.rs @@ -7,7 +7,6 @@ mod open; mod rm; mod save; mod start; -mod touch; mod ucp; mod umkdir; mod umv; @@ -24,7 +23,6 @@ pub use mktemp::Mktemp; pub use rm::Rm; pub use save::Save; pub use start::Start; -pub use touch::Touch; pub use ucp::UCp; pub use umkdir::UMkdir; pub use umv::UMv; diff --git a/crates/nu-command/src/filesystem/touch.rs b/crates/nu-command/src/filesystem/touch.rs deleted file mode 100644 index 93b960d843..0000000000 --- a/crates/nu-command/src/filesystem/touch.rs +++ /dev/null @@ -1,249 +0,0 @@ -use filetime::FileTime; -use nu_engine::command_prelude::*; -use nu_path::expand_path_with; -use nu_protocol::NuGlob; -use std::{fs::OpenOptions, time::SystemTime}; - -#[derive(Clone)] -pub struct Touch; - -impl Command for Touch { - fn name(&self) -> &str { - "touch" - } - - fn search_terms(&self) -> Vec<&str> { - vec!["create", "file"] - } - - fn signature(&self) -> Signature { - Signature::build("touch") - .input_output_types(vec![(Type::Nothing, Type::Nothing)]) - .rest( - "files", - SyntaxShape::OneOf(vec![SyntaxShape::GlobPattern, SyntaxShape::Filepath]), - "The file(s) to create." - ) - .named( - "reference", - SyntaxShape::String, - "change the file or directory time to the time of the reference file/directory", - Some('r'), - ) - .switch( - "modified", - "change the modification time of the file or directory. If no reference file/directory is given, the current time is used", - Some('m'), - ) - .switch( - "access", - "change the access time of the file or directory. If no reference file/directory is given, the current time is used", - Some('a'), - ) - .switch( - "no-create", - "do not create the file if it does not exist", - Some('c'), - ) - .switch( - "no-deref", - "do not follow symlinks", - Some('s') - ) - .category(Category::FileSystem) - } - - fn description(&self) -> &str { - "Creates one or more files." - } - - fn run( - &self, - engine_state: &EngineState, - stack: &mut Stack, - call: &Call, - _input: PipelineData, - ) -> Result { - let mut change_mtime: bool = call.has_flag(engine_state, stack, "modified")?; - let mut change_atime: bool = call.has_flag(engine_state, stack, "access")?; - let no_follow_symlinks: bool = call.has_flag(engine_state, stack, "no-deref")?; - let reference: Option> = call.get_flag(engine_state, stack, "reference")?; - let no_create: bool = call.has_flag(engine_state, stack, "no-create")?; - let files = call.rest::>(engine_state, stack, 0)?; - - let cwd = engine_state.cwd(Some(stack))?; - - if files.is_empty() { - return Err(ShellError::MissingParameter { - param_name: "requires file paths".to_string(), - span: call.head, - }); - } - - let mut mtime = SystemTime::now(); - let mut atime = mtime; - - // Change both times if neither is specified - if !change_mtime && !change_atime { - change_mtime = true; - change_atime = true; - } - - if let Some(reference) = reference { - let reference_path = nu_path::expand_path_with(reference.item, &cwd, true); - let exists = if no_follow_symlinks { - // There's no symlink_exists function, so we settle for - // getting direct metadata and if it's OK, it exists - reference_path.symlink_metadata().is_ok() - } else { - reference_path.exists() - }; - if !exists { - return Err(ShellError::FileNotFoundCustom { - msg: "Reference path not found".into(), - span: reference.span, - }); - } - - let metadata = if no_follow_symlinks { - reference_path.symlink_metadata() - } else { - reference_path.metadata() - }; - let metadata = metadata.map_err(|err| ShellError::IOErrorSpanned { - msg: format!("Failed to read metadata: {err}"), - span: reference.span, - })?; - mtime = metadata - .modified() - .map_err(|err| ShellError::IOErrorSpanned { - msg: format!("Failed to read modified time: {err}"), - span: reference.span, - })?; - atime = metadata - .accessed() - .map_err(|err| ShellError::IOErrorSpanned { - msg: format!("Failed to read access time: {err}"), - span: reference.span, - })?; - } - - for glob in files { - let path = expand_path_with(glob.item.as_ref(), &cwd, glob.item.is_expand()); - let exists = if no_follow_symlinks { - path.symlink_metadata().is_ok() - } else { - path.exists() - }; - - // If --no-create is passed and the file/dir does not exist there's nothing to do - if no_create && !exists { - continue; - } - - // If --no-deref was passed in, the behavior of touch is to error on missing - if no_follow_symlinks && !exists { - return Err(ShellError::FileNotFound { - file: path.to_string_lossy().into_owned(), - span: glob.span, - }); - } - - // Create a file at the given path unless the path is a directory (or a symlink with -d) - if !path.is_dir() && (!no_follow_symlinks || !path.is_symlink()) { - if let Err(err) = OpenOptions::new() - .write(true) - .create(true) - .truncate(false) - .open(&path) - { - return Err(ShellError::CreateNotPossible { - msg: format!("Failed to create file: {err}"), - span: glob.span, - }); - }; - } - - // We have to inefficiently access the target metadata to not reset it - // in set_symlink_file_times, because the filetime doesn't expose individual methods for it - let get_target_metadata = || { - path.symlink_metadata() - .map_err(|err| ShellError::IOErrorSpanned { - msg: format!("Failed to read metadata: {err}"), - span: glob.span, - }) - }; - - if change_mtime { - let result = if no_follow_symlinks { - filetime::set_symlink_file_times( - &path, - if change_atime { - FileTime::from_system_time(atime) - } else { - FileTime::from_system_time(get_target_metadata()?.accessed()?) - }, - FileTime::from_system_time(mtime), - ) - } else { - filetime::set_file_mtime(&path, FileTime::from_system_time(mtime)) - }; - if let Err(err) = result { - return Err(ShellError::ChangeModifiedTimeNotPossible { - msg: format!("Failed to change the modified time: {err}"), - span: glob.span, - }); - }; - } - - if change_atime { - let result = if no_follow_symlinks { - filetime::set_symlink_file_times( - &path, - FileTime::from_system_time(atime), - if change_mtime { - FileTime::from_system_time(mtime) - } else { - FileTime::from_system_time(get_target_metadata()?.modified()?) - }, - ) - } else { - filetime::set_file_atime(&path, FileTime::from_system_time(atime)) - }; - if let Err(err) = result { - return Err(ShellError::ChangeAccessTimeNotPossible { - msg: format!("Failed to change the access time: {err}"), - span: glob.span, - }); - }; - } - } - - Ok(PipelineData::empty()) - } - - fn examples(&self) -> Vec { - vec![ - Example { - description: "Creates \"fixture.json\"", - example: "touch fixture.json", - result: None, - }, - Example { - description: "Creates files a, b and c", - example: "touch a b c", - result: None, - }, - Example { - description: r#"Changes the last modified time of "fixture.json" to today's date"#, - example: "touch -m fixture.json", - result: None, - }, - Example { - description: r#"Changes the last modified time of file d and e to "fixture.json"'s last modified time"#, - example: r#"touch -m -r fixture.json d e"#, - result: None, - }, - ] - } -} diff --git a/crates/nu-command/src/filesystem/utouch.rs b/crates/nu-command/src/filesystem/utouch.rs index 4e3d32d906..9fbf9805de 100644 --- a/crates/nu-command/src/filesystem/utouch.rs +++ b/crates/nu-command/src/filesystem/utouch.rs @@ -12,7 +12,7 @@ pub struct UTouch; impl Command for UTouch { fn name(&self) -> &str { - "utouch" + "touch" } fn search_terms(&self) -> Vec<&str> { @@ -20,7 +20,7 @@ impl Command for UTouch { } fn signature(&self) -> Signature { - Signature::build("utouch") + Signature::build("touch") .input_output_types(vec![ (Type::Nothing, Type::Nothing) ]) .rest( "files", @@ -82,7 +82,7 @@ impl Command for UTouch { let change_mtime: bool = call.has_flag(engine_state, stack, "modified")?; let change_atime: bool = call.has_flag(engine_state, stack, "access")?; let no_create: bool = call.has_flag(engine_state, stack, "no-create")?; - let no_deref: bool = call.has_flag(engine_state, stack, "no-dereference")?; + let no_deref: bool = call.has_flag(engine_state, stack, "no-deref")?; let file_globs = call.rest::>(engine_state, stack, 0)?; let cwd = engine_state.cwd(Some(stack))?; @@ -220,12 +220,11 @@ impl Command for UTouch { }, TouchError::InvalidDateFormat(date) => ShellError::IncorrectValue { msg: format!("Invalid date: {}", date), - val_span: date_span.expect("utouch should've been given a date"), + val_span: date_span.expect("touch should've been given a date"), call_span: call.head, }, TouchError::ReferenceFileInaccessible(reference_path, io_err) => { - let span = - reference_span.expect("utouch should've been given a reference file"); + let span = reference_span.expect("touch should've been given a reference file"); if io_err.kind() == ErrorKind::NotFound { ShellError::FileNotFound { span, @@ -259,47 +258,47 @@ impl Command for UTouch { vec![ Example { description: "Creates \"fixture.json\"", - example: "utouch fixture.json", + example: "touch fixture.json", result: None, }, Example { description: "Creates files a, b and c", - example: "utouch a b c", + example: "touch a b c", result: None, }, Example { description: r#"Changes the last modified time of "fixture.json" to today's date"#, - example: "utouch -m fixture.json", + example: "touch -m fixture.json", result: None, }, Example { description: r#"Changes the last modified and accessed time of all files with the .json extension to today's date"#, - example: "utouch *.json", + example: "touch *.json", result: None, }, Example { description: "Changes the last accessed and modified times of files a, b and c to the current time but yesterday", - example: r#"utouch -d "yesterday" a b c"#, + example: r#"touch -d "yesterday" a b c"#, result: None, }, Example { description: r#"Changes the last modified time of files d and e to "fixture.json"'s last modified time"#, - example: r#"utouch -m -r fixture.json d e"#, + example: r#"touch -m -r fixture.json d e"#, result: None, }, Example { description: r#"Changes the last accessed time of "fixture.json" to a datetime"#, - example: r#"utouch -a -t 2019-08-24T12:30:30 fixture.json"#, + example: r#"touch -a -t 2019-08-24T12:30:30 fixture.json"#, result: None, }, Example { description: r#"Change the last accessed and modified times of stdout"#, - example: r#"utouch -"#, + example: r#"touch -"#, result: None, }, Example { description: r#"Changes the last accessed and modified times of file a to 1 month before "fixture.json"'s last modified time"#, - example: r#"utouch -r fixture.json -d "-1 month" a"#, + example: r#"touch -r fixture.json -d "-1 month" a"#, result: None, }, ] diff --git a/crates/nu-command/tests/commands/mod.rs b/crates/nu-command/tests/commands/mod.rs index 3de3888823..eb90d56d1e 100644 --- a/crates/nu-command/tests/commands/mod.rs +++ b/crates/nu-command/tests/commands/mod.rs @@ -112,7 +112,6 @@ mod take; mod tee; mod terminal; mod to_text; -mod touch; mod transpose; mod try_; mod ucp; diff --git a/crates/nu-command/tests/commands/touch.rs b/crates/nu-command/tests/commands/touch.rs deleted file mode 100644 index b7ab06f511..0000000000 --- a/crates/nu-command/tests/commands/touch.rs +++ /dev/null @@ -1,654 +0,0 @@ -use chrono::{DateTime, Local}; -use nu_test_support::fs::{files_exist_at, Stub}; -use nu_test_support::nu; -use nu_test_support::playground::{Dirs, Playground}; - -// Use 1 instead of 0 because 0 has a special meaning in Windows -const TIME_ONE: filetime::FileTime = filetime::FileTime::from_unix_time(1, 0); - -#[test] -fn creates_a_file_when_it_doesnt_exist() { - Playground::setup("create_test_1", |dirs, _sandbox| { - nu!( - cwd: dirs.test(), - "touch i_will_be_created.txt" - ); - - let path = dirs.test().join("i_will_be_created.txt"); - assert!(path.exists()); - }) -} - -#[test] -fn creates_two_files() { - Playground::setup("create_test_2", |dirs, _sandbox| { - nu!( - cwd: dirs.test(), - "touch a b" - ); - - let path = dirs.test().join("a"); - assert!(path.exists()); - - let path2 = dirs.test().join("b"); - assert!(path2.exists()); - }) -} - -#[test] -fn change_modified_time_of_file_to_today() { - Playground::setup("change_time_test_9", |dirs, sandbox| { - sandbox.with_files(&[Stub::EmptyFile("file.txt")]); - let path = dirs.test().join("file.txt"); - - // Set file.txt's times to the past before the test to make sure `touch` actually changes the mtime to today - filetime::set_file_times(&path, TIME_ONE, TIME_ONE).unwrap(); - - nu!( - cwd: dirs.test(), - "touch -m file.txt" - ); - - let metadata = path.metadata().unwrap(); - - // Check only the date since the time may not match exactly - let today = Local::now().date_naive(); - let mtime_day = DateTime::::from(metadata.modified().unwrap()).date_naive(); - - assert_eq!(today, mtime_day); - - // Check that atime remains unchanged - assert_eq!( - TIME_ONE, - filetime::FileTime::from_system_time(metadata.accessed().unwrap()) - ); - }) -} - -#[test] -fn change_access_time_of_file_to_today() { - Playground::setup("change_time_test_18", |dirs, sandbox| { - sandbox.with_files(&[Stub::EmptyFile("file.txt")]); - let path = dirs.test().join("file.txt"); - - // Set file.txt's times to the past before the test to make sure `touch` actually changes the atime to today - filetime::set_file_times(&path, TIME_ONE, TIME_ONE).unwrap(); - - nu!( - cwd: dirs.test(), - "touch -a file.txt" - ); - - let metadata = path.metadata().unwrap(); - - // Check only the date since the time may not match exactly - let today = Local::now().date_naive(); - let atime_day = DateTime::::from(metadata.accessed().unwrap()).date_naive(); - - assert_eq!(today, atime_day); - - // Check that mtime remains unchanged - assert_eq!( - TIME_ONE, - filetime::FileTime::from_system_time(metadata.modified().unwrap()) - ); - }) -} - -#[test] -fn change_modified_and_access_time_of_file_to_today() { - Playground::setup("change_time_test_27", |dirs, sandbox| { - sandbox.with_files(&[Stub::EmptyFile("file.txt")]); - let path = dirs.test().join("file.txt"); - - filetime::set_file_times(&path, TIME_ONE, TIME_ONE).unwrap(); - - nu!( - cwd: dirs.test(), - "touch -a -m file.txt" - ); - - let metadata = path.metadata().unwrap(); - - // Check only the date since the time may not match exactly - let today = Local::now().date_naive(); - let mtime_day = DateTime::::from(metadata.modified().unwrap()).date_naive(); - let atime_day = DateTime::::from(metadata.accessed().unwrap()).date_naive(); - - assert_eq!(today, mtime_day); - assert_eq!(today, atime_day); - }) -} - -#[test] -fn not_create_file_if_it_not_exists() { - Playground::setup("change_time_test_28", |dirs, _sandbox| { - let outcome = nu!( - cwd: dirs.test(), - "touch -c file.txt" - ); - - let path = dirs.test().join("file.txt"); - - assert!(!path.exists()); - - // If --no-create is improperly handled `touch` may error when trying to change the times of a nonexistent file - assert!(outcome.status.success()) - }) -} - -#[test] -fn change_file_times_if_exists_with_no_create() { - Playground::setup( - "change_file_times_if_exists_with_no_create", - |dirs, sandbox| { - sandbox.with_files(&[Stub::EmptyFile("file.txt")]); - let path = dirs.test().join("file.txt"); - - filetime::set_file_times(&path, TIME_ONE, TIME_ONE).unwrap(); - - nu!( - cwd: dirs.test(), - "touch -c file.txt" - ); - - let metadata = path.metadata().unwrap(); - - // Check only the date since the time may not match exactly - let today = Local::now().date_naive(); - let mtime_day = DateTime::::from(metadata.modified().unwrap()).date_naive(); - let atime_day = DateTime::::from(metadata.accessed().unwrap()).date_naive(); - - assert_eq!(today, mtime_day); - assert_eq!(today, atime_day); - }, - ) -} - -#[test] -fn creates_file_three_dots() { - Playground::setup("create_test_1", |dirs, _sandbox| { - nu!( - cwd: dirs.test(), - "touch file..." - ); - - let path = dirs.test().join("file..."); - assert!(path.exists()); - }) -} - -#[test] -fn creates_file_four_dots() { - Playground::setup("create_test_1", |dirs, _sandbox| { - nu!( - cwd: dirs.test(), - "touch file...." - ); - - let path = dirs.test().join("file...."); - assert!(path.exists()); - }) -} - -#[test] -fn creates_file_four_dots_quotation_marks() { - Playground::setup("create_test_1", |dirs, _sandbox| { - nu!( - cwd: dirs.test(), - "touch 'file....'" - ); - - let path = dirs.test().join("file...."); - assert!(path.exists()); - }) -} - -#[test] -fn change_file_times_to_reference_file() { - Playground::setup("change_dir_times_to_reference_dir", |dirs, sandbox| { - sandbox.with_files(&[ - Stub::EmptyFile("reference_file"), - Stub::EmptyFile("target_file"), - ]); - - let reference = dirs.test().join("reference_file"); - let target = dirs.test().join("target_file"); - - // Change the times for reference - filetime::set_file_times( - &reference, - filetime::FileTime::from_unix_time(1337, 0), - TIME_ONE, - ) - .unwrap(); - - // target should have today's date since it was just created, but reference should be different - assert_ne!( - reference.metadata().unwrap().accessed().unwrap(), - target.metadata().unwrap().accessed().unwrap() - ); - assert_ne!( - reference.metadata().unwrap().modified().unwrap(), - target.metadata().unwrap().modified().unwrap() - ); - - nu!( - cwd: dirs.test(), - "touch -r reference_file target_file" - ); - - assert_eq!( - reference.metadata().unwrap().accessed().unwrap(), - target.metadata().unwrap().accessed().unwrap() - ); - assert_eq!( - reference.metadata().unwrap().modified().unwrap(), - target.metadata().unwrap().modified().unwrap() - ); - }) -} - -#[test] -fn change_file_mtime_to_reference() { - Playground::setup("change_file_mtime_to_reference", |dirs, sandbox| { - sandbox.with_files(&[ - Stub::EmptyFile("reference_file"), - Stub::EmptyFile("target_file"), - ]); - - let reference = dirs.test().join("reference_file"); - let target = dirs.test().join("target_file"); - - // Change the times for reference - filetime::set_file_times( - &reference, - TIME_ONE, - filetime::FileTime::from_unix_time(1337, 0), - ) - .unwrap(); - - // target should have today's date since it was just created, but reference should be different - assert_ne!( - reference.metadata().unwrap().accessed().unwrap(), - target.metadata().unwrap().accessed().unwrap() - ); - assert_ne!( - reference.metadata().unwrap().modified().unwrap(), - target.metadata().unwrap().modified().unwrap() - ); - - // Save target's current atime to make sure it is preserved - let target_original_atime = target.metadata().unwrap().accessed().unwrap(); - - nu!( - cwd: dirs.test(), - "touch -mr reference_file target_file" - ); - - assert_eq!( - reference.metadata().unwrap().modified().unwrap(), - target.metadata().unwrap().modified().unwrap() - ); - assert_ne!( - reference.metadata().unwrap().accessed().unwrap(), - target.metadata().unwrap().accessed().unwrap() - ); - assert_eq!( - target_original_atime, - target.metadata().unwrap().accessed().unwrap() - ); - }) -} - -#[test] -fn change_modified_time_of_dir_to_today() { - Playground::setup("change_dir_mtime", |dirs, sandbox| { - sandbox.mkdir("test_dir"); - let path = dirs.test().join("test_dir"); - - filetime::set_file_mtime(&path, TIME_ONE).unwrap(); - - nu!( - cwd: dirs.test(), - "touch -m test_dir" - ); - - // Check only the date since the time may not match exactly - let today = Local::now().date_naive(); - let mtime_day = - DateTime::::from(path.metadata().unwrap().modified().unwrap()).date_naive(); - - assert_eq!(today, mtime_day); - }) -} - -#[test] -fn change_access_time_of_dir_to_today() { - Playground::setup("change_dir_atime", |dirs, sandbox| { - sandbox.mkdir("test_dir"); - let path = dirs.test().join("test_dir"); - - filetime::set_file_atime(&path, TIME_ONE).unwrap(); - - nu!( - cwd: dirs.test(), - "touch -a test_dir" - ); - - // Check only the date since the time may not match exactly - let today = Local::now().date_naive(); - let atime_day = - DateTime::::from(path.metadata().unwrap().accessed().unwrap()).date_naive(); - - assert_eq!(today, atime_day); - }) -} - -#[test] -fn change_modified_and_access_time_of_dir_to_today() { - Playground::setup("change_dir_times", |dirs, sandbox| { - sandbox.mkdir("test_dir"); - let path = dirs.test().join("test_dir"); - - filetime::set_file_times(&path, TIME_ONE, TIME_ONE).unwrap(); - - nu!( - cwd: dirs.test(), - "touch -a -m test_dir" - ); - - let metadata = path.metadata().unwrap(); - - // Check only the date since the time may not match exactly - let today = Local::now().date_naive(); - let mtime_day = DateTime::::from(metadata.modified().unwrap()).date_naive(); - let atime_day = DateTime::::from(metadata.accessed().unwrap()).date_naive(); - - assert_eq!(today, mtime_day); - assert_eq!(today, atime_day); - }) -} - -#[test] -fn change_dir_three_dots_times() { - Playground::setup("change_dir_three_dots_times", |dirs, sandbox| { - sandbox.mkdir("test_dir..."); - let path = dirs.test().join("test_dir..."); - - filetime::set_file_times(&path, TIME_ONE, TIME_ONE).unwrap(); - - nu!( - cwd: dirs.test(), - "touch test_dir..." - ); - - let metadata = path.metadata().unwrap(); - - // Check only the date since the time may not match exactly - let today = Local::now().date_naive(); - let mtime_day = DateTime::::from(metadata.modified().unwrap()).date_naive(); - let atime_day = DateTime::::from(metadata.accessed().unwrap()).date_naive(); - - assert_eq!(today, mtime_day); - assert_eq!(today, atime_day); - }) -} - -#[test] -fn change_dir_times_to_reference_dir() { - Playground::setup("change_dir_times_to_reference_dir", |dirs, sandbox| { - sandbox.mkdir("reference_dir"); - sandbox.mkdir("target_dir"); - - let reference = dirs.test().join("reference_dir"); - let target = dirs.test().join("target_dir"); - - // Change the times for reference - filetime::set_file_times( - &reference, - filetime::FileTime::from_unix_time(1337, 0), - TIME_ONE, - ) - .unwrap(); - - // target should have today's date since it was just created, but reference should be different - assert_ne!( - reference.metadata().unwrap().accessed().unwrap(), - target.metadata().unwrap().accessed().unwrap() - ); - assert_ne!( - reference.metadata().unwrap().modified().unwrap(), - target.metadata().unwrap().modified().unwrap() - ); - - nu!( - cwd: dirs.test(), - "touch -r reference_dir target_dir" - ); - - assert_eq!( - reference.metadata().unwrap().accessed().unwrap(), - target.metadata().unwrap().accessed().unwrap() - ); - assert_eq!( - reference.metadata().unwrap().modified().unwrap(), - target.metadata().unwrap().modified().unwrap() - ); - }) -} - -#[test] -fn change_dir_atime_to_reference() { - Playground::setup("change_dir_atime_to_reference", |dirs, sandbox| { - sandbox.mkdir("reference_dir"); - sandbox.mkdir("target_dir"); - - let reference = dirs.test().join("reference_dir"); - let target = dirs.test().join("target_dir"); - - // Change the times for reference - filetime::set_file_times( - &reference, - filetime::FileTime::from_unix_time(1337, 0), - TIME_ONE, - ) - .unwrap(); - - // target should have today's date since it was just created, but reference should be different - assert_ne!( - reference.metadata().unwrap().accessed().unwrap(), - target.metadata().unwrap().accessed().unwrap() - ); - assert_ne!( - reference.metadata().unwrap().modified().unwrap(), - target.metadata().unwrap().modified().unwrap() - ); - - // Save target's current mtime to make sure it is preserved - let target_original_mtime = target.metadata().unwrap().modified().unwrap(); - - nu!( - cwd: dirs.test(), - "touch -ar reference_dir target_dir" - ); - - assert_eq!( - reference.metadata().unwrap().accessed().unwrap(), - target.metadata().unwrap().accessed().unwrap() - ); - assert_ne!( - reference.metadata().unwrap().modified().unwrap(), - target.metadata().unwrap().modified().unwrap() - ); - assert_eq!( - target_original_mtime, - target.metadata().unwrap().modified().unwrap() - ); - }) -} - -#[test] -fn create_a_file_with_tilde() { - Playground::setup("touch with tilde", |dirs, _| { - let actual = nu!(cwd: dirs.test(), "touch '~tilde'"); - assert!(actual.err.is_empty()); - assert!(files_exist_at(&["~tilde"], dirs.test())); - - // pass variable - let actual = nu!(cwd: dirs.test(), "let f = '~tilde2'; touch $f"); - assert!(actual.err.is_empty()); - assert!(files_exist_at(&["~tilde2"], dirs.test())); - }) -} - -#[test] -fn respects_cwd() { - Playground::setup("touch_respects_cwd", |dirs, _sandbox| { - nu!( - cwd: dirs.test(), - "mkdir 'dir'; cd 'dir'; touch 'i_will_be_created.txt'" - ); - - let path = dirs.test().join("dir/i_will_be_created.txt"); - assert!(path.exists()); - }) -} - -#[test] -fn reference_respects_cwd() { - Playground::setup("touch_reference_respects_cwd", |dirs, _sandbox| { - nu!( - cwd: dirs.test(), - "mkdir 'dir'; cd 'dir'; touch 'ref.txt'; touch --reference 'ref.txt' 'foo.txt'" - ); - - let path = dirs.test().join("dir/foo.txt"); - assert!(path.exists()); - }) -} - -fn setup_symlink_fs(dirs: &Dirs, sandbox: &mut Playground<'_>) { - sandbox.mkdir("d"); - sandbox.with_files(&[Stub::EmptyFile("f"), Stub::EmptyFile("d/f")]); - sandbox.symlink("f", "fs"); - sandbox.symlink("d", "ds"); - sandbox.symlink("d/f", "fds"); - - // sandbox.symlink does not handle symlinks to missing files well. It panics - // But they are useful, and they should be tested. - #[cfg(unix)] - { - std::os::unix::fs::symlink(dirs.test().join("m"), dirs.test().join("fms")).unwrap(); - } - - #[cfg(windows)] - { - std::os::windows::fs::symlink_file(dirs.test().join("m"), dirs.test().join("fms")).unwrap(); - } - - // Change the file times to a known "old" value for comparison - filetime::set_symlink_file_times(dirs.test().join("f"), TIME_ONE, TIME_ONE).unwrap(); - filetime::set_symlink_file_times(dirs.test().join("d"), TIME_ONE, TIME_ONE).unwrap(); - filetime::set_symlink_file_times(dirs.test().join("d/f"), TIME_ONE, TIME_ONE).unwrap(); - filetime::set_symlink_file_times(dirs.test().join("ds"), TIME_ONE, TIME_ONE).unwrap(); - filetime::set_symlink_file_times(dirs.test().join("fs"), TIME_ONE, TIME_ONE).unwrap(); - filetime::set_symlink_file_times(dirs.test().join("fds"), TIME_ONE, TIME_ONE).unwrap(); - filetime::set_symlink_file_times(dirs.test().join("fms"), TIME_ONE, TIME_ONE).unwrap(); -} - -fn get_times(path: &nu_path::AbsolutePath) -> (filetime::FileTime, filetime::FileTime) { - let metadata = path.symlink_metadata().unwrap(); - - ( - filetime::FileTime::from_system_time(metadata.accessed().unwrap()), - filetime::FileTime::from_system_time(metadata.modified().unwrap()), - ) -} - -#[test] -fn follow_symlinks() { - Playground::setup("touch_follows_symlinks", |dirs, sandbox| { - setup_symlink_fs(&dirs, sandbox); - - let missing = dirs.test().join("m"); - assert!(!missing.exists()); - - nu!( - cwd: dirs.test(), - " - touch fds - touch ds - touch fs - touch fms - " - ); - - // We created the missing symlink target - assert!(missing.exists()); - - // The timestamps for files and directories were changed from TIME_ONE - let file_times = get_times(&dirs.test().join("f")); - let dir_times = get_times(&dirs.test().join("d")); - let dir_file_times = get_times(&dirs.test().join("d/f")); - - assert_ne!(file_times, (TIME_ONE, TIME_ONE)); - assert_ne!(dir_times, (TIME_ONE, TIME_ONE)); - assert_ne!(dir_file_times, (TIME_ONE, TIME_ONE)); - - // For symlinks, they remain (mostly) the same - // We can't test accessed times, since to reach the target file, the symlink must be accessed! - let file_symlink_times = get_times(&dirs.test().join("fs")); - let dir_symlink_times = get_times(&dirs.test().join("ds")); - let dir_file_symlink_times = get_times(&dirs.test().join("fds")); - let file_missing_symlink_times = get_times(&dirs.test().join("fms")); - - assert_eq!(file_symlink_times.1, TIME_ONE); - assert_eq!(dir_symlink_times.1, TIME_ONE); - assert_eq!(dir_file_symlink_times.1, TIME_ONE); - assert_eq!(file_missing_symlink_times.1, TIME_ONE); - }) -} - -#[test] -fn no_follow_symlinks() { - Playground::setup("touch_touches_symlinks", |dirs, sandbox| { - setup_symlink_fs(&dirs, sandbox); - - let missing = dirs.test().join("m"); - assert!(!missing.exists()); - - nu!( - cwd: dirs.test(), - " - touch fds -s - touch ds -s - touch fs -s - touch fms -s - " - ); - - // We did not create the missing symlink target - assert!(!missing.exists()); - - // The timestamps for files and directories remain the same - let file_times = get_times(&dirs.test().join("f")); - let dir_times = get_times(&dirs.test().join("d")); - let dir_file_times = get_times(&dirs.test().join("d/f")); - - assert_eq!(file_times, (TIME_ONE, TIME_ONE)); - assert_eq!(dir_times, (TIME_ONE, TIME_ONE)); - assert_eq!(dir_file_times, (TIME_ONE, TIME_ONE)); - - // For symlinks, everything changed. (except their targets, and paths, and personality) - let file_symlink_times = get_times(&dirs.test().join("fs")); - let dir_symlink_times = get_times(&dirs.test().join("ds")); - let dir_file_symlink_times = get_times(&dirs.test().join("fds")); - let file_missing_symlink_times = get_times(&dirs.test().join("fms")); - - assert_ne!(file_symlink_times, (TIME_ONE, TIME_ONE)); - assert_ne!(dir_symlink_times, (TIME_ONE, TIME_ONE)); - assert_ne!(dir_file_symlink_times, (TIME_ONE, TIME_ONE)); - assert_ne!(file_missing_symlink_times, (TIME_ONE, TIME_ONE)); - }) -} diff --git a/crates/nu-command/tests/commands/utouch.rs b/crates/nu-command/tests/commands/utouch.rs index f0c7ffbff5..acdf62ac59 100644 --- a/crates/nu-command/tests/commands/utouch.rs +++ b/crates/nu-command/tests/commands/utouch.rs @@ -59,7 +59,7 @@ fn creates_a_file_when_it_doesnt_exist() { Playground::setup("create_test_1", |dirs, _sandbox| { nu!( cwd: dirs.test(), - "utouch i_will_be_created.txt" + "touch i_will_be_created.txt" ); let path = dirs.test().join("i_will_be_created.txt"); @@ -72,7 +72,7 @@ fn creates_two_files() { Playground::setup("create_test_2", |dirs, _sandbox| { nu!( cwd: dirs.test(), - "utouch a b" + "touch a b" ); let path = dirs.test().join("a"); @@ -91,7 +91,7 @@ fn creates_a_file_when_glob_is_quoted() { Playground::setup("create_test_glob", |dirs, _sandbox| { nu!( cwd: dirs.test(), - "utouch '*.txt'" + "touch '*.txt'" ); let path = dirs.test().join("*.txt"); @@ -104,7 +104,7 @@ fn fails_when_glob_has_no_matches() { Playground::setup("create_test_glob_no_matches", |dirs, _sandbox| { let actual = nu!( cwd: dirs.test(), - "utouch *.txt" + "touch *.txt" ); assert!(actual.err.contains("No matches found for glob *.txt")); @@ -117,12 +117,12 @@ fn change_modified_time_of_file_to_today() { sandbox.with_files(&[Stub::EmptyFile("file.txt")]); let path = dirs.test().join("file.txt"); - // Set file.txt's times to the past before the test to make sure `utouch` actually changes the mtime to today + // Set file.txt's times to the past before the test to make sure `touch` actually changes the mtime to today filetime::set_file_times(&path, TIME_ONE, TIME_ONE).unwrap(); nu!( cwd: dirs.test(), - "utouch -m file.txt" + "touch -m file.txt" ); let metadata = path.metadata().unwrap(); @@ -147,12 +147,12 @@ fn change_access_time_of_file_to_today() { sandbox.with_files(&[Stub::EmptyFile("file.txt")]); let path = dirs.test().join("file.txt"); - // Set file.txt's times to the past before the test to make sure `utouch` actually changes the atime to today + // Set file.txt's times to the past before the test to make sure `touch` actually changes the atime to today filetime::set_file_times(&path, TIME_ONE, TIME_ONE).unwrap(); nu!( cwd: dirs.test(), - "utouch -a file.txt" + "touch -a file.txt" ); let metadata = path.metadata().unwrap(); @@ -181,7 +181,7 @@ fn change_modified_and_access_time_of_file_to_today() { nu!( cwd: dirs.test(), - "utouch -a -m file.txt" + "touch -a -m file.txt" ); let metadata = path.metadata().unwrap(); @@ -206,7 +206,7 @@ fn change_modified_and_access_time_of_files_matching_glob_to_today() { nu!( cwd: dirs.test(), - "utouch *.txt" + "touch *.txt" ); let metadata = path.metadata().unwrap(); @@ -226,14 +226,14 @@ fn not_create_file_if_it_not_exists() { Playground::setup("change_time_test_28", |dirs, _sandbox| { let outcome = nu!( cwd: dirs.test(), - "utouch -c file.txt" + "touch -c file.txt" ); let path = dirs.test().join("file.txt"); assert!(!path.exists()); - // If --no-create is improperly handled `utouch` may error when trying to change the times of a nonexistent file + // If --no-create is improperly handled `touch` may error when trying to change the times of a nonexistent file assert!(outcome.status.success()) }) } @@ -250,7 +250,7 @@ fn change_file_times_if_exists_with_no_create() { nu!( cwd: dirs.test(), - "utouch -c file.txt" + "touch -c file.txt" ); let metadata = path.metadata().unwrap(); @@ -271,7 +271,7 @@ fn creates_file_three_dots() { Playground::setup("create_test_1", |dirs, _sandbox| { nu!( cwd: dirs.test(), - "utouch file..." + "touch file..." ); let path = dirs.test().join("file..."); @@ -284,7 +284,7 @@ fn creates_file_four_dots() { Playground::setup("create_test_1", |dirs, _sandbox| { nu!( cwd: dirs.test(), - "utouch file...." + "touch file...." ); let path = dirs.test().join("file...."); @@ -297,7 +297,7 @@ fn creates_file_four_dots_quotation_marks() { Playground::setup("create_test_1", |dirs, _sandbox| { nu!( cwd: dirs.test(), - "utouch 'file....'" + "touch 'file....'" ); let path = dirs.test().join("file...."); @@ -331,7 +331,7 @@ fn change_file_times_to_reference_file() { nu!( cwd: dirs.test(), - "utouch -r reference_file target_file" + "touch -r reference_file target_file" ); assert_eq!( @@ -367,7 +367,7 @@ fn change_file_mtime_to_reference() { nu!( cwd: dirs.test(), - "utouch -mr reference_file target_file" + "touch -mr reference_file target_file" ); assert_eq!( @@ -416,7 +416,7 @@ fn change_file_times_to_reference_file_with_date() { nu!( cwd: dirs.test(), - r#"utouch -r reference_file -d "yesterday" target_file"# + r#"touch -r reference_file -d "yesterday" target_file"# ); let (got_atime, got_mtime) = file_times(target); @@ -445,7 +445,7 @@ fn change_file_times_to_timestamp() { .unwrap() .to_rfc3339(); - nu!(cwd: dirs.test(), format!("utouch --timestamp {} target_file", timestamp)); + nu!(cwd: dirs.test(), format!("touch --timestamp {} target_file", timestamp)); assert_eq!((TIME_ONE, TIME_ONE), file_times(target)); }) @@ -461,7 +461,7 @@ fn change_modified_time_of_dir_to_today() { nu!( cwd: dirs.test(), - "utouch -m test_dir" + "touch -m test_dir" ); // Check only the date since the time may not match exactly @@ -483,7 +483,7 @@ fn change_access_time_of_dir_to_today() { nu!( cwd: dirs.test(), - "utouch -a test_dir" + "touch -a test_dir" ); // Check only the date since the time may not match exactly @@ -505,7 +505,7 @@ fn change_modified_and_access_time_of_dir_to_today() { nu!( cwd: dirs.test(), - "utouch -a -m test_dir" + "touch -a -m test_dir" ); let metadata = path.metadata().unwrap(); @@ -529,7 +529,7 @@ fn change_file_times_to_date() { sandbox.with_files(&[Stub::EmptyFile("target_file")]); let expected = Utc::now().checked_sub_signed(TimeDelta::hours(2)).unwrap(); - nu!(cwd: dirs.test(), "utouch -d '-2 hours' target_file"); + nu!(cwd: dirs.test(), "touch -d '-2 hours' target_file"); let (got_atime, got_mtime) = file_times(dirs.test().join("target_file")); let got_atime = @@ -559,7 +559,7 @@ fn change_dir_three_dots_times() { nu!( cwd: dirs.test(), - "utouch test_dir..." + "touch test_dir..." ); let metadata = path.metadata().unwrap(); @@ -598,7 +598,7 @@ fn change_dir_times_to_reference_dir() { nu!( cwd: dirs.test(), - "utouch -r reference_dir target_dir" + "touch -r reference_dir target_dir" ); assert_eq!( @@ -639,7 +639,7 @@ fn change_dir_atime_to_reference() { nu!( cwd: dirs.test(), - "utouch -ar reference_dir target_dir" + "touch -ar reference_dir target_dir" ); assert_eq!( @@ -659,13 +659,13 @@ fn change_dir_atime_to_reference() { #[test] fn create_a_file_with_tilde() { - Playground::setup("utouch with tilde", |dirs, _| { - let actual = nu!(cwd: dirs.test(), "utouch '~tilde'"); + Playground::setup("touch with tilde", |dirs, _| { + let actual = nu!(cwd: dirs.test(), "touch '~tilde'"); assert!(actual.err.is_empty()); assert!(files_exist_at(&[Path::new("~tilde")], dirs.test())); // pass variable - let actual = nu!(cwd: dirs.test(), "let f = '~tilde2'; utouch $f"); + let actual = nu!(cwd: dirs.test(), "let f = '~tilde2'; touch $f"); assert!(actual.err.is_empty()); assert!(files_exist_at(&[Path::new("~tilde2")], dirs.test())); }) @@ -673,10 +673,10 @@ fn create_a_file_with_tilde() { #[test] fn respects_cwd() { - Playground::setup("utouch_respects_cwd", |dirs, _sandbox| { + Playground::setup("touch_respects_cwd", |dirs, _sandbox| { nu!( cwd: dirs.test(), - "mkdir 'dir'; cd 'dir'; utouch 'i_will_be_created.txt'" + "mkdir 'dir'; cd 'dir'; touch 'i_will_be_created.txt'" ); let path = dirs.test().join("dir/i_will_be_created.txt"); @@ -686,10 +686,10 @@ fn respects_cwd() { #[test] fn reference_respects_cwd() { - Playground::setup("utouch_reference_respects_cwd", |dirs, _sandbox| { + Playground::setup("touch_reference_respects_cwd", |dirs, _sandbox| { nu!( cwd: dirs.test(), - "mkdir 'dir'; cd 'dir'; utouch 'ref.txt'; utouch --reference 'ref.txt' 'foo.txt'" + "mkdir 'dir'; cd 'dir'; touch 'ref.txt'; touch --reference 'ref.txt' 'foo.txt'" ); let path = dirs.test().join("dir/foo.txt"); @@ -699,8 +699,8 @@ fn reference_respects_cwd() { #[test] fn recognizes_stdout() { - Playground::setup("utouch_recognizes_stdout", |dirs, _sandbox| { - nu!(cwd: dirs.test(), "utouch -"); + Playground::setup("touch_recognizes_stdout", |dirs, _sandbox| { + nu!(cwd: dirs.test(), "touch -"); assert!(!dirs.test().join("-").exists()); }) }