diff --git a/runTests.sh b/runTests.sh index 18846d9..c0fc3ee 100755 --- a/runTests.sh +++ b/runTests.sh @@ -5,5 +5,7 @@ set -eu SCRIPT_DIR="$(cd -- "$(dirname -- "$0")" && pwd)" cd "$SCRIPT_DIR" +export TYPECHECKING=1 + ${JQ:-jq} -n -r -L tool -L . "include \"testLib\"; testLibMain" ${JQ:-jq} -n -r -L tool -L . "include \"tests\"; testsMain" \ No newline at end of file diff --git a/tool/main.jq b/tool/main.jq index a878f68..9d311b1 100644 --- a/tool/main.jq +++ b/tool/main.jq @@ -3,6 +3,8 @@ include "utils"; include "argsLib"; include "journalUtils"; +import "types" as types; +import "typeLib" as typeLib; def printExperienceStats($stats; $substanceFilter; $consumerFilter; $withTitle): . as $experience | @@ -249,7 +251,8 @@ def main($ARGS): "" ] | join("\n") | halt_error(1); - . as $exportData | + . as $exportData | + $exportData | types::ensureExportData | ($ARGS | parseArgs) as $parsedArgs | diff --git a/tool/tests.jq b/tool/tests.jq index 6cadd79..e351d5c 100644 --- a/tool/tests.jq +++ b/tool/tests.jq @@ -1,8 +1,9 @@ import "./testdata/tests_export" as $exportData; include "journalUtils"; include "testLib"; +import "typeLib" as typeLib; -def testsMain: +def journalUtilsTests: expectPassed(runTest( "invalid input to experienceByTitle"; ( @@ -10,4 +11,9 @@ def testsMain: ); . == "experienceByTitle takes a array of experiences as input"; true - )) | "Tests Passed\n" | halt_error(0); + )); + +def testsMain: + journalUtilsTests | + typeLib::typeLibTests | + "Tests Passed\n" | halt_error(0); diff --git a/tool/typeLib.jq b/tool/typeLib.jq new file mode 100644 index 0000000..dd5e80a --- /dev/null +++ b/tool/typeLib.jq @@ -0,0 +1,56 @@ +include "testLib"; + +def typecheckingEnabled: + if $ENV["TYPECHECKING"] != null and (["1", "true", "on"] | any(index($ENV["TYPECHECKING"]))) then + true + else false end; + +def typecheckingDebug: + if $ENV["TYPECHECKING_DEBUG"] != "" and $ENV["TYPECHECKING_DEBUG"] != null then + true + else false end; + +def typeErrorText: + . as $type | + "Type Error Checking '\($type)'"; +def typeErrorText($type): $type | typeErrorText; + +def typeError($type): + if typecheckingDebug then debug({$type, value: .}) end | + error($type | typeErrorText); + +def ensureNull: if typecheckingEnabled and (. | type != "null") then typeError("null") end; +def ensureBool: if typecheckingEnabled and (. | type) != "boolean" then typeError("bool") end; +def ensureString: if typecheckingEnabled and (. | type) != "string" then typeError("string") end; +def ensureArray: if typecheckingEnabled and (. | type) != "array" then typeError("array") end; +def ensureObject: if typecheckingEnabled and (. | type) != "object" then typeError("object") end; +def ensureNumber: if typecheckingEnabled and (. | type) != "number" then typeError("number") end; + +def ensureNull($value): $value | ensureNull; +def ensureBool($value): $value | ensureBool; +def ensureString($value): $value | ensureString; +def ensureArray($value): $value | ensureArray; +def ensureObject($value): $value | ensureObject; +def ensureNumber($value): $value | ensureNumber; + +def ensureNullOr(ensureType): + if . == null then . else . | ensureType end; + +def ensureKey($type; $key): if (. | has($key) | not) then typeError("\($type):\($key)") end; +def ensureKey($value; $type; $key): $value | ensureKey($type; $key); + +# TYPECHECKING=1 required for tests +def typeLibTests: + expectPassed(runTest( + "null is null"; + (null | ensureNull); + true; + false + )) | + expectPassed(runTest( + "boolean is not null"; + (true | ensureNull); + (. == typeErrorText("null")); + true + )); + diff --git a/tool/types.jq b/tool/types.jq new file mode 100644 index 0000000..6025b95 --- /dev/null +++ b/tool/types.jq @@ -0,0 +1,26 @@ +import "typeLib" as typeLib; + +def ensureExperience: + . as $experience | + typeLib::ensureObject | + + $experience | + typeLib::ensureKey("experience"; "title") | + .title | typeLib::ensureString | + + $experience | + typeLib::ensureKey("experience"; "text") | + .text | typeLib::ensureString + + ; + +def ensureExportData: + . as $exportData | + if typeLib::typecheckingEnabled then + typeLib::ensureObject | + typeLib::ensureKey("exportData"; "customSubstances") | + typeLib::ensureKey("exportData"; "customUnits") | + typeLib::ensureKey("exportData"; "experiences") | + typeLib::ensureKey("exportData"; "substanceCompanions") | + (.experiences[] | ensureExperience) + end; \ No newline at end of file