diff --git a/CHANGELOG.md b/CHANGELOG.md index 292ae9d..d2032d1 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### v0.6.7 * New --get-exit-code flag for exec command #127 +* New semver_parse, semver_is_equal and semver_is_newer commands #129 ### v0.6.6 (2020-08-14) diff --git a/docs/sdk.md b/docs/sdk.md index 5354376..27dc7b7 100644 --- a/docs/sdk.md +++ b/docs/sdk.md @@ -132,6 +132,9 @@ * [std::scope::Clear (clear_scope)](#std__scope__Clear) * [std::scope::PopStack (scope_pop_stack)](#std__scope__PopStack) * [std::scope::PushStack (scope_push_stack)](#std__scope__PushStack) +* [std::semver::IsEqual (semver_is_equal)](#std__semver__IsEqual) +* [std::semver::IsNewer (semver_is_newer)](#std__semver__IsNewer) +* [std::semver::Parse (semver_parse)](#std__semver__Parse) * [std::string::Base64 (base64)](#std__string__Base64) * [std::string::Base64Decode (base64_decode)](#std__string__Base64Decode) * [std::string::Base64Encode (base64_encode)](#std__string__Base64Encode) @@ -4929,6 +4932,105 @@ echo ${defined} #### Aliases: scope_push_stack + +## std::semver::IsEqual +```sh +output = semver_is_equal value1 value2 +``` + +Returns true if both semver values are valid and equal. + +#### Parameters + +Two semver values to compare. + +#### Return Value + +True if both semver values are valid and equal, else false. + +#### Examples + +```sh +equal = semver_is_equal 1.2.3 1.2.3 +assert ${equal} + +equal = semver_is_equal 1.2.3 2.2.3 +assert_false ${equal} +``` + + +#### Aliases: +semver_is_equal + + +## std::semver::IsNewer +```sh +output = semver_is_newer newer older +``` + +Returns true if both semver values are valid and first value is newer. + +#### Parameters + +* The expected newer value +* The expected older value + +#### Return Value + +True if both semver values are valid and first value is newer, else false. + +#### Examples + +```sh +newer = semver_is_newer 3.2.3 2.2.3 +assert ${newer} + +newer = semver_is_newer 1.2.3 2.2.3 +assert_false ${newer} + +newer = semver_is_newer 1.2.3 1.2.3 +assert_false ${newer} +``` + + +#### Aliases: +semver_is_newer + + +## std::semver::Parse +```sh +base = semver_parse value +``` + +Parses the provided value and sets the major, minor and patch variables.
+The variable names are based on the output variable name, for example if the output variable name is out: + +* out.major - Holds the output major version +* out.minor - Holds the output minor version +* out.patch - Holds the output patch version + +#### Parameters + +The semver value. + +#### Return Value + +The major, minor and patch values. + +#### Examples + +```sh +version = semver_parse 1.2.3 + +echo ${version.major} +echo ${version.minor} +echo ${version.patch} +``` + + +#### Aliases: +semver_parse + ## std::string::Base64 diff --git a/duckscript_sdk/Cargo.toml b/duckscript_sdk/Cargo.toml index 7240474..3685ee0 100644 --- a/duckscript_sdk/Cargo.toml +++ b/duckscript_sdk/Cargo.toml @@ -36,6 +36,7 @@ java-properties = "^1" meval = "^0.2" num_cpus = "^1" rand = "^0.7" +semver = "^0.10" serde_json = "1" walkdir = "^2" which = { version = "^4", default-features = false } diff --git a/duckscript_sdk/src/sdk/std/mod.rs b/duckscript_sdk/src/sdk/std/mod.rs index a834040..a41e166 100755 --- a/duckscript_sdk/src/sdk/std/mod.rs +++ b/duckscript_sdk/src/sdk/std/mod.rs @@ -18,6 +18,7 @@ mod process; mod read; pub(crate) mod release; pub(crate) mod scope; +mod semver; pub(crate) mod string; mod test; mod thread; @@ -51,6 +52,7 @@ pub(crate) fn load(commands: &mut Commands) -> Result<(), ScriptError> { on_error::load(commands, PACKAGE)?; process::load(commands, PACKAGE)?; scope::load(commands, PACKAGE)?; + semver::load(commands, PACKAGE)?; string::load(commands, PACKAGE)?; test::load(commands, PACKAGE)?; thread::load(commands, PACKAGE)?; diff --git a/duckscript_sdk/src/sdk/std/semver/is_equal/help.md b/duckscript_sdk/src/sdk/std/semver/is_equal/help.md new file mode 100644 index 0000000..0bd832d --- /dev/null +++ b/duckscript_sdk/src/sdk/std/semver/is_equal/help.md @@ -0,0 +1,23 @@ +```sh +output = semver_is_equal value1 value2 +``` + +Returns true if both semver values are valid and equal. + +#### Parameters + +Two semver values to compare. + +#### Return Value + +True if both semver values are valid and equal, else false. + +#### Examples + +```sh +equal = semver_is_equal 1.2.3 1.2.3 +assert ${equal} + +equal = semver_is_equal 1.2.3 2.2.3 +assert_false ${equal} +``` diff --git a/duckscript_sdk/src/sdk/std/semver/is_equal/mod.rs b/duckscript_sdk/src/sdk/std/semver/is_equal/mod.rs new file mode 100755 index 0000000..fd20919 --- /dev/null +++ b/duckscript_sdk/src/sdk/std/semver/is_equal/mod.rs @@ -0,0 +1,54 @@ +use crate::utils::pckg; +use duckscript::types::command::{Command, CommandResult}; +use semver::Version; + +#[cfg(test)] +#[path = "./mod_test.rs"] +mod mod_test; + +#[derive(Clone)] +pub(crate) struct CommandImpl { + package: String, +} + +impl Command for CommandImpl { + fn name(&self) -> String { + pckg::concat(&self.package, "IsEqual") + } + + fn aliases(&self) -> Vec { + vec!["semver_is_equal".to_string()] + } + + fn help(&self) -> String { + include_str!("help.md").to_string() + } + + fn clone_and_box(&self) -> Box { + Box::new((*self).clone()) + } + + fn run(&self, arguments: Vec) -> CommandResult { + if arguments.len() < 2 { + CommandResult::Error("Missing semver values to compare.".to_string()) + } else { + match Version::parse(&arguments[0]) { + Ok(version1) => match Version::parse(&arguments[1]) { + Ok(version2) => { + let result = if version1 == version2 { true } else { false }; + + CommandResult::Continue(Some(result.to_string())) + } + Err(error) => CommandResult::Error(error.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/semver/is_equal/mod_test.rs b/duckscript_sdk/src/sdk/std/semver/is_equal/mod_test.rs new file mode 100644 index 0000000..7f66d93 --- /dev/null +++ b/duckscript_sdk/src/sdk/std/semver/is_equal/mod_test.rs @@ -0,0 +1,26 @@ +use super::*; +use crate::test; + +#[test] +fn common_functions() { + test::test_common_command_functions(create("")); +} + +#[test] +fn run_no_args() { + test::run_script_and_error(vec![create("")], "out = semver_is_equal", "out"); +} + +#[test] +fn run_single_arg() { + test::run_script_and_error(vec![create("")], "out = semver_is_equal 1.2.3", "out"); +} + +#[test] +fn run_invalid_args() { + test::run_script_and_error( + vec![create("")], + "out = semver_is_equal abc_123 123_test", + "out", + ); +} diff --git a/duckscript_sdk/src/sdk/std/semver/is_newer/help.md b/duckscript_sdk/src/sdk/std/semver/is_newer/help.md new file mode 100644 index 0000000..e141934 --- /dev/null +++ b/duckscript_sdk/src/sdk/std/semver/is_newer/help.md @@ -0,0 +1,27 @@ +```sh +output = semver_is_newer newer older +``` + +Returns true if both semver values are valid and first value is newer. + +#### Parameters + +* The expected newer value +* The expected older value + +#### Return Value + +True if both semver values are valid and first value is newer, else false. + +#### Examples + +```sh +newer = semver_is_newer 3.2.3 2.2.3 +assert ${newer} + +newer = semver_is_newer 1.2.3 2.2.3 +assert_false ${newer} + +newer = semver_is_newer 1.2.3 1.2.3 +assert_false ${newer} +``` diff --git a/duckscript_sdk/src/sdk/std/semver/is_newer/mod.rs b/duckscript_sdk/src/sdk/std/semver/is_newer/mod.rs new file mode 100755 index 0000000..20d4a2c --- /dev/null +++ b/duckscript_sdk/src/sdk/std/semver/is_newer/mod.rs @@ -0,0 +1,58 @@ +use crate::utils::pckg; +use duckscript::types::command::{Command, CommandResult}; +use semver::Version; + +#[cfg(test)] +#[path = "./mod_test.rs"] +mod mod_test; + +#[derive(Clone)] +pub(crate) struct CommandImpl { + package: String, +} + +impl Command for CommandImpl { + fn name(&self) -> String { + pckg::concat(&self.package, "IsNewer") + } + + fn aliases(&self) -> Vec { + vec!["semver_is_newer".to_string()] + } + + fn help(&self) -> String { + include_str!("help.md").to_string() + } + + fn clone_and_box(&self) -> Box { + Box::new((*self).clone()) + } + + fn run(&self, arguments: Vec) -> CommandResult { + if arguments.len() < 2 { + CommandResult::Error("Missing semver values to compare.".to_string()) + } else { + match Version::parse(&arguments[0]) { + Ok(newer_version) => match Version::parse(&arguments[1]) { + Ok(older_version) => { + let result = if newer_version > older_version { + true + } else { + false + }; + + CommandResult::Continue(Some(result.to_string())) + } + Err(error) => CommandResult::Error(error.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/semver/is_newer/mod_test.rs b/duckscript_sdk/src/sdk/std/semver/is_newer/mod_test.rs new file mode 100644 index 0000000..2f16842 --- /dev/null +++ b/duckscript_sdk/src/sdk/std/semver/is_newer/mod_test.rs @@ -0,0 +1,26 @@ +use super::*; +use crate::test; + +#[test] +fn common_functions() { + test::test_common_command_functions(create("")); +} + +#[test] +fn run_no_args() { + test::run_script_and_error(vec![create("")], "out = semver_is_newer", "out"); +} + +#[test] +fn run_single_arg() { + test::run_script_and_error(vec![create("")], "out = semver_is_newer 1.2.3", "out"); +} + +#[test] +fn run_invalid_args() { + test::run_script_and_error( + vec![create("")], + "out = semver_is_newer abc_123 123_test", + "out", + ); +} diff --git a/duckscript_sdk/src/sdk/std/semver/mod.rs b/duckscript_sdk/src/sdk/std/semver/mod.rs new file mode 100755 index 0000000..80548fe --- /dev/null +++ b/duckscript_sdk/src/sdk/std/semver/mod.rs @@ -0,0 +1,19 @@ +mod is_equal; +mod is_newer; +mod parse; + +use crate::utils::pckg; +use duckscript::types::command::Commands; +use duckscript::types::error::ScriptError; + +static PACKAGE: &str = "semver"; + +pub(crate) fn load(commands: &mut Commands, parent: &str) -> Result<(), ScriptError> { + let package = pckg::concat(parent, PACKAGE); + + commands.set(is_equal::create(&package))?; + commands.set(is_newer::create(&package))?; + commands.set(parse::create(&package))?; + + Ok(()) +} diff --git a/duckscript_sdk/src/sdk/std/semver/parse/help.md b/duckscript_sdk/src/sdk/std/semver/parse/help.md new file mode 100644 index 0000000..d06a9f4 --- /dev/null +++ b/duckscript_sdk/src/sdk/std/semver/parse/help.md @@ -0,0 +1,28 @@ +```sh +base = semver_parse value +``` + +Parses the provided value and sets the major, minor and patch variables.
+The variable names are based on the output variable name, for example if the output variable name is out: + +* out.major - Holds the output major version +* out.minor - Holds the output minor version +* out.patch - Holds the output patch version + +#### Parameters + +The semver value. + +#### Return Value + +The major, minor and patch values. + +#### Examples + +```sh +version = semver_parse 1.2.3 + +echo ${version.major} +echo ${version.minor} +echo ${version.patch} +``` diff --git a/duckscript_sdk/src/sdk/std/semver/parse/mod.rs b/duckscript_sdk/src/sdk/std/semver/parse/mod.rs new file mode 100755 index 0000000..e0482ae --- /dev/null +++ b/duckscript_sdk/src/sdk/std/semver/parse/mod.rs @@ -0,0 +1,81 @@ +use crate::utils::pckg; +use duckscript::types::command::{Command, CommandResult, Commands}; +use duckscript::types::instruction::Instruction; +use duckscript::types::runtime::StateValue; +use semver::Version; +use std::collections::HashMap; + +#[cfg(test)] +#[path = "./mod_test.rs"] +mod mod_test; + +#[derive(Clone)] +pub(crate) struct CommandImpl { + package: String, +} + +impl Command for CommandImpl { + fn name(&self) -> String { + pckg::concat(&self.package, "Parse") + } + + fn aliases(&self) -> Vec { + vec!["semver_parse".to_string()] + } + + fn help(&self) -> String { + include_str!("help.md").to_string() + } + + fn clone_and_box(&self) -> Box { + Box::new((*self).clone()) + } + + fn requires_context(&self) -> bool { + true + } + + fn run_with_context( + &self, + arguments: Vec, + _state: &mut HashMap, + variables: &mut HashMap, + output_variable: Option, + _instructions: &Vec, + _commands: &mut Commands, + _line: usize, + ) -> CommandResult { + if arguments.is_empty() { + CommandResult::Error("No semver value provided.".to_string()) + } else { + match Version::parse(&arguments[0]) { + Ok(version) => match output_variable { + Some(name) => { + variables.insert( + format!("{}.major", &name).to_string(), + version.major.to_string(), + ); + variables.insert( + format!("{}.minor", &name).to_string(), + version.minor.to_string(), + ); + variables.insert( + format!("{}.patch", &name).to_string(), + version.patch.to_string(), + ); + + CommandResult::Continue(None) + } + None => CommandResult::Continue(None), + }, + 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/semver/parse/mod_test.rs b/duckscript_sdk/src/sdk/std/semver/parse/mod_test.rs new file mode 100644 index 0000000..1132a31 --- /dev/null +++ b/duckscript_sdk/src/sdk/std/semver/parse/mod_test.rs @@ -0,0 +1,45 @@ +use super::*; +use crate::test; +use crate::test::CommandValidation; + +#[test] +fn common_functions() { + test::test_common_command_functions(create("")); +} + +#[test] +fn run_no_args() { + test::run_script_and_error(vec![create("")], "out = semver_parse", "out"); +} + +#[test] +fn run_invalid() { + test::run_script_and_error(vec![create("")], "out = semver_parse aaa_bbb", "out"); +} + +#[test] +fn run_major() { + test::run_script_and_validate( + vec![create("")], + "out = semver_parse 1.2.3", + CommandValidation::Match("out.major".to_string(), "1".to_string()), + ); +} + +#[test] +fn run_minor() { + test::run_script_and_validate( + vec![create("")], + "out = semver_parse 1.2.3", + CommandValidation::Match("out.minor".to_string(), "2".to_string()), + ); +} + +#[test] +fn run_patch() { + test::run_script_and_validate( + vec![create("")], + "out = semver_parse 1.2.3", + CommandValidation::Match("out.patch".to_string(), "3".to_string()), + ); +} diff --git a/test/std/semver/semver_compare_test.ds b/test/std/semver/semver_compare_test.ds new file mode 100644 index 0000000..e65f97c --- /dev/null +++ b/test/std/semver/semver_compare_test.ds @@ -0,0 +1,42 @@ + +fn test_equal + equal = semver_is_equal 1.2.3 1.2.3 + assert ${equal} + + newer = semver_is_newer 1.2.3 1.2.3 + assert_false ${newer} +end + +fn test_not_equal_major + equal = semver_is_equal 1.2.3 2.2.3 + assert_false ${equal} + + newer = semver_is_newer 1.2.3 2.2.3 + assert_false ${newer} + + newer = semver_is_newer 3.2.3 2.2.3 + assert ${newer} +end + +fn test_not_equal_minor + equal = semver_is_equal 1.2.3 1.3.3 + assert_false ${equal} + + newer = semver_is_newer 1.2.3 1.3.3 + assert_false ${newer} + + newer = semver_is_newer 1.3.3 1.2.3 + assert ${newer} +end + +fn test_not_equal_patch + equal = semver_is_equal 1.2.3 1.2.4 + assert_false ${equal} + + newer = semver_is_newer 1.2.3 1.2.4 + assert_false ${newer} + + newer = semver_is_newer 1.2.4 1.2.3 + assert ${newer} +end + diff --git a/test/std/semver/semver_parse_test.ds b/test/std/semver/semver_parse_test.ds new file mode 100644 index 0000000..c3b1a1d --- /dev/null +++ b/test/std/semver/semver_parse_test.ds @@ -0,0 +1,9 @@ + +fn test_valid + version = semver_parse 1.2.3 + + assert_eq ${version.major} 1 + assert_eq ${version.minor} 2 + assert_eq ${version.patch} 3 +end +