diff --git a/docs/sdk.md b/docs/sdk.md index 512b642..cf8520e 100644 --- a/docs/sdk.md +++ b/docs/sdk.md @@ -7,6 +7,7 @@ * [std::ReadUserInput (read)](#std__ReadUserInput) * [std::Release (release)](#std__Release) * [std::ShowCommandDocumentation (man)](#std__ShowCommandDocumentation) +* [std::collections](#std__collections) * [std::collections::Array (array)](#std__collections__Array) * [std::collections::ArrayClear (array_clear)](#std__collections__ArrayClear) * [std::collections::ArrayConcat (array_concat)](#std__collections__ArrayConcat) @@ -105,6 +106,7 @@ * [std::fs::TempFile (temp_file)](#std__fs__TempFile) * [std::fs::WriteBytes (writebinfile, write_binary_file)](#std__fs__WriteBytes) * [std::fs::WriteText (writefile, write_text_file)](#std__fs__WriteText) +* [std::json](#std__json) * [std::json::Encode (json_encode)](#std__json__Encode) * [std::json::Parse (json_parse)](#std__json__Parse) * [std::lib::alias::Set (alias)](#std__lib__alias__Set) @@ -156,6 +158,7 @@ * [std::test::AssertFail (assert_fail)](#std__test__AssertFail) * [std::test::AssertFalse (assert_false)](#std__test__AssertFalse) * [std::test::TestDirectory (test_directory)](#std__test__TestDirectory) +* [std::test::TestFile (test_file)](#std__test__TestFile) * [std::thread::Sleep (sleep)](#std__thread__Sleep) * [std::time::CurrentTimeMillies (current_time)](#std__time__CurrentTimeMillies) * [std::var::GetAllVarNames (get_all_var_names)](#std__var__GetAllVarNames) @@ -447,6 +450,21 @@ man set #### Aliases: man + +## std::collections +The collections module contains commands which enable to interact with different data models such as arrays, sets and maps. + +* Arrays are simple ordered list of items +* Sets are unordered unique collection of items +* Maps are key/value (dictionary) structure where the keys are unique + +Access to these data structures are done via handles.
+Handles are provided by the data structure creation command (such as: array, range, map, set) and are used in all +other commands to read/modify those data structures.
+Once done with a specific data structure, you must release it via release command to prevent any memory leaks. + + + ## std::collections::Array ```sh @@ -3889,6 +3907,77 @@ result = writefile ./target/tests/writefile.txt "line 1\nline 2" #### Aliases: writefile, write_text_file + +## std::json +The json module provides json parsing and encoding capabilities.
+When parsing a JSON string, the structure will be represented by simple variables.
+The root object (or simple value) will be set in the json_parse output variable and any sub structure will be +defined as variables with prefix of the root variable name.
+Object nodes, will have the value of: **[OBJECT]**.
+Array nodes will have a length variable defined, for example: **arr.length**
+ +Because duckscript variables have no type, the json_encode will define every boolean/numeric value as JSON string.
+ +Below is a simple example showing how to parse and encode values of all types. + +```sh +fn test_simple_types + str = json_parse \"myvalue\" + assert_eq ${str} myvalue + jsonstring = json_encode str + assert_eq ${jsonstring} \"myvalue\" + + number = json_parse 500 + assert_eq ${number} 500 + jsonstring = json_encode number + # numeric value is encoded as string + assert_eq ${jsonstring} \"500\" + + bool = json_parse true + assert_eq ${bool} true + jsonstring = json_encode bool + # boolean value is encoded to string + assert_eq ${jsonstring} \"true\" + + arr = json_parse "[1, 2, 3]" + # arr.length is not part of the JSON structure but added as a variable to enable + # to loop over the array using the range command + assert_eq ${arr.length} 3 + # direct array location access example + assert_eq ${arr[0]} 1 + assert_eq ${arr[1]} 2 + assert_eq ${arr[2]} 3 + # array loop example + arr_range = range 0 ${arr.length} + for index in ${arr_range} + expected_value = calc ${index} + 1 + value = get_by_name arr[${index}] + assert_eq ${value} ${expected_value} + end + + object = json_parse "{\"str\": \"my string value\", \"number\": 500, \"bool\": true, \"array\": [1, 2, 3]}" + assert_eq ${object} [OBJECT] + assert_eq ${object.str} "my string value" + assert_eq ${object.number} 500 + assert_eq ${object.bool} true + assert_eq ${object.array.length} 3 + assert_eq ${object.array[0]} 1 + assert_eq ${object.array[1]} 2 + assert_eq ${object.array[2]} 3 + jsonstring = json_encode object + found = contains ${jsonstring} "\"str\":\"my string value\"" + assert ${found} + found = contains ${jsonstring} "\"number\":\"500\"" + assert ${found} + found = contains ${jsonstring} "\"bool\":\"true\"" + assert ${found} + found = contains ${jsonstring} "\"array\":[\"1\",\"2\",\"3\"]" + assert ${found} +end +``` + + + ## std::json::Encode ```sh @@ -5749,6 +5838,46 @@ end #### Aliases: test_directory + +## std::test::TestFile +```sh +test_file file [test name] +``` + +This command can be used to run unit tests written in duckscript.
+It will run all test functions that start with **test_** in the given file.
+Each such function is considered as a test and can run any type of code and check itself using assert commands. + +#### Parameters + +* The file name containing the test functions. +* Optional pattern for the test function to limit invocation of only those tests. + +#### Return Value + +**true** if successful. + +#### Examples + +This is an example of a test function: + +```sh +function test_set_get_unset + unset_env TEST_SET_GET_UNSET + value = get_env TEST_SET_GET_UNSET + assert_false ${value} + + value = set_env TEST_SET_GET_UNSET "test value" + assert ${value} + value = get_env TEST_SET_GET_UNSET + assert_eq ${value} "test value" +end +``` + + +#### Aliases: +test_file + ## std::thread::Sleep ```sh diff --git a/duckscript_sdk/src/sdk/std/collections/help.md b/duckscript_sdk/src/sdk/std/collections/help.md new file mode 100644 index 0000000..0e9a3b2 --- /dev/null +++ b/duckscript_sdk/src/sdk/std/collections/help.md @@ -0,0 +1,11 @@ +The collections module contains commands which enable to interact with different data models such as arrays, sets and maps. + +* Arrays are simple ordered list of items +* Sets are unordered unique collection of items +* Maps are key/value (dictionary) structure where the keys are unique + +Access to these data structures are done via handles.
+Handles are provided by the data structure creation command (such as: array, range, map, set) and are used in all +other commands to read/modify those data structures.
+Once done with a specific data structure, you must release it via release command to prevent any memory leaks. + diff --git a/duckscript_sdk/src/sdk/std/collections/mod.rs b/duckscript_sdk/src/sdk/std/collections/mod.rs index 400b4e3..b2d5a7d 100755 --- a/duckscript_sdk/src/sdk/std/collections/mod.rs +++ b/duckscript_sdk/src/sdk/std/collections/mod.rs @@ -38,6 +38,7 @@ mod set_size; mod set_to_array; mod write_properties; +use crate::types::command::create_doc_only_command; use crate::utils::pckg; use duckscript::types::command::Commands; use duckscript::types::error::ScriptError; @@ -47,6 +48,8 @@ static PACKAGE: &str = "collections"; pub(crate) fn load(commands: &mut Commands, parent: &str) -> Result<(), ScriptError> { let package = pckg::concat(parent, PACKAGE); + commands.set(create_doc_only_command(&package, include_str!("help.md")))?; + commands.set(array::create(&package))?; commands.set(array_clear::create(&package))?; commands.set(array_concat::create(&package)?)?; diff --git a/duckscript_sdk/src/sdk/std/json/help.md b/duckscript_sdk/src/sdk/std/json/help.md new file mode 100644 index 0000000..62c24f6 --- /dev/null +++ b/duckscript_sdk/src/sdk/std/json/help.md @@ -0,0 +1,67 @@ +The json module provides json parsing and encoding capabilities.
+When parsing a JSON string, the structure will be represented by simple variables.
+The root object (or simple value) will be set in the json_parse output variable and any sub structure will be +defined as variables with prefix of the root variable name.
+Object nodes, will have the value of: **[OBJECT]**.
+Array nodes will have a length variable defined, for example: **arr.length**
+ +Because duckscript variables have no type, the json_encode will define every boolean/numeric value as JSON string.
+ +Below is a simple example showing how to parse and encode values of all types. + +```sh +fn test_simple_types + str = json_parse \"myvalue\" + assert_eq ${str} myvalue + jsonstring = json_encode str + assert_eq ${jsonstring} \"myvalue\" + + number = json_parse 500 + assert_eq ${number} 500 + jsonstring = json_encode number + # numeric value is encoded as string + assert_eq ${jsonstring} \"500\" + + bool = json_parse true + assert_eq ${bool} true + jsonstring = json_encode bool + # boolean value is encoded to string + assert_eq ${jsonstring} \"true\" + + arr = json_parse "[1, 2, 3]" + # arr.length is not part of the JSON structure but added as a variable to enable + # to loop over the array using the range command + assert_eq ${arr.length} 3 + # direct array location access example + assert_eq ${arr[0]} 1 + assert_eq ${arr[1]} 2 + assert_eq ${arr[2]} 3 + # array loop example + arr_range = range 0 ${arr.length} + for index in ${arr_range} + expected_value = calc ${index} + 1 + value = get_by_name arr[${index}] + assert_eq ${value} ${expected_value} + end + + object = json_parse "{\"str\": \"my string value\", \"number\": 500, \"bool\": true, \"array\": [1, 2, 3]}" + assert_eq ${object} [OBJECT] + assert_eq ${object.str} "my string value" + assert_eq ${object.number} 500 + assert_eq ${object.bool} true + assert_eq ${object.array.length} 3 + assert_eq ${object.array[0]} 1 + assert_eq ${object.array[1]} 2 + assert_eq ${object.array[2]} 3 + jsonstring = json_encode object + found = contains ${jsonstring} "\"str\":\"my string value\"" + assert ${found} + found = contains ${jsonstring} "\"number\":\"500\"" + assert ${found} + found = contains ${jsonstring} "\"bool\":\"true\"" + assert ${found} + found = contains ${jsonstring} "\"array\":[\"1\",\"2\",\"3\"]" + assert ${found} +end +``` + diff --git a/duckscript_sdk/src/sdk/std/json/mod.rs b/duckscript_sdk/src/sdk/std/json/mod.rs index addf1eb..05f6738 100644 --- a/duckscript_sdk/src/sdk/std/json/mod.rs +++ b/duckscript_sdk/src/sdk/std/json/mod.rs @@ -1,6 +1,7 @@ mod encode; mod parse; +use crate::types::command::create_doc_only_command; use crate::utils::pckg; use duckscript::types::command::Commands; use duckscript::types::error::ScriptError; @@ -12,6 +13,8 @@ pub(crate) static OBJECT_VALUE: &str = "[OBJECT]"; pub(crate) fn load(commands: &mut Commands, parent: &str) -> Result<(), ScriptError> { let package = pckg::concat(parent, PACKAGE); + commands.set(create_doc_only_command(&package, include_str!("help.md")))?; + commands.set(encode::create(&package))?; commands.set(parse::create(&package))?; diff --git a/duckscript_sdk/src/types/command.rs b/duckscript_sdk/src/types/command.rs index 67822ba..ab205fb 100644 --- a/duckscript_sdk/src/types/command.rs +++ b/duckscript_sdk/src/types/command.rs @@ -154,6 +154,30 @@ impl Command for AliasCommand { } } +#[derive(Clone)] +struct DocOnlyCommand { + name: String, + help: String, +} + +impl Command for DocOnlyCommand { + fn name(&self) -> String { + self.name.clone() + } + + fn help(&self) -> String { + self.help.clone() + } + + fn clone_and_box(&self) -> Box { + Box::new((*self).clone()) + } + + fn run(&self, _arguments: Vec) -> CommandResult { + CommandResult::Error("Documentation only commands should not be executed.".to_string()) + } +} + pub(crate) fn create_alias_command( name: String, aliases: Vec, @@ -175,3 +199,10 @@ pub(crate) fn create_alias_command( Ok(command) } + +pub(crate) fn create_doc_only_command(name: &str, help: &str) -> Box { + Box::new(DocOnlyCommand { + name: name.to_string(), + help: help.to_string(), + }) +} diff --git a/test/std/json/json_encode_test.ds b/test/std/json/json_encode_test.ds index cfb7eb1..11c32fb 100644 --- a/test/std/json/json_encode_test.ds +++ b/test/std/json/json_encode_test.ds @@ -1,4 +1,49 @@ +fn test_simple_types + str = json_parse \"myvalue\" + assert_eq ${str} myvalue + jsonstring = json_encode str + assert_eq ${jsonstring} \"myvalue\" + + number = json_parse 500 + assert_eq ${number} 500 + jsonstring = json_encode number + assert_eq ${jsonstring} \"500\" + + bool = json_parse true + assert_eq ${bool} true + jsonstring = json_encode bool + assert_eq ${jsonstring} \"true\" + + arr = json_parse "[1, 2, 3]" + assert_eq ${arr.length} 3 + arr_range = range 0 ${arr.length} + for index in ${arr_range} + expected_value = calc ${index} + 1 + value = get_by_name arr[${index}] + assert_eq ${value} ${expected_value} + end + + object = json_parse "{\"str\": \"my string value\", \"number\": 500, \"bool\": true, \"array\": [1, 2, 3]}" + assert_eq ${object} [OBJECT] + assert_eq ${object.str} "my string value" + assert_eq ${object.number} 500 + assert_eq ${object.bool} true + assert_eq ${object.array.length} 3 + assert_eq ${object.array[0]} 1 + assert_eq ${object.array[1]} 2 + assert_eq ${object.array[2]} 3 + jsonstring = json_encode object + found = contains ${jsonstring} "\"str\":\"my string value\"" + assert ${found} + found = contains ${jsonstring} "\"number\":\"500\"" + assert ${found} + found = contains ${jsonstring} "\"bool\":\"true\"" + assert ${found} + found = contains ${jsonstring} "\"array\":[\"1\",\"2\",\"3\"]" + assert ${found} +end + fn test_all_types jsonstring = set "{\"name\": \"my package\", \"version\": 1, \"publish\": false, \"keywords\": [\"test1\", \"test2\"], \"directories\": {\"test\": \"spec\"}}" package = json_parse ${jsonstring}