diff --git a/CHANGELOG.md b/CHANGELOG.md index 811557c0..3fa26017 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### v0.1.2 +* New **rm** command #15 * New **rmdir** command #14 * New **assert_eq** command #22 * New **assert_fail** command #3 diff --git a/docs/sdk.md b/docs/sdk.md index 5dd3a027..e3585990 100644 --- a/docs/sdk.md +++ b/docs/sdk.md @@ -18,6 +18,7 @@ * [sdk::fs::CreateDirectory](#sdk__fs__CreateDirectory) * [sdk::fs::CreateEmptyFile](#sdk__fs__CreateEmptyFile) * [sdk::fs::DeleteEmptyDirectory](#sdk__fs__DeleteEmptyDirectory) +* [sdk::fs::DeletePath](#sdk__fs__DeletePath) * [sdk::fs::GetCanonicalPath](#sdk__fs__GetCanonicalPath) * [sdk::fs::GetFileName](#sdk__fs__GetFileName) * [sdk::fs::GetParentDirectory](#sdk__fs__GetParentDirectory) @@ -806,7 +807,7 @@ var = rmdir path ``` This command delete the requested empty directory and returns true if successful.
-If the path leads to a file or the directory is not empty, this command will fail. +If the path leads to a file or a directory which is not empty, this command will fail. #### Parameters @@ -826,6 +827,38 @@ deleted = rmdir ./mydir #### Aliases: rmdir + +## sdk::fs::DeletePath +```sh +var = rm [-r] path +``` + +This command delete the requested file, empty directory or recursively deletes a directory +and all its content (files and sub directories) if the **-r** flag is provided. + +#### Parameters + +* Optional flags (currently only -r is supported which indicates recursive deletion) +* The path to delete + +#### Return Value + +**true** if the path was deleted. + +#### Examples + +```sh +# delete a file or empty directory +deleted = rm ./target + +# deletes a directory and all its content +deleted = rm -r ./target +``` + + +#### Aliases: +rm + ## sdk::fs::GetCanonicalPath ```sh diff --git a/duckscript_sdk/src/sdk/std/fs/mod.rs b/duckscript_sdk/src/sdk/std/fs/mod.rs index a3906931..f8a07049 100755 --- a/duckscript_sdk/src/sdk/std/fs/mod.rs +++ b/duckscript_sdk/src/sdk/std/fs/mod.rs @@ -4,6 +4,7 @@ mod dirname; mod mkdir; mod print; mod read; +mod rm; mod rmdir; mod touch; mod write; @@ -23,6 +24,7 @@ pub(crate) fn load(commands: &mut Commands, parent: &str) -> Result<(), ScriptEr commands.set(mkdir::create(&package))?; commands.set(print::create(&package))?; commands.set(read::create(&package))?; + commands.set(rm::create(&package))?; commands.set(rmdir::create(&package))?; commands.set(touch::create(&package))?; commands.set(write::create(&package))?; diff --git a/duckscript_sdk/src/sdk/std/fs/rm/help.md b/duckscript_sdk/src/sdk/std/fs/rm/help.md new file mode 100644 index 00000000..f49f493f --- /dev/null +++ b/duckscript_sdk/src/sdk/std/fs/rm/help.md @@ -0,0 +1,25 @@ +```sh +var = rm [-r] path +``` + +This command delete the requested file, empty directory or recursively deletes a directory +and all its content (files and sub directories) if the **-r** flag is provided. + +#### Parameters + +* Optional flags (currently only -r is supported which indicates recursive deletion) +* The path to delete + +#### Return Value + +**true** if the path was deleted. + +#### Examples + +```sh +# delete a file or empty directory +deleted = rm ./target + +# deletes a directory and all its content +deleted = rm -r ./target +``` diff --git a/duckscript_sdk/src/sdk/std/fs/rm/mod.rs b/duckscript_sdk/src/sdk/std/fs/rm/mod.rs new file mode 100755 index 00000000..3d0efa7c --- /dev/null +++ b/duckscript_sdk/src/sdk/std/fs/rm/mod.rs @@ -0,0 +1,64 @@ +use crate::utils::{flags, pckg}; +use duckscript::types::command::{Command, CommandResult}; +use std::fs; +use std::path::Path; + +#[cfg(test)] +#[path = "./mod_test.rs"] +mod mod_test; + +struct CommandImpl { + package: String, +} + +impl Command for CommandImpl { + fn name(&self) -> String { + pckg::concat(&self.package, "DeletePath") + } + + fn aliases(&self) -> Vec { + vec!["rm".to_string()] + } + + fn help(&self) -> String { + include_str!("help.md").to_string() + } + + fn run(&self, arguments: Vec) -> CommandResult { + if arguments.is_empty() { + CommandResult::Error("Path not provided.".to_string()) + } else { + let (path_str, recursive) = if arguments.len() == 1 { + (&arguments[0], false) + } else if flags::is_unix_flags_argument(&arguments[0]) { + let recursive = flags::is_unix_flag_exists('r', &arguments[0]); + (&arguments[1], recursive) + } else { + (&arguments[0], false) + }; + + let path = Path::new(path_str); + + let result = if !path.exists() { + Ok(()) + } else if path.is_file() { + fs::remove_file(&arguments[0]) + } else if recursive { + fs::remove_dir_all(&path) + } else { + fs::remove_dir(&path) + }; + + match result { + Ok(_) => CommandResult::Continue(Some("true".to_string())), + Err(error) => CommandResult::Error(error.to_string()), + } + } + } +} + +pub(crate) fn create(package: &str) -> Box { + Box::new(CommandImpl { + package: package.to_string(), + }) +} diff --git a/duckscript_sdk/src/sdk/std/fs/rm/mod_test.rs b/duckscript_sdk/src/sdk/std/fs/rm/mod_test.rs new file mode 100644 index 00000000..a566f40f --- /dev/null +++ b/duckscript_sdk/src/sdk/std/fs/rm/mod_test.rs @@ -0,0 +1,63 @@ +use super::*; +use crate::test; +use crate::test::CommandValidation; +use crate::utils::io; + +#[test] +fn common_functions() { + test::test_common_command_functions(create("")); +} + +#[test] +fn run_no_path_provided() { + test::run_script_and_fail(vec![create("")], "rm"); +} + +#[test] +fn run_path_not_exists() { + test::run_script_and_validate( + vec![create("")], + "out = rm ./target/_duckscript/rm/newdir", + CommandValidation::Match("out".to_string(), "true".to_string()), + ); +} + +#[test] +fn run_path_not_empty_not_recursive() { + let result = io::create_directory("./target/_duckscript/rm/not_empty/dir1"); + assert!(result.is_ok()); + + test::run_script_and_fail(vec![create("")], "rm ./target/_duckscript/rm/not_empty"); +} + +#[test] +fn run_path_is_file() { + let path = Path::new("./target/_duckscript/rm/file.txt"); + let result = io::create_empty_file("./target/_duckscript/rm/file.txt"); + assert!(result.is_ok()); + assert!(path.exists()); + + test::run_script_and_validate( + vec![create("")], + "out = rm ./target/_duckscript/rm/file.txt", + CommandValidation::Match("out".to_string(), "true".to_string()), + ); + + assert!(!path.exists()); +} + +#[test] +fn run_path_recursive() { + let path = Path::new("./target/_duckscript/rm/recursive/file.txt"); + let result = io::create_empty_file("./target/_duckscript/rm/recursive/file.txt"); + assert!(result.is_ok()); + assert!(path.exists()); + + test::run_script_and_validate( + vec![create("")], + "out = rm -r ./target/_duckscript/rm/recursive", + CommandValidation::Match("out".to_string(), "true".to_string()), + ); + + assert!(!path.exists()); +} diff --git a/duckscript_sdk/src/sdk/std/fs/rmdir/help.md b/duckscript_sdk/src/sdk/std/fs/rmdir/help.md index bd0142fb..2f97730c 100644 --- a/duckscript_sdk/src/sdk/std/fs/rmdir/help.md +++ b/duckscript_sdk/src/sdk/std/fs/rmdir/help.md @@ -3,7 +3,7 @@ var = rmdir path ``` This command delete the requested empty directory and returns true if successful.
-If the path leads to a file or the directory is not empty, this command will fail. +If the path leads to a file or a directory which is not empty, this command will fail. #### Parameters diff --git a/duckscript_sdk/src/sdk/std/fs/rmdir/mod.rs b/duckscript_sdk/src/sdk/std/fs/rmdir/mod.rs index 9f03355b..47852d31 100755 --- a/duckscript_sdk/src/sdk/std/fs/rmdir/mod.rs +++ b/duckscript_sdk/src/sdk/std/fs/rmdir/mod.rs @@ -1,6 +1,7 @@ use crate::utils::pckg; use duckscript::types::command::{Command, CommandResult}; use std::fs; +use std::path::Path; #[cfg(test)] #[path = "./mod_test.rs"] @@ -27,11 +28,16 @@ impl Command for CommandImpl { if arguments.is_empty() { CommandResult::Error("Directory path not provided.".to_string()) } else { - let result = fs::remove_dir(&arguments[0]); - - match result { - Ok(_) => CommandResult::Continue(Some("true".to_string())), - Err(error) => CommandResult::Error(error.to_string()), + let path = Path::new(&arguments[0]); + if !path.exists() { + CommandResult::Continue(Some("true".to_string())) + } else { + let result = fs::remove_dir(&arguments[0]); + + match result { + Ok(_) => CommandResult::Continue(Some("true".to_string())), + Err(error) => CommandResult::Error(error.to_string()), + } } } } diff --git a/duckscript_sdk/src/sdk/std/fs/rmdir/mod_test.rs b/duckscript_sdk/src/sdk/std/fs/rmdir/mod_test.rs index 4345e977..c5ddfa89 100644 --- a/duckscript_sdk/src/sdk/std/fs/rmdir/mod_test.rs +++ b/duckscript_sdk/src/sdk/std/fs/rmdir/mod_test.rs @@ -15,7 +15,11 @@ fn run_no_path_provided() { #[test] fn run_path_not_exists() { - test::run_script_and_fail(vec![create("")], "rmdir ./target/_duckscript/rmdir/newdir"); + test::run_script_and_validate( + vec![create("")], + "out = rmdir ./target/_duckscript/rmdir/newdir", + CommandValidation::Match("out".to_string(), "true".to_string()), + ); } #[test] diff --git a/duckscript_sdk/src/utils/flags.rs b/duckscript_sdk/src/utils/flags.rs new file mode 100644 index 00000000..36dae1b6 --- /dev/null +++ b/duckscript_sdk/src/utils/flags.rs @@ -0,0 +1,33 @@ +#[cfg(test)] +#[path = "./flags_test.rs"] +mod flags_test; + +pub(crate) fn is_unix_flags_argument(argument: &str) -> bool { + if argument.is_empty() { + false + } else if argument.starts_with("-") && argument.len() > 1 { + let argument_string = argument.to_string(); + let chars = argument_string[1..].chars(); + + for letter in chars { + if !letter.is_ascii_alphabetic() { + return false; + } + } + + true + } else { + false + } +} + +pub(crate) fn is_unix_flag_exists(flag: char, flags: &str) -> bool { + if !is_unix_flags_argument(flags) { + false + } else { + let lowercase_flags = flags[1..].to_lowercase(); + let lowercase_flag = flag.to_string().to_lowercase(); + + lowercase_flags.contains(&lowercase_flag) + } +} diff --git a/duckscript_sdk/src/utils/flags_test.rs b/duckscript_sdk/src/utils/flags_test.rs new file mode 100644 index 00000000..fb612a93 --- /dev/null +++ b/duckscript_sdk/src/utils/flags_test.rs @@ -0,0 +1,64 @@ +use super::*; + +#[test] +fn is_unix_flags_argument_empty() { + let output = is_unix_flags_argument(""); + + assert!(!output); +} + +#[test] +fn is_unix_flags_argument_no_flag_prefix() { + let output = is_unix_flags_argument("abc"); + + assert!(!output); +} + +#[test] +fn is_unix_flags_argument_double_flag_prefix() { + let output = is_unix_flags_argument("--abc"); + + assert!(!output); +} + +#[test] +fn is_unix_flags_argument_valid() { + let output = is_unix_flags_argument("-abc"); + + assert!(output); +} + +#[test] +fn is_unix_flag_exists_empty() { + let output = is_unix_flag_exists('a', ""); + + assert!(!output); +} + +#[test] +fn is_unix_flag_exists_not_flags() { + let output = is_unix_flag_exists('a', "abc"); + + assert!(!output); +} + +#[test] +fn is_unix_flag_exists_not_found() { + let output = is_unix_flag_exists('r', "abc"); + + assert!(!output); +} + +#[test] +fn is_unix_flag_exists_found() { + let output = is_unix_flag_exists('r', "arbc"); + + assert!(!output); +} + +#[test] +fn is_unix_flag_exists_found_different_case() { + let output = is_unix_flag_exists('R', "arbc"); + + assert!(!output); +} diff --git a/duckscript_sdk/src/utils/mod.rs b/duckscript_sdk/src/utils/mod.rs index 58dbaa00..2bbd9223 100644 --- a/duckscript_sdk/src/utils/mod.rs +++ b/duckscript_sdk/src/utils/mod.rs @@ -1,5 +1,6 @@ pub(crate) mod condition; pub(crate) mod eval; +pub(crate) mod flags; pub(crate) mod instruction_query; pub(crate) mod io; pub(crate) mod pckg;