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;