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}