Merge branch 'dust_helpers' into render
This commit is contained in:
commit
974ce96f14
@ -10,7 +10,7 @@ RUN addgroup -S duster && adduser -S duster -G duster
|
|||||||
# Install LinkedIn DustJS. Installing it globally before copying in
|
# Install LinkedIn DustJS. Installing it globally before copying in
|
||||||
# the repo to avoid spamming the npm servers with every codebase
|
# the repo to avoid spamming the npm servers with every codebase
|
||||||
# change.
|
# change.
|
||||||
RUN npm install -g dustjs-linkedin
|
RUN npm install -g dustjs-linkedin dustjs-helpers
|
||||||
|
|
||||||
|
|
||||||
# Copy repo into duster user's directory
|
# Copy repo into duster user's directory
|
||||||
@ -22,5 +22,5 @@ RUN chown -R duster:duster /home/duster/duster
|
|||||||
USER duster
|
USER duster
|
||||||
RUN git clean -dfxq
|
RUN git clean -dfxq
|
||||||
RUN cargo build
|
RUN cargo build
|
||||||
RUN npm link dustjs-linkedin
|
RUN npm link dustjs-linkedin dustjs-helpers
|
||||||
ENTRYPOINT ["/home/duster/duster/js/run_compliance_suite.bash"]
|
ENTRYPOINT ["/home/duster/duster/js/run_compliance_suite.bash"]
|
||||||
|
@ -1,20 +1,21 @@
|
|||||||
var dust = require('dustjs-linkedin');
|
// var dust = require("dustjs-linkedin");
|
||||||
var fs = require('fs');
|
var dust = require("dustjs-helpers");
|
||||||
const path = require('path');
|
var fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
var argv = process.argv.slice(2);
|
var argv = process.argv.slice(2);
|
||||||
if (argv.length < 1) {
|
if (argv.length < 1) {
|
||||||
console.error("Expecting only 1 argument (a path to a template)");
|
console.error("Expecting only 1 argument (a path to a template)");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
var context = JSON.parse(fs.readFileSync(0, 'utf-8'));
|
var context = JSON.parse(fs.readFileSync(0, "utf-8"));
|
||||||
var main_template = path.parse(argv[0])["name"];
|
var main_template = path.parse(argv[0])["name"];
|
||||||
|
|
||||||
for (var i = 0, len = argv.length; i < len; ++i) {
|
for (var i = 0, len = argv.length; i < len; ++i) {
|
||||||
var filename = path.parse(argv[i])["name"];
|
var filename = path.parse(argv[i])["name"];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var template_source = fs.readFileSync(argv[i], 'utf-8');
|
var template_source = fs.readFileSync(argv[i], "utf-8");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
@ -38,7 +38,7 @@ while read -r test_group; do
|
|||||||
else
|
else
|
||||||
echo "$test_group_name::$test_case_name FAILED"
|
echo "$test_group_name::$test_case_name FAILED"
|
||||||
if [ $show_diff -eq 1 ]; then
|
if [ $show_diff -eq 1 ]; then
|
||||||
diff --label "dustjs-linked" --label "duster" <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) node "$DIR/dustjs_shim.js" < "$test_case" 2>/dev/null ) <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) "$DIR/../target/debug/duster-cli" < "$test_case" 2>/dev/null )
|
diff --label "dustjs-linkedin" --label "duster" <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) node "$DIR/dustjs_shim.js" < "$test_case" 2>/dev/null ) <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) "$DIR/../target/debug/duster-cli" < "$test_case" 2>/dev/null )
|
||||||
fi
|
fi
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@ -48,7 +48,12 @@ while read -r test_group; do
|
|||||||
fi
|
fi
|
||||||
set -e
|
set -e
|
||||||
done <<<"$(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.json' | sort)"
|
done <<<"$(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.json' | sort)"
|
||||||
done <<<"$(find "$DIR/test_cases" -maxdepth 1 -mindepth 1 -type d | sort)"
|
done <<<"$(find "$DIR/test_cases" -maxdepth 1 -mindepth 1 -type d ! -name '_*' | sort)"
|
||||||
|
|
||||||
|
ignored_count=$(find "$DIR/test_cases" -maxdepth 1 -mindepth 1 -type d -name '_*' | wc -l)
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "$ignored_count ignored tests"
|
||||||
|
|
||||||
if [ $failed_count -ne 0 ]; then
|
if [ $failed_count -ne 0 ]; then
|
||||||
echo "$failed_count failed tests"
|
echo "$failed_count failed tests"
|
||||||
|
15
js/test_cases/helpers_eq/README.md
Normal file
15
js/test_cases/helpers_eq/README.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
Without a key parameter, neither the main block nor the else block is rendered.
|
||||||
|
|
||||||
|
Literal values work in both keys and values.
|
||||||
|
|
||||||
|
Can't Walk Theory
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Assuming a missing value = cantwalk and a non-existent key = cantwalk then their equality makes sense.
|
||||||
|
|
||||||
|
The null tests have proven that absent parameters and missing values do not equal null and therefore it is not just making all falsey values equal.
|
||||||
|
|
||||||
|
Non-Scalar Values
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
For non-scalar values, it seems that dust does mark two values as true if they have the same path, but otherwise they are not equal.
|
60
js/test_cases/helpers_eq/input1.json
Normal file
60
js/test_cases/helpers_eq/input1.json
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"str": "master",
|
||||||
|
"int": 7,
|
||||||
|
"alpha": 21,
|
||||||
|
"beta": "21",
|
||||||
|
"null": null,
|
||||||
|
"true_value": true,
|
||||||
|
"false_value": false,
|
||||||
|
"array_lower": [
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
7
|
||||||
|
],
|
||||||
|
"copy_array_lower": [
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
7
|
||||||
|
],
|
||||||
|
"array_higher": [
|
||||||
|
8,
|
||||||
|
9
|
||||||
|
],
|
||||||
|
"some_obj": {
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
"copy_some_obj": {
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
"other_obj": {
|
||||||
|
"name": "dog"
|
||||||
|
},
|
||||||
|
"array_of_some_obj": [
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"copy_array_of_some_obj": [
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"array_of_other_obj": [
|
||||||
|
{
|
||||||
|
"name": "dog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dog"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"array_of_strings": [
|
||||||
|
"cat",
|
||||||
|
"dog"
|
||||||
|
]
|
||||||
|
}
|
28
js/test_cases/helpers_eq/main.dust
Normal file
28
js/test_cases/helpers_eq/main.dust
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
Testing helpers:{~n}
|
||||||
|
str is {str}{~n}
|
||||||
|
int is {int}{~n}
|
||||||
|
alpha is {alpha}{~n}
|
||||||
|
beta is {beta}{~n}
|
||||||
|
{@eq key=str value="master"}str is equal to "master"{:else}str does not equal "master"{/eq}{~n}
|
||||||
|
{@eq key=str value="7"}str is equal to "7"{:else}str does not equal "7"{/eq}{~n}
|
||||||
|
{@eq key=int value="7"}int is equal to "7"{:else}int does not equal "7"{/eq}{~n}
|
||||||
|
{@eq key=int value=7}int is equal to 7{:else}int does not equal 7{/eq}{~n}
|
||||||
|
{@eq key=alpha value=beta}alpha is equal to beta{:else}alpha does not equal beta{/eq}{~n}
|
||||||
|
{@eq value=beta}missing key is true{:else}missing key is false{/eq}{~n}
|
||||||
|
{@eq value=gamma}missing key and non-existent value is true{:else}missing key and non-existent value is false{/eq}{~n}
|
||||||
|
{@eq key=alpha}missing value is true{:else}missing value is false{/eq}{~n}
|
||||||
|
{@eq key=gamma}missing value and non-existent key is true{:else}missing value and non-existent key is false{/eq}{~n}
|
||||||
|
{@eq key="master" value="master"}"master" is equal to "master"{:else}"master" does not equal "master"{/eq}{~n}
|
||||||
|
{@eq key=null}null equals a missing value{:else}null does not equal a missing value{/eq}{~n}
|
||||||
|
{@eq key=null value=gamma}null equals a non-existent value{:else}null does not equal a non-existent value{/eq}{~n}
|
||||||
|
{@eq}no parameters is true{:else}no parameters is false{/eq}{~n}
|
||||||
|
{@eq key=array_lower value=array_higher}[3,5,7] is equal to [8,9]{:else}[3,5,7] does not equal [8,9]{/eq}{~n}
|
||||||
|
{! non-scalar and copied value tests !}
|
||||||
|
{@eq key=array_lower value=array_lower}array_lower is equal to array_lower{:else}array_lower does not equal array_lower{/eq}{~n}
|
||||||
|
{@eq key=array_lower value=copy_array_lower}array_lower is equal to copy_array_lower{:else}array_lower does not equal copy_array_lower{/eq}{~n}
|
||||||
|
{@eq key=some_obj value=some_obj}some_obj is equal to some_obj{:else}some_obj does not equal some_obj{/eq}{~n}
|
||||||
|
{@eq key=some_obj value=copy_some_obj}some_obj is equal to copy_some_obj{:else}some_obj does not equal copy_some_obj{/eq}{~n}
|
||||||
|
{@eq key=some_obj value=other_obj}some_obj is equal to other_obj{:else}some_obj does not equal other_obj{/eq}{~n}
|
||||||
|
{@eq key=array_of_some_obj value=array_of_some_obj}array_of_some_obj is equal to array_of_some_obj{:else}array_of_some_obj does not equal array_of_some_obj{/eq}{~n}
|
||||||
|
{@eq key=array_of_some_obj value=copy_array_of_some_obj}array_of_some_obj is equal to copy_array_of_some_obj{:else}array_of_some_obj does not equal copy_array_of_some_obj{/eq}{~n}
|
||||||
|
{@eq key=array_of_some_obj value=array_of_other_obj}array_of_some_obj is equal to array_of_other_obj{:else}array_of_some_obj does not equal array_of_other_obj{/eq}{~n}
|
37
js/test_cases/helpers_gt/README.md
Normal file
37
js/test_cases/helpers_gt/README.md
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
Based on my tests, it appears dust is sorting based on ascii-table values. This also appears to extend to unicode codepoints based on a symbols test.
|
||||||
|
|
||||||
|
Greater than follows the same pattern for not rendering when key is omitted or null.
|
||||||
|
|
||||||
|
Longer arrays are greater than shorter arrays if all preceding values match.
|
||||||
|
|
||||||
|
Theory for comparing arrays: Compare the values, if theres any mismatched types then take the same action you would for non-matching scalar types.
|
||||||
|
|
||||||
|
greater than
|
||||||
|
------------
|
||||||
|
|
||||||
|
All comparisons between non-matching types (for example, int vs string) appear to render the else block.
|
||||||
|
|
||||||
|
Comparisons between non-scalar types (like arrays) appears to render the else block
|
||||||
|
|
||||||
|
|
||||||
|
greater than or equals to
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
All comparisons between non-matching types (for example, int vs string) appear to render the main block.
|
||||||
|
|
||||||
|
Comparisons between non-scalar types (like arrays) appears to render the main block
|
||||||
|
|
||||||
|
less than
|
||||||
|
---------
|
||||||
|
|
||||||
|
All comparisons between non-matching types (for example, int vs string) appear to render the else block.
|
||||||
|
|
||||||
|
Comparisons between non-scalar types (like arrays) appears to render the else block
|
||||||
|
|
||||||
|
|
||||||
|
less than or equal to
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
All comparisons between non-matching types (for example, int vs string) appear to render the main block.
|
||||||
|
|
||||||
|
Comparisons between non-scalar types (like arrays) appears to render the main block
|
72
js/test_cases/helpers_gt/input1.json
Normal file
72
js/test_cases/helpers_gt/input1.json
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"str": "master",
|
||||||
|
"int": 7,
|
||||||
|
"alpha": 21,
|
||||||
|
"beta": "21",
|
||||||
|
"null": null,
|
||||||
|
"true_value": true,
|
||||||
|
"false_value": false,
|
||||||
|
"array_lower": [
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
7
|
||||||
|
],
|
||||||
|
"array_lower_with_array": [
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
[
|
||||||
|
7
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"copy_array_lower": [
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
7
|
||||||
|
],
|
||||||
|
"array_higher": [
|
||||||
|
8,
|
||||||
|
9
|
||||||
|
],
|
||||||
|
"array_higher_longer": [
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"some_obj": {
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
"copy_some_obj": {
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
"other_obj": {
|
||||||
|
"name": "dog"
|
||||||
|
},
|
||||||
|
"array_of_some_obj": [
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"copy_array_of_some_obj": [
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"array_of_other_obj": [
|
||||||
|
{
|
||||||
|
"name": "dog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dog"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"array_of_strings": [
|
||||||
|
"cat",
|
||||||
|
"dog"
|
||||||
|
]
|
||||||
|
}
|
42
js/test_cases/helpers_gt/main.dust
Normal file
42
js/test_cases/helpers_gt/main.dust
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
Testing helpers:{~n}
|
||||||
|
str is {str}{~n}
|
||||||
|
int is {int}{~n}
|
||||||
|
alpha is {alpha}{~n}
|
||||||
|
beta is {beta}{~n}
|
||||||
|
{@gt key=str value="master"}str is greater than "master"{:else}str is less than or equal to "master"{/gt}{~n}
|
||||||
|
{@gt key=str value="7"}str is greater than "7"{:else}str is less than or equal to "7"{/gt}{~n}
|
||||||
|
{@gt key=int value="7"}int is greater than "7"{:else}int is less than or equal to "7"{/gt}{~n}
|
||||||
|
{@gt key=int value=7}int is greater than 7{:else}int is less than or equal to 7{/gt}{~n}
|
||||||
|
{@gt key=int value=6}int is greater than 6{:else}int is less than or equal to 6{/gt}{~n}
|
||||||
|
{@gt key=alpha value=beta}alpha is greater than beta{:else}alpha is less than or equal to beta{/gt}{~n}
|
||||||
|
{@gt value=beta}missing key is true{:else}missing key is false{/gt}{~n}
|
||||||
|
{@gt value=gamma}missing key and non-existent value is true{:else}missing key and non-existent value is false{/gt}{~n}
|
||||||
|
{@gt key=alpha}missing value is true{:else}missing value is false{/gt}{~n}
|
||||||
|
{@gt key=gamma}missing value and non-existent key is true{:else}missing value and non-existent key is false{/gt}{~n}
|
||||||
|
{@gt key="master" value="master"}"master" is greater than "master"{:else}"master" is less than or equal to "master"{/gt}{~n}
|
||||||
|
{@gt key=null}null is greater than a missing value{:else}null is less than or equal to a missing value{/gt}{~n}
|
||||||
|
{@gt key=null value=gamma}null is greater than a non-existent value{:else}null is less than or equal to a non-existent value{/gt}{~n}
|
||||||
|
{@gt}no parameters is true{:else}no parameters is false{/gt}{~n}
|
||||||
|
{@gt key="a" value="]"}"a" is greater than "]"{:else}"a" is less than or equal to "]"{/gt}{~n}
|
||||||
|
{@gt key="a" value="A"}"a" is greater than "A"{:else}"a" is less than or equal to "A"{/gt}{~n}
|
||||||
|
{@gt key="a" value="}"}"a" is greater than "}"{:else}"a" is less than or equal to "}"{/gt}{~n}
|
||||||
|
{!
|
||||||
|
Commented out because unicode breaks nom
|
||||||
|
{@gt key="☃" value="☄"}"☃" is greater than "☄"{:else}"☃" is less than or equal to "☄"{/gt}{~n}
|
||||||
|
!}
|
||||||
|
{@gt key=true_value value=false_value}true is greater than false{:else}true is less than or equal to false{/gt}{~n}
|
||||||
|
{@gt key=array_lower value=array_higher}[3,5,7] is greater than [8,9]{:else}[3,5,7] is less than or equal to [8,9]{/gt}{~n}
|
||||||
|
{@gt key=array_higher value=array_lower}[8,9] is greater than [3,5,7]{:else}[8,9] is less than or equal to [3,5,7]{/gt}{~n}
|
||||||
|
{! non-scalar and copied value tests !}
|
||||||
|
{@gt key=array_lower value=array_lower}array_lower is greater than array_lower{:else}array_lower is less than or equal to array_lower{/gt}{~n}
|
||||||
|
{@gt key=array_lower value=copy_array_lower}array_lower is greater than copy_array_lower{:else}array_lower is less than or equal to copy_array_lower{/gt}{~n}
|
||||||
|
{@gt key=some_obj value=some_obj}some_obj is greater than some_obj{:else}some_obj is less than or equal to some_obj{/gt}{~n}
|
||||||
|
{@gt key=some_obj value=copy_some_obj}some_obj is greater than copy_some_obj{:else}some_obj is less than or equal to copy_some_obj{/gt}{~n}
|
||||||
|
{@gt key=some_obj value=other_obj}some_obj is greater than other_obj{:else}some_obj is less than or equal to other_obj{/gt}{~n}
|
||||||
|
{@gt key=array_of_some_obj value=array_of_some_obj}array_of_some_obj is greater than array_of_some_obj{:else}array_of_some_obj is less than or equal to array_of_some_obj{/gt}{~n}
|
||||||
|
{@gt key=array_of_some_obj value=copy_array_of_some_obj}array_of_some_obj is greater than copy_array_of_some_obj{:else}array_of_some_obj is less than or equal to copy_array_of_some_obj{/gt}{~n}
|
||||||
|
{@gt key=array_of_some_obj value=array_of_other_obj}array_of_some_obj is greater than array_of_other_obj{:else}array_of_some_obj is less than or equal to array_of_other_obj{/gt}{~n}
|
||||||
|
|
||||||
|
{@gt key=array_higher value=array_higher_longer}array_higher is greater than array_higher_longer{:else}array_higher is less than or equal to array_higher_longer{/gt}{~n}
|
||||||
|
{@gt key=array_lower value=array_lower_with_array}array_lower is greater than array_lower_with_array{:else}array_lower is less than or equal to array_lower_with_array{/gt}{~n}
|
||||||
|
{@gt key=array_lower_with_array value=array_lower}array_lower_with_array is greater than array_lower{:else}array_lower_with_array is less than or equal to array_lower{/gt}{~n}
|
72
js/test_cases/helpers_gte/input1.json
Normal file
72
js/test_cases/helpers_gte/input1.json
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"str": "master",
|
||||||
|
"int": 7,
|
||||||
|
"alpha": 21,
|
||||||
|
"beta": "21",
|
||||||
|
"null": null,
|
||||||
|
"true_value": true,
|
||||||
|
"false_value": false,
|
||||||
|
"array_lower": [
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
7
|
||||||
|
],
|
||||||
|
"array_lower_with_array": [
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
[
|
||||||
|
7
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"copy_array_lower": [
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
7
|
||||||
|
],
|
||||||
|
"array_higher": [
|
||||||
|
8,
|
||||||
|
9
|
||||||
|
],
|
||||||
|
"array_higher_longer": [
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"some_obj": {
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
"copy_some_obj": {
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
"other_obj": {
|
||||||
|
"name": "dog"
|
||||||
|
},
|
||||||
|
"array_of_some_obj": [
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"copy_array_of_some_obj": [
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"array_of_other_obj": [
|
||||||
|
{
|
||||||
|
"name": "dog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dog"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"array_of_strings": [
|
||||||
|
"cat",
|
||||||
|
"dog"
|
||||||
|
]
|
||||||
|
}
|
41
js/test_cases/helpers_gte/main.dust
Normal file
41
js/test_cases/helpers_gte/main.dust
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
Testing helpers:{~n}
|
||||||
|
str is {str}{~n}
|
||||||
|
int is {int}{~n}
|
||||||
|
alpha is {alpha}{~n}
|
||||||
|
beta is {beta}{~n}
|
||||||
|
{@gte key=str value="master"}str is greater than or equal to "master"{:else}str is less than "master"{/gte}{~n}
|
||||||
|
{@gte key=str value="7"}str is greater than or equal to "7"{:else}str is less than "7"{/gte}{~n}
|
||||||
|
{@gte key=int value="7"}int is greater than or equal to "7"{:else}int is less than "7"{/gte}{~n}
|
||||||
|
{@gte key=int value=7}int is greater than or equal to 7{:else}int is less than 7{/gte}{~n}
|
||||||
|
{@gte key=int value=6}int is greater than or equal to 6{:else}int is less than 6{/gte}{~n}
|
||||||
|
{@gte key=alpha value=beta}alpha is greater than or equal to beta{:else}alpha is less than beta{/gte}{~n}
|
||||||
|
{@gte value=beta}missing key is true{:else}missing key is false{/gte}{~n}
|
||||||
|
{@gte value=gamma}missing key and non-existent value is true{:else}missing key and non-existent value is false{/gte}{~n}
|
||||||
|
{@gte key=alpha}missing value is true{:else}missing value is false{/gte}{~n}
|
||||||
|
{@gte key=gamma}missing value and non-existent key is true{:else}missing value and non-existent key is false{/gte}{~n}
|
||||||
|
{@gte key="master" value="master"}"master" is greater than or equal to "master"{:else}"master" is less than "master"{/gte}{~n}
|
||||||
|
{@gte key=null}null is greater than or equal to a missing value{:else}null is less than a missing value{/gte}{~n}
|
||||||
|
{@gte key=null value=gamma}null is greater than or equal to a non-existent value{:else}null is less than a non-existent value{/gte}{~n}
|
||||||
|
{@gte}no parameters is true{:else}no parameters is false{/gte}{~n}
|
||||||
|
{@gte key="a" value="]"}"a" is greater than or equal to "]"{:else}"a" is less than "]"{/gte}{~n}
|
||||||
|
{@gte key="a" value="A"}"a" is greater than or equal to "A"{:else}"a" is less than "A"{/gte}{~n}
|
||||||
|
{@gte key="a" value="}"}"a" is greater than or equal to "}"{:else}"a" is less than "}"{/gte}{~n}
|
||||||
|
{!
|
||||||
|
Commented out because unicode breaks nom
|
||||||
|
{@gte key="☃" value="☄"}"☃" is greater than or equal to "☄"{:else}"☃" is less than "☄"{/gte}{~n}
|
||||||
|
!}
|
||||||
|
{@gte key=true_value value=false_value}true is greater than or equal to false{:else}true is less than false{/gte}{~n}
|
||||||
|
{@gte key=array_lower value=array_higher}[3,5,7] is greater than or equal to [8,9]{:else}[3,5,7] is less than [8,9]{/gte}{~n}
|
||||||
|
{@gte key=array_higher value=array_lower}[8,9] is greater than or equal to [3,5,7]{:else}[8,9] is less than [3,5,7]{/gte}{~n}
|
||||||
|
{! non-scalar and copied value tests !}
|
||||||
|
{@gte key=array_lower value=array_lower}array_lower is greater than or equal to array_lower{:else}array_lower is less than array_lower{/gte}{~n}
|
||||||
|
{@gte key=array_lower value=copy_array_lower}array_lower is greater than or equal to copy_array_lower{:else}array_lower is less than copy_array_lower{/gte}{~n}
|
||||||
|
{@gte key=some_obj value=some_obj}some_obj is greater than or equal to some_obj{:else}some_obj is less than some_obj{/gte}{~n}
|
||||||
|
{@gte key=some_obj value=copy_some_obj}some_obj is greater than or equal to copy_some_obj{:else}some_obj is less than copy_some_obj{/gte}{~n}
|
||||||
|
{@gte key=some_obj value=other_obj}some_obj is greater than or equal to other_obj{:else}some_obj is less than other_obj{/gte}{~n}
|
||||||
|
{@gte key=array_of_some_obj value=array_of_some_obj}array_of_some_obj is greater than or equal to array_of_some_obj{:else}array_of_some_obj is less than array_of_some_obj{/gte}{~n}
|
||||||
|
{@gte key=array_of_some_obj value=copy_array_of_some_obj}array_of_some_obj is greater than or equal to copy_array_of_some_obj{:else}array_of_some_obj is less than copy_array_of_some_obj{/gte}{~n}
|
||||||
|
{@gte key=array_of_some_obj value=array_of_other_obj}array_of_some_obj is greater than or equal to array_of_other_obj{:else}array_of_some_obj is less than array_of_other_obj{/gte}{~n}
|
||||||
|
{@gte key=array_higher value=array_higher_longer}array_higher is greater than or equal to array_higher_longer{:else}array_higher is less than array_higher_longer{/gte}{~n}
|
||||||
|
{@gte key=array_lower value=array_lower_with_array}array_lower is greater than or equal to array_lower_with_array{:else}array_lower is less than array_lower_with_array{/gte}{~n}
|
||||||
|
{@gte key=array_lower_with_array value=array_lower}array_lower_with_array is greater than or equal to array_lower{:else}array_lower_with_array is less than array_lower{/gte}{~n}
|
72
js/test_cases/helpers_lt/input1.json
Normal file
72
js/test_cases/helpers_lt/input1.json
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"str": "master",
|
||||||
|
"int": 7,
|
||||||
|
"alpha": 21,
|
||||||
|
"beta": "21",
|
||||||
|
"null": null,
|
||||||
|
"true_value": true,
|
||||||
|
"false_value": false,
|
||||||
|
"array_lower": [
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
7
|
||||||
|
],
|
||||||
|
"array_lower_with_array": [
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
[
|
||||||
|
7
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"copy_array_lower": [
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
7
|
||||||
|
],
|
||||||
|
"array_higher": [
|
||||||
|
8,
|
||||||
|
9
|
||||||
|
],
|
||||||
|
"array_higher_longer": [
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"some_obj": {
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
"copy_some_obj": {
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
"other_obj": {
|
||||||
|
"name": "dog"
|
||||||
|
},
|
||||||
|
"array_of_some_obj": [
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"copy_array_of_some_obj": [
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"array_of_other_obj": [
|
||||||
|
{
|
||||||
|
"name": "dog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dog"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"array_of_strings": [
|
||||||
|
"cat",
|
||||||
|
"dog"
|
||||||
|
]
|
||||||
|
}
|
41
js/test_cases/helpers_lt/main.dust
Normal file
41
js/test_cases/helpers_lt/main.dust
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
Testing helpers:{~n}
|
||||||
|
str is {str}{~n}
|
||||||
|
int is {int}{~n}
|
||||||
|
alpha is {alpha}{~n}
|
||||||
|
beta is {beta}{~n}
|
||||||
|
{@lt key=str value="master"}str is less than "master"{:else}str is greater than or equal to "master"{/lt}{~n}
|
||||||
|
{@lt key=str value="7"}str is less than "7"{:else}str is greater than or equal to "7"{/lt}{~n}
|
||||||
|
{@lt key=int value="7"}int is less than "7"{:else}int is greater than or equal to "7"{/lt}{~n}
|
||||||
|
{@lt key=int value=7}int is less than 7{:else}int is greater than or equal to 7{/lt}{~n}
|
||||||
|
{@lt key=int value=6}int is less than 6{:else}int is greater than or equal to 6{/lt}{~n}
|
||||||
|
{@lt key=alpha value=beta}alpha is less than beta{:else}alpha is greater than or equal to beta{/lt}{~n}
|
||||||
|
{@lt value=beta}missing key is true{:else}missing key is false{/lt}{~n}
|
||||||
|
{@lt value=gamma}missing key and non-existent value is true{:else}missing key and non-existent value is false{/lt}{~n}
|
||||||
|
{@lt key=alpha}missing value is true{:else}missing value is false{/lt}{~n}
|
||||||
|
{@lt key=gamma}missing value and non-existent key is true{:else}missing value and non-existent key is false{/lt}{~n}
|
||||||
|
{@lt key="master" value="master"}"master" is less than "master"{:else}"master" is greater than or equal to "master"{/lt}{~n}
|
||||||
|
{@lt key=null}null is less than a missing value{:else}null is greater than or equal to a missing value{/lt}{~n}
|
||||||
|
{@lt key=null value=gamma}null is less than a non-existent value{:else}null is greater than or equal to a non-existent value{/lt}{~n}
|
||||||
|
{@lt}no parameters is true{:else}no parameters is false{/lt}{~n}
|
||||||
|
{@lt key="a" value="]"}"a" is less than "]"{:else}"a" is greater than or equal to "]"{/lt}{~n}
|
||||||
|
{@lt key="a" value="A"}"a" is less than "A"{:else}"a" is greater than or equal to "A"{/lt}{~n}
|
||||||
|
{@lt key="a" value="}"}"a" is less than "}"{:else}"a" is greater than or equal to "}"{/lt}{~n}
|
||||||
|
{!
|
||||||
|
Commented out because unicode breaks nom
|
||||||
|
{@lt key="☃" value="☄"}"☃" is less than "☄"{:else}"☃" is greater than or equal to "☄"{/lt}{~n}
|
||||||
|
!}
|
||||||
|
{@lt key=true_value value=false_value}true is less than false{:else}true is greater than or equal to false{/lt}{~n}
|
||||||
|
{@lt key=array_lower value=array_higher}[3,5,7] is less than [8,9]{:else}[3,5,7] is greater than or equal to [8,9]{/lt}{~n}
|
||||||
|
{@lt key=array_higher value=array_lower}[8,9] is less than [3,5,7]{:else}[8,9] is greater than or equal to [3,5,7]{/lt}{~n}
|
||||||
|
{! non-scalar and copied value tests !}
|
||||||
|
{@lt key=array_lower value=array_lower}array_lower is less than array_lower{:else}array_lower is greater than or equal to array_lower{/lt}{~n}
|
||||||
|
{@lt key=array_lower value=copy_array_lower}array_lower is less than copy_array_lower{:else}array_lower is greater than or equal to copy_array_lower{/lt}{~n}
|
||||||
|
{@lt key=some_obj value=some_obj}some_obj is less than some_obj{:else}some_obj is greater than or equal to some_obj{/lt}{~n}
|
||||||
|
{@lt key=some_obj value=copy_some_obj}some_obj is less than copy_some_obj{:else}some_obj is greater than or equal to copy_some_obj{/lt}{~n}
|
||||||
|
{@lt key=some_obj value=other_obj}some_obj is less than other_obj{:else}some_obj is greater than or equal to other_obj{/lt}{~n}
|
||||||
|
{@lt key=array_of_some_obj value=array_of_some_obj}array_of_some_obj is less than array_of_some_obj{:else}array_of_some_obj is greater than or equal to array_of_some_obj{/lt}{~n}
|
||||||
|
{@lt key=array_of_some_obj value=copy_array_of_some_obj}array_of_some_obj is less than copy_array_of_some_obj{:else}array_of_some_obj is greater than or equal to copy_array_of_some_obj{/lt}{~n}
|
||||||
|
{@lt key=array_of_some_obj value=array_of_other_obj}array_of_some_obj is less than array_of_other_obj{:else}array_of_some_obj is greater than or equal to array_of_other_obj{/lt}{~n}
|
||||||
|
{@lt key=array_higher value=array_higher_longer}array_higher is less than array_higher_longer{:else}array_higher is greater than or equal to array_higher_longer{/lt}{~n}
|
||||||
|
{@lt key=array_lower value=array_lower_with_array}array_lower is less than array_lower_with_array{:else}array_lower is greater than or equal to array_lower_with_array{/lt}{~n}
|
||||||
|
{@lt key=array_lower_with_array value=array_lower}array_lower_with_array is less than array_lower{:else}array_lower_with_array is greater than or equal to array_lower{/lt}{~n}
|
72
js/test_cases/helpers_lte/input1.json
Normal file
72
js/test_cases/helpers_lte/input1.json
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"str": "master",
|
||||||
|
"int": 7,
|
||||||
|
"alpha": 21,
|
||||||
|
"beta": "21",
|
||||||
|
"null": null,
|
||||||
|
"true_value": true,
|
||||||
|
"false_value": false,
|
||||||
|
"array_lower": [
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
7
|
||||||
|
],
|
||||||
|
"array_lower_with_array": [
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
[
|
||||||
|
7
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"copy_array_lower": [
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
7
|
||||||
|
],
|
||||||
|
"array_higher": [
|
||||||
|
8,
|
||||||
|
9
|
||||||
|
],
|
||||||
|
"array_higher_longer": [
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"some_obj": {
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
"copy_some_obj": {
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
"other_obj": {
|
||||||
|
"name": "dog"
|
||||||
|
},
|
||||||
|
"array_of_some_obj": [
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"copy_array_of_some_obj": [
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"array_of_other_obj": [
|
||||||
|
{
|
||||||
|
"name": "dog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dog"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"array_of_strings": [
|
||||||
|
"cat",
|
||||||
|
"dog"
|
||||||
|
]
|
||||||
|
}
|
41
js/test_cases/helpers_lte/main.dust
Normal file
41
js/test_cases/helpers_lte/main.dust
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
Testing helpers:{~n}
|
||||||
|
str is {str}{~n}
|
||||||
|
int is {int}{~n}
|
||||||
|
alpha is {alpha}{~n}
|
||||||
|
beta is {beta}{~n}
|
||||||
|
{@lte key=str value="master"}str is less than or equal to "master"{:else}str is greater than "master"{/lte}{~n}
|
||||||
|
{@lte key=str value="7"}str is less than or equal to "7"{:else}str is greater than "7"{/lte}{~n}
|
||||||
|
{@lte key=int value="7"}int is less than or equal to "7"{:else}int is greater than "7"{/lte}{~n}
|
||||||
|
{@lte key=int value=7}int is less than or equal to 7{:else}int is greater than 7{/lte}{~n}
|
||||||
|
{@lte key=int value=6}int is less than or equal to 6{:else}int is greater than 6{/lte}{~n}
|
||||||
|
{@lte key=alpha value=beta}alpha is less than or equal to beta{:else}alpha is greater than beta{/lte}{~n}
|
||||||
|
{@lte value=beta}missing key is true{:else}missing key is false{/lte}{~n}
|
||||||
|
{@lte value=gamma}missing key and non-existent value is true{:else}missing key and non-existent value is false{/lte}{~n}
|
||||||
|
{@lte key=alpha}missing value is true{:else}missing value is false{/lte}{~n}
|
||||||
|
{@lte key=gamma}missing value and non-existent key is true{:else}missing value and non-existent key is false{/lte}{~n}
|
||||||
|
{@lte key="master" value="master"}"master" is less than or equal to "master"{:else}"master" is greater than "master"{/lte}{~n}
|
||||||
|
{@lte key=null}null is less than or equal to a missing value{:else}null is greater than a missing value{/lte}{~n}
|
||||||
|
{@lte key=null value=gamma}null is less than or equal to a non-existent value{:else}null is greater than a non-existent value{/lte}{~n}
|
||||||
|
{@lte}no parameters is true{:else}no parameters is false{/lte}{~n}
|
||||||
|
{@lte key="a" value="]"}"a" is less than or equal to "]"{:else}"a" is greater than "]"{/lte}{~n}
|
||||||
|
{@lte key="a" value="A"}"a" is less than or equal to "A"{:else}"a" is greater than "A"{/lte}{~n}
|
||||||
|
{@lte key="a" value="}"}"a" is less than or equal to "}"{:else}"a" is greater than "}"{/lte}{~n}
|
||||||
|
{!
|
||||||
|
Commented out because unicode breaks nom
|
||||||
|
{@lte key="☃" value="☄"}"☃" is less than or equal to "☄"{:else}"☃" is greater than "☄"{/lte}{~n}
|
||||||
|
!}
|
||||||
|
{@lte key=true_value value=false_value}true is less than or equal to false{:else}true is greater than false{/lte}{~n}
|
||||||
|
{@lte key=array_lower value=array_higher}[3,5,7] is less than or equal to [8,9]{:else}[3,5,7] is greater than [8,9]{/lte}{~n}
|
||||||
|
{@lte key=array_higher value=array_lower}[8,9] is less than or equal to [3,5,7]{:else}[8,9] is greater than [3,5,7]{/lte}{~n}
|
||||||
|
{! non-scalar and copied value tests !}
|
||||||
|
{@lte key=array_lower value=array_lower}array_lower is less than or equal to array_lower{:else}array_lower is greater than array_lower{/lte}{~n}
|
||||||
|
{@lte key=array_lower value=copy_array_lower}array_lower is less than or equal to copy_array_lower{:else}array_lower is greater than copy_array_lower{/lte}{~n}
|
||||||
|
{@lte key=some_obj value=some_obj}some_obj is less than or equal to some_obj{:else}some_obj is greater than some_obj{/lte}{~n}
|
||||||
|
{@lte key=some_obj value=copy_some_obj}some_obj is less than or equal to copy_some_obj{:else}some_obj is greater than copy_some_obj{/lte}{~n}
|
||||||
|
{@lte key=some_obj value=other_obj}some_obj is less than or equal to other_obj{:else}some_obj is greater than other_obj{/lte}{~n}
|
||||||
|
{@lte key=array_of_some_obj value=array_of_some_obj}array_of_some_obj is less than or equal to array_of_some_obj{:else}array_of_some_obj is greater than array_of_some_obj{/lte}{~n}
|
||||||
|
{@lte key=array_of_some_obj value=copy_array_of_some_obj}array_of_some_obj is less than or equal to copy_array_of_some_obj{:else}array_of_some_obj is greater than copy_array_of_some_obj{/lte}{~n}
|
||||||
|
{@lte key=array_of_some_obj value=array_of_other_obj}array_of_some_obj is less than or equal to array_of_other_obj{:else}array_of_some_obj is greater than array_of_other_obj{/lte}{~n}
|
||||||
|
{@lte key=array_higher value=array_higher_longer}array_higher is less than or equal to array_higher_longer{:else}array_higher is greater than array_higher_longer{/lte}{~n}
|
||||||
|
{@lte key=array_lower value=array_lower_with_array}array_lower is less than or equal to array_lower_with_array{:else}array_lower is greater than array_lower_with_array{/lte}{~n}
|
||||||
|
{@lte key=array_lower_with_array value=array_lower}array_lower_with_array is less than or equal to array_lower{:else}array_lower_with_array is greater than array_lower{/lte}{~n}
|
60
js/test_cases/helpers_ne/input1.json
Normal file
60
js/test_cases/helpers_ne/input1.json
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"str": "master",
|
||||||
|
"int": 7,
|
||||||
|
"alpha": 21,
|
||||||
|
"beta": "21",
|
||||||
|
"null": null,
|
||||||
|
"true_value": true,
|
||||||
|
"false_value": false,
|
||||||
|
"array_lower": [
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
7
|
||||||
|
],
|
||||||
|
"copy_array_lower": [
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
7
|
||||||
|
],
|
||||||
|
"array_higher": [
|
||||||
|
8,
|
||||||
|
9
|
||||||
|
],
|
||||||
|
"some_obj": {
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
"copy_some_obj": {
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
"other_obj": {
|
||||||
|
"name": "dog"
|
||||||
|
},
|
||||||
|
"array_of_some_obj": [
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"copy_array_of_some_obj": [
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cat"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"array_of_other_obj": [
|
||||||
|
{
|
||||||
|
"name": "dog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dog"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"array_of_strings": [
|
||||||
|
"cat",
|
||||||
|
"dog"
|
||||||
|
]
|
||||||
|
}
|
28
js/test_cases/helpers_ne/main.dust
Normal file
28
js/test_cases/helpers_ne/main.dust
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
Testing helpers:{~n}
|
||||||
|
str is {str}{~n}
|
||||||
|
int is {int}{~n}
|
||||||
|
alpha is {alpha}{~n}
|
||||||
|
beta is {beta}{~n}
|
||||||
|
{@ne key=str value="master"}str does not equal "master"{:else}str is equal to "master"{/ne}{~n}
|
||||||
|
{@ne key=str value="7"}str does not equal "7"{:else}str is equal to "7"{/ne}{~n}
|
||||||
|
{@ne key=int value="7"}int does not equal "7"{:else}int is equal to "7"{/ne}{~n}
|
||||||
|
{@ne key=int value=7}int does not equal 7{:else}int is equal to 7{/ne}{~n}
|
||||||
|
{@ne key=alpha value=beta}alpha does not equal beta{:else}alpha is equal to beta{/ne}{~n}
|
||||||
|
{@ne value=beta}missing key is true{:else}missing key is false{/ne}{~n}
|
||||||
|
{@ne value=gamma}missing key and non-existent value is true{:else}missing key and non-existent value is false{/ne}{~n}
|
||||||
|
{@ne key=alpha}missing value is true{:else}missing value is false{/ne}{~n}
|
||||||
|
{@ne key=gamma}missing value and non-existent key is true{:else}missing value and non-existent key is false{/ne}{~n}
|
||||||
|
{@ne key="master" value="master"}"master" does not equal "master"{:else}"master" is equal to "master"{/ne}{~n}
|
||||||
|
{@ne key=null}null does not equal a missing value{:else}null equals a missing value{/ne}{~n}
|
||||||
|
{@ne key=null value=gamma}null does not equal non-existent value{:else}null equals a non-existent value{/ne}{~n}
|
||||||
|
{@ne}no parameters is true{:else}no parameters is false{/ne}{~n}
|
||||||
|
{@ne key=array_lower value=array_higher}[3,5,7] does not equal [8,9]{:else}[3,5,7] is equal to [8,9]{/ne}{~n}
|
||||||
|
{! non-scalar and copied value tests !}
|
||||||
|
{@ne key=array_lower value=array_lower}array_lower does not equal array_lower{:else}array_lower is equals to array_lower{/ne}{~n}
|
||||||
|
{@ne key=array_lower value=copy_array_lower}array_lower does not equal copy_array_lower{:else}array_lower is equals to copy_array_lower{/ne}{~n}
|
||||||
|
{@ne key=some_obj value=some_obj}some_obj does not equal some_obj{:else}some_obj is equals to some_obj{/ne}{~n}
|
||||||
|
{@ne key=some_obj value=copy_some_obj}some_obj does not equal copy_some_obj{:else}some_obj is equals to copy_some_obj{/ne}{~n}
|
||||||
|
{@ne key=some_obj value=other_obj}some_obj does not equal other_obj{:else}some_obj is equals to other_obj{/ne}{~n}
|
||||||
|
{@ne key=array_of_some_obj value=array_of_some_obj}array_of_some_obj does not equal array_of_some_obj{:else}array_of_some_obj is equals to array_of_some_obj{/ne}{~n}
|
||||||
|
{@ne key=array_of_some_obj value=copy_array_of_some_obj}array_of_some_obj does not equal copy_array_of_some_obj{:else}array_of_some_obj is equals to copy_array_of_some_obj{/ne}{~n}
|
||||||
|
{@ne key=array_of_some_obj value=array_of_other_obj}array_of_some_obj does not equal array_of_other_obj{:else}array_of_some_obj is equals to array_of_other_obj{/ne}{~n}
|
119
src/bin.rs
119
src/bin.rs
@ -1,5 +1,6 @@
|
|||||||
extern crate nom;
|
extern crate nom;
|
||||||
|
|
||||||
|
use crate::renderer::CompareContextElement;
|
||||||
use parser::Filter;
|
use parser::Filter;
|
||||||
use renderer::compile_template;
|
use renderer::compile_template;
|
||||||
use renderer::CompiledTemplate;
|
use renderer::CompiledTemplate;
|
||||||
@ -10,6 +11,7 @@ use renderer::RenderError;
|
|||||||
use renderer::Renderable;
|
use renderer::Renderable;
|
||||||
use renderer::WalkError;
|
use renderer::WalkError;
|
||||||
use renderer::Walkable;
|
use renderer::Walkable;
|
||||||
|
use std::cmp::Ordering;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::{self, Read};
|
use std::io::{self, Read};
|
||||||
@ -137,3 +139,120 @@ impl Loopable for serde_json::Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CompareContextElement for serde_json::Value {
|
||||||
|
fn equals(&self, other: &dyn ContextElement) -> bool {
|
||||||
|
// Handle other serde_json::Value
|
||||||
|
match other.to_any().downcast_ref::<Self>() {
|
||||||
|
None => (),
|
||||||
|
Some(other_json_value) => match (self, other_json_value) {
|
||||||
|
// Non-scalar values not caught in the renderer by the
|
||||||
|
// identical-path shortcut are always not equal.
|
||||||
|
(serde_json::Value::Array(_), _)
|
||||||
|
| (_, serde_json::Value::Array(_))
|
||||||
|
| (serde_json::Value::Object(_), _)
|
||||||
|
| (_, serde_json::Value::Object(_)) => return false,
|
||||||
|
_ => return self == other_json_value,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// Handle string literals
|
||||||
|
match other.to_any().downcast_ref::<String>() {
|
||||||
|
None => (),
|
||||||
|
Some(other_string) => return self.as_str().map_or(false, |s| s == other_string),
|
||||||
|
}
|
||||||
|
// Handle numeric literals
|
||||||
|
match other.to_any().downcast_ref::<u64>() {
|
||||||
|
None => (),
|
||||||
|
Some(other_num) => return self.as_u64().map_or(false, |n| n == *other_num),
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn partial_compare(&self, other: &dyn ContextElement) -> Option<Ordering> {
|
||||||
|
// Handle other serde_json::Value
|
||||||
|
match other.to_any().downcast_ref::<Self>() {
|
||||||
|
None => (),
|
||||||
|
Some(other_json_value) => {
|
||||||
|
return match (self, other_json_value) {
|
||||||
|
(
|
||||||
|
serde_json::Value::Bool(self_boolean),
|
||||||
|
serde_json::Value::Bool(other_boolean),
|
||||||
|
) => self_boolean.partial_cmp(other_boolean),
|
||||||
|
(
|
||||||
|
serde_json::Value::Number(self_number),
|
||||||
|
serde_json::Value::Number(other_number),
|
||||||
|
) => match (
|
||||||
|
self_number.as_f64(),
|
||||||
|
other_number.as_f64(),
|
||||||
|
self_number.as_u64(),
|
||||||
|
other_number.as_u64(),
|
||||||
|
self_number.as_i64(),
|
||||||
|
other_number.as_i64(),
|
||||||
|
) {
|
||||||
|
(_, _, _, _, Some(self_int), Some(other_int)) => {
|
||||||
|
self_int.partial_cmp(&other_int)
|
||||||
|
}
|
||||||
|
(_, _, Some(self_uint), Some(other_uint), _, _) => {
|
||||||
|
self_uint.partial_cmp(&other_uint)
|
||||||
|
}
|
||||||
|
(_, _, Some(_self_uint), _, _, Some(_other_int)) => {
|
||||||
|
// If the previous matches did not catch
|
||||||
|
// it, then other must be negative and
|
||||||
|
// self must be larger than can be
|
||||||
|
// represented with an i64, therefore self
|
||||||
|
// is greater than other.
|
||||||
|
Some(Ordering::Greater)
|
||||||
|
}
|
||||||
|
(_, _, _, Some(_other_uint), Some(_self_int), _) => {
|
||||||
|
// If the previous matches did not catch
|
||||||
|
// it, then self must be negative and
|
||||||
|
// other must be larger than can be
|
||||||
|
// represented with an i64, therefore
|
||||||
|
// other is greater than self.
|
||||||
|
Some(Ordering::Less)
|
||||||
|
}
|
||||||
|
(Some(self_float), Some(other_float), _, _, _, _) => {
|
||||||
|
self_float.partial_cmp(&other_float)
|
||||||
|
}
|
||||||
|
_ => panic!("This should be impossible since u64 and i64 can both be converted to floats"),
|
||||||
|
},
|
||||||
|
(
|
||||||
|
serde_json::Value::String(self_string),
|
||||||
|
serde_json::Value::String(other_string),
|
||||||
|
) => self_string.partial_cmp(other_string),
|
||||||
|
(serde_json::Value::Array(self_array), serde_json::Value::Array(other_array)) => convert_vec_to_context_element(self_array).partial_cmp(&convert_vec_to_context_element(other_array)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Handle string literals
|
||||||
|
match other.to_any().downcast_ref::<String>() {
|
||||||
|
None => (),
|
||||||
|
Some(other_string) => {
|
||||||
|
return self
|
||||||
|
.as_str()
|
||||||
|
.map_or(None, |s| s.partial_cmp(other_string.as_str()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Handle numeric literals
|
||||||
|
match other.to_any().downcast_ref::<u64>() {
|
||||||
|
None => (),
|
||||||
|
Some(other_num) => return self.as_u64().map_or(None, |n| n.partial_cmp(other_num)),
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new vec by of references to the serde_json::Values as
|
||||||
|
/// ContextElement trait objects so we can use its implementation of
|
||||||
|
/// PartialOrd.
|
||||||
|
///
|
||||||
|
/// You cannot implement a trait you do not define for a type you do
|
||||||
|
/// not define, so I cannot implement PartialOrd for
|
||||||
|
/// serde_json::value. Instead, I just re-use the PartialOrd
|
||||||
|
/// implementation for ContextElement which unfortunately has extra
|
||||||
|
/// overhead of downcasting. This would be a good spot for
|
||||||
|
/// optimization.
|
||||||
|
fn convert_vec_to_context_element(array: &Vec<serde_json::Value>) -> Vec<&dyn ContextElement> {
|
||||||
|
array.iter().map(|v| v as _).collect()
|
||||||
|
}
|
||||||
|
@ -2,11 +2,11 @@ use nom::branch::alt;
|
|||||||
use nom::bytes::complete::escaped_transform;
|
use nom::bytes::complete::escaped_transform;
|
||||||
use nom::bytes::complete::is_a;
|
use nom::bytes::complete::is_a;
|
||||||
use nom::bytes::complete::is_not;
|
use nom::bytes::complete::is_not;
|
||||||
use nom::bytes::complete::{tag, take_until, take_until_parser_matches};
|
use nom::bytes::complete::{tag, take_until, take_until_parser_matches, take_while};
|
||||||
use nom::character::complete::line_ending;
|
use nom::character::complete::line_ending;
|
||||||
use nom::character::complete::multispace0;
|
use nom::character::complete::multispace0;
|
||||||
use nom::character::complete::one_of;
|
use nom::character::complete::one_of;
|
||||||
use nom::character::complete::{space0, space1};
|
use nom::character::complete::{digit1, space0, space1};
|
||||||
use nom::combinator::all_consuming;
|
use nom::combinator::all_consuming;
|
||||||
use nom::combinator::map;
|
use nom::combinator::map;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
@ -38,8 +38,8 @@ pub enum DustTag<'a> {
|
|||||||
DTHelperNotEquals(ParameterizedBlock<'a>),
|
DTHelperNotEquals(ParameterizedBlock<'a>),
|
||||||
DTHelperGreaterThan(ParameterizedBlock<'a>),
|
DTHelperGreaterThan(ParameterizedBlock<'a>),
|
||||||
DTHelperLessThan(ParameterizedBlock<'a>),
|
DTHelperLessThan(ParameterizedBlock<'a>),
|
||||||
DTHelperGreaterThenOrEquals(ParameterizedBlock<'a>),
|
DTHelperGreaterThanOrEquals(ParameterizedBlock<'a>),
|
||||||
DTHelperLessThenOrEquals(ParameterizedBlock<'a>),
|
DTHelperLessThanOrEquals(ParameterizedBlock<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
@ -106,10 +106,10 @@ pub struct NamedBlock<'a> {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct ParameterizedBlock<'a> {
|
pub struct ParameterizedBlock<'a> {
|
||||||
name: &'a str,
|
pub name: &'a str,
|
||||||
params: Vec<KVPair<'a>>,
|
pub params: Vec<KVPair<'a>>,
|
||||||
contents: Option<Body<'a>>,
|
pub contents: Option<Body<'a>>,
|
||||||
else_contents: Option<Body<'a>>,
|
pub else_contents: Option<Body<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
@ -122,6 +122,7 @@ pub struct Partial<'a> {
|
|||||||
pub enum RValue<'a> {
|
pub enum RValue<'a> {
|
||||||
RVPath(Path<'a>),
|
RVPath(Path<'a>),
|
||||||
RVString(String),
|
RVString(String),
|
||||||
|
RVPositiveInteger(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
@ -161,8 +162,8 @@ fn dust_tag(i: &str) -> IResult<&str, DustTag> {
|
|||||||
named_block("{+", DustTag::DTBlock),
|
named_block("{+", DustTag::DTBlock),
|
||||||
named_block("{<", DustTag::DTInlinePartial),
|
named_block("{<", DustTag::DTInlinePartial),
|
||||||
partial("{>", DustTag::DTPartial),
|
partial("{>", DustTag::DTPartial),
|
||||||
parameterized_block("{@", "gte", DustTag::DTHelperGreaterThenOrEquals),
|
parameterized_block("{@", "gte", DustTag::DTHelperGreaterThanOrEquals),
|
||||||
parameterized_block("{@", "lte", DustTag::DTHelperLessThenOrEquals),
|
parameterized_block("{@", "lte", DustTag::DTHelperLessThanOrEquals),
|
||||||
parameterized_block("{@", "eq", DustTag::DTHelperEquals),
|
parameterized_block("{@", "eq", DustTag::DTHelperEquals),
|
||||||
parameterized_block("{@", "ne", DustTag::DTHelperNotEquals),
|
parameterized_block("{@", "ne", DustTag::DTHelperNotEquals),
|
||||||
parameterized_block("{@", "gt", DustTag::DTHelperGreaterThan),
|
parameterized_block("{@", "gt", DustTag::DTHelperGreaterThan),
|
||||||
@ -210,11 +211,23 @@ fn path(i: &str) -> IResult<&str, Path> {
|
|||||||
))(i)
|
))(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Just digits, no signs or decimals
|
||||||
|
fn postitive_integer_literal(i: &str) -> IResult<&str, u64> {
|
||||||
|
map(
|
||||||
|
verify(
|
||||||
|
map(digit1, |number_string: &str| number_string.parse::<u64>()),
|
||||||
|
|parse_result| parse_result.is_ok(),
|
||||||
|
),
|
||||||
|
|parsed_number| parsed_number.unwrap(),
|
||||||
|
)(i)
|
||||||
|
}
|
||||||
|
|
||||||
/// Either a literal or a path to a value
|
/// Either a literal or a path to a value
|
||||||
fn rvalue(i: &str) -> IResult<&str, RValue> {
|
fn rvalue(i: &str) -> IResult<&str, RValue> {
|
||||||
alt((
|
alt((
|
||||||
map(path, RValue::RVPath),
|
map(path, RValue::RVPath),
|
||||||
map(quoted_string, RValue::RVString),
|
map(quoted_string, RValue::RVString),
|
||||||
|
map(postitive_integer_literal, RValue::RVPositiveInteger),
|
||||||
))(i)
|
))(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -929,6 +942,29 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_literals() {
|
||||||
|
assert_eq!(
|
||||||
|
dust_tag(r#"{>foo a="foo" b=179/}"#),
|
||||||
|
Ok((
|
||||||
|
"",
|
||||||
|
DustTag::DTPartial(Partial {
|
||||||
|
name: "foo".to_owned(),
|
||||||
|
params: vec![
|
||||||
|
KVPair {
|
||||||
|
key: "a",
|
||||||
|
value: RValue::RVString("foo".to_owned())
|
||||||
|
},
|
||||||
|
KVPair {
|
||||||
|
key: "b",
|
||||||
|
value: RValue::RVPositiveInteger(179)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_helper() {
|
fn test_helper() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
use crate::parser::Filter;
|
use crate::parser::Filter;
|
||||||
use crate::renderer::errors::RenderError;
|
use crate::renderer::errors::RenderError;
|
||||||
use crate::renderer::errors::WalkError;
|
use crate::renderer::errors::WalkError;
|
||||||
use std::fmt::Debug;
|
use std::any::Any;
|
||||||
|
use std::{cmp::Ordering, fmt::Debug};
|
||||||
|
|
||||||
pub trait ContextElement: Debug + Walkable + Renderable + Loopable {}
|
pub trait ContextElement:
|
||||||
|
Debug + Walkable + Renderable + Loopable + CloneIntoBoxedContextElement + CompareContextElement
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Walkable {
|
pub trait Walkable {
|
||||||
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError>;
|
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError>;
|
||||||
@ -25,3 +29,41 @@ pub trait Loopable {
|
|||||||
/// for each element of the array.
|
/// for each element of the array.
|
||||||
fn get_loop_elements(&self) -> Vec<&dyn ContextElement>;
|
fn get_loop_elements(&self) -> Vec<&dyn ContextElement>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait CastToAny {
|
||||||
|
fn to_any(&self) -> &dyn Any;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CompareContextElement: CastToAny {
|
||||||
|
fn equals(&self, other: &dyn ContextElement) -> bool;
|
||||||
|
|
||||||
|
fn partial_compare(&self, other: &dyn ContextElement) -> Option<Ordering>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CloneIntoBoxedContextElement {
|
||||||
|
fn clone_to_box(&self) -> Box<dyn ContextElement>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: 'static + ContextElement + Clone> CloneIntoBoxedContextElement for C {
|
||||||
|
fn clone_to_box(&self) -> Box<dyn ContextElement> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: 'static + ContextElement> CastToAny for C {
|
||||||
|
fn to_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> PartialEq<&'b dyn ContextElement> for &'a dyn ContextElement {
|
||||||
|
fn eq(&self, other: &&'b dyn ContextElement) -> bool {
|
||||||
|
self.equals(*other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> PartialOrd<&'b dyn ContextElement> for &'a dyn ContextElement {
|
||||||
|
fn partial_cmp(&self, other: &&'b dyn ContextElement) -> Option<Ordering> {
|
||||||
|
self.partial_compare(*other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,6 +9,7 @@ pub enum RenderError {
|
|||||||
TemplateNotFound(String),
|
TemplateNotFound(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
pub enum WalkError {
|
pub enum WalkError {
|
||||||
CantWalk,
|
CantWalk,
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,65 @@ fn extract_inline_partials_from_tag<'a, 'b>(
|
|||||||
blocks.insert(&named_block.name, &named_block.contents);
|
blocks.insert(&named_block.name, &named_block.contents);
|
||||||
}
|
}
|
||||||
DustTag::DTBlock(..) => (),
|
DustTag::DTBlock(..) => (),
|
||||||
_ => (), // TODO: Implement the rest
|
DustTag::DTHelperEquals(parameterized_block) => {
|
||||||
|
match ¶meterized_block.contents {
|
||||||
|
None => (),
|
||||||
|
Some(body) => extract_inline_partials_from_body(blocks, &body),
|
||||||
|
};
|
||||||
|
match ¶meterized_block.else_contents {
|
||||||
|
None => (),
|
||||||
|
Some(body) => extract_inline_partials_from_body(blocks, &body),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
DustTag::DTHelperNotEquals(parameterized_block) => {
|
||||||
|
match ¶meterized_block.contents {
|
||||||
|
None => (),
|
||||||
|
Some(body) => extract_inline_partials_from_body(blocks, &body),
|
||||||
|
};
|
||||||
|
match ¶meterized_block.else_contents {
|
||||||
|
None => (),
|
||||||
|
Some(body) => extract_inline_partials_from_body(blocks, &body),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
DustTag::DTHelperGreaterThan(parameterized_block) => {
|
||||||
|
match ¶meterized_block.contents {
|
||||||
|
None => (),
|
||||||
|
Some(body) => extract_inline_partials_from_body(blocks, &body),
|
||||||
|
};
|
||||||
|
match ¶meterized_block.else_contents {
|
||||||
|
None => (),
|
||||||
|
Some(body) => extract_inline_partials_from_body(blocks, &body),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
DustTag::DTHelperLessThan(parameterized_block) => {
|
||||||
|
match ¶meterized_block.contents {
|
||||||
|
None => (),
|
||||||
|
Some(body) => extract_inline_partials_from_body(blocks, &body),
|
||||||
|
};
|
||||||
|
match ¶meterized_block.else_contents {
|
||||||
|
None => (),
|
||||||
|
Some(body) => extract_inline_partials_from_body(blocks, &body),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
DustTag::DTHelperGreaterThanOrEquals(parameterized_block) => {
|
||||||
|
match ¶meterized_block.contents {
|
||||||
|
None => (),
|
||||||
|
Some(body) => extract_inline_partials_from_body(blocks, &body),
|
||||||
|
};
|
||||||
|
match ¶meterized_block.else_contents {
|
||||||
|
None => (),
|
||||||
|
Some(body) => extract_inline_partials_from_body(blocks, &body),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
DustTag::DTHelperLessThanOrEquals(parameterized_block) => {
|
||||||
|
match ¶meterized_block.contents {
|
||||||
|
None => (),
|
||||||
|
Some(body) => extract_inline_partials_from_body(blocks, &body),
|
||||||
|
};
|
||||||
|
match ¶meterized_block.else_contents {
|
||||||
|
None => (),
|
||||||
|
Some(body) => extract_inline_partials_from_body(blocks, &body),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,8 @@ mod parameters_context;
|
|||||||
mod renderer;
|
mod renderer;
|
||||||
mod walking;
|
mod walking;
|
||||||
|
|
||||||
|
pub use context_element::CloneIntoBoxedContextElement;
|
||||||
|
pub use context_element::CompareContextElement;
|
||||||
pub use context_element::ContextElement;
|
pub use context_element::ContextElement;
|
||||||
pub use context_element::Loopable;
|
pub use context_element::Loopable;
|
||||||
pub use context_element::Renderable;
|
pub use context_element::Renderable;
|
||||||
|
@ -1,39 +1,75 @@
|
|||||||
use crate::parser::KVPair;
|
use crate::parser::KVPair;
|
||||||
use crate::parser::{Filter, RValue};
|
use crate::parser::{Filter, RValue};
|
||||||
|
use crate::renderer::context_element::CompareContextElement;
|
||||||
use crate::renderer::context_element::ContextElement;
|
use crate::renderer::context_element::ContextElement;
|
||||||
use crate::renderer::walking::walk_path;
|
use crate::renderer::walking::owned_walk_path;
|
||||||
use crate::renderer::Loopable;
|
use crate::renderer::Loopable;
|
||||||
use crate::renderer::RenderError;
|
use crate::renderer::RenderError;
|
||||||
use crate::renderer::Renderable;
|
use crate::renderer::Renderable;
|
||||||
use crate::renderer::WalkError;
|
use crate::renderer::WalkError;
|
||||||
use crate::renderer::Walkable;
|
use crate::renderer::Walkable;
|
||||||
use std::collections::HashMap;
|
use std::{cmp::Ordering, collections::HashMap};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
/// Copy the data from an RValue to an Owned struct
|
||||||
pub struct ParametersContext<'a> {
|
///
|
||||||
params: HashMap<&'a str, &'a RValue<'a>>,
|
/// In order to get comparisons to work for our `ContextElement` trait
|
||||||
breadcrumbs: &'a Vec<&'a dyn ContextElement>,
|
/// objects, we need to be able to use `std::any::Any`. Unfortunately,
|
||||||
|
/// `Any` requires that the structs do not have a lifetime (so they
|
||||||
|
/// will have a `'static` lifetime. This means that we cannot have a
|
||||||
|
/// `<'a>` appended to the struct type, so the struct cannot contain
|
||||||
|
/// any borrows. Rather than impose the copy cost in the parser, we
|
||||||
|
/// are imposing the cost of copying the data in the renderer because
|
||||||
|
/// the parser has no reason to not be able to reference data from the
|
||||||
|
/// input string.
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum OwnedRValue {
|
||||||
|
RVPath(OwnedPath),
|
||||||
|
RVString(String),
|
||||||
|
RVPositiveInteger(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ParametersContext<'a> {
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub fn new(
|
pub struct OwnedPath {
|
||||||
breadcrumbs: &'a Vec<&'a dyn ContextElement>,
|
pub keys: Vec<String>,
|
||||||
params: &'a Vec<KVPair<'a>>,
|
}
|
||||||
) -> ParametersContext<'a> {
|
|
||||||
let param_map = params
|
impl From<&RValue<'_>> for OwnedRValue {
|
||||||
.iter()
|
fn from(original: &RValue<'_>) -> Self {
|
||||||
.map(|pair: &KVPair<'a>| (pair.key, &pair.value))
|
match original {
|
||||||
.collect();
|
RValue::RVString(text) => OwnedRValue::RVString(text.to_owned()),
|
||||||
ParametersContext {
|
RValue::RVPath(path) => OwnedRValue::RVPath(OwnedPath {
|
||||||
params: param_map,
|
keys: path.keys.iter().map(|k| k.to_string()).collect(),
|
||||||
breadcrumbs: breadcrumbs,
|
}),
|
||||||
|
RValue::RVPositiveInteger(num) => OwnedRValue::RVPositiveInteger(*num),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ContextElement for ParametersContext<'a> {}
|
#[derive(Debug)]
|
||||||
|
pub struct ParametersContext {
|
||||||
|
params: HashMap<String, OwnedRValue>,
|
||||||
|
breadcrumbs: Vec<Box<dyn ContextElement>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Renderable for ParametersContext<'a> {
|
impl ParametersContext {
|
||||||
|
pub fn new(breadcrumbs: &Vec<&dyn ContextElement>, params: &Vec<KVPair>) -> ParametersContext {
|
||||||
|
let owned_params: HashMap<String, OwnedRValue> = params
|
||||||
|
.iter()
|
||||||
|
.map(|kvpair| (kvpair.key.to_string(), OwnedRValue::from(&kvpair.value)))
|
||||||
|
.collect();
|
||||||
|
let owned_breadcrumbs: Vec<Box<dyn ContextElement>> =
|
||||||
|
breadcrumbs.iter().map(|ce| ce.clone_to_box()).collect();
|
||||||
|
|
||||||
|
ParametersContext {
|
||||||
|
params: owned_params,
|
||||||
|
breadcrumbs: owned_breadcrumbs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContextElement for ParametersContext {}
|
||||||
|
|
||||||
|
impl Renderable for ParametersContext {
|
||||||
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
|
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
|
||||||
// TODO: Would this even ever be called? Won't matter, but I'd
|
// TODO: Would this even ever be called? Won't matter, but I'd
|
||||||
// like to know. Since it is injected 1 above the current
|
// like to know. Since it is injected 1 above the current
|
||||||
@ -42,7 +78,7 @@ impl<'a> Renderable for ParametersContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Loopable for ParametersContext<'a> {
|
impl Loopable for ParametersContext {
|
||||||
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
|
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
|
||||||
// TODO: Would this even ever be called? Won't matter, but I'd
|
// TODO: Would this even ever be called? Won't matter, but I'd
|
||||||
// like to know. Since it is injected 1 above the current
|
// like to know. Since it is injected 1 above the current
|
||||||
@ -51,16 +87,48 @@ impl<'a> Loopable for ParametersContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Walkable for ParametersContext<'a> {
|
impl Walkable for ParametersContext {
|
||||||
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
|
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
|
||||||
let rval = self.params.get(segment).ok_or(WalkError::CantWalk)?;
|
let rval = self.params.get(segment).ok_or(WalkError::CantWalk)?;
|
||||||
match rval {
|
match rval {
|
||||||
RValue::RVPath(path) => walk_path(self.breadcrumbs, &path.keys),
|
OwnedRValue::RVPath(path) => owned_walk_path(&self.breadcrumbs, &path.keys),
|
||||||
RValue::RVString(text) => Ok(text),
|
OwnedRValue::RVString(text) => Ok(text),
|
||||||
|
OwnedRValue::RVPositiveInteger(num) => Ok(num),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Clone for ParametersContext {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
let new_params: HashMap<String, OwnedRValue> = self
|
||||||
|
.params
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| (k.clone(), v.clone()))
|
||||||
|
.collect();
|
||||||
|
let new_breadcrumbs: Vec<Box<dyn ContextElement>> = self
|
||||||
|
.breadcrumbs
|
||||||
|
.iter()
|
||||||
|
.map(|bread| bread.clone_to_box())
|
||||||
|
.collect();
|
||||||
|
ParametersContext {
|
||||||
|
params: new_params,
|
||||||
|
breadcrumbs: new_breadcrumbs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompareContextElement for ParametersContext {
|
||||||
|
fn equals(&self, other: &dyn ContextElement) -> bool {
|
||||||
|
// TODO: Does this ever happen? perhaps I should have a panic here.
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn partial_compare(&self, other: &dyn ContextElement) -> Option<Ordering> {
|
||||||
|
// TODO: Does this ever happen? perhaps I should have a panic here.
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ContextElement for String {}
|
impl ContextElement for String {}
|
||||||
|
|
||||||
impl Renderable for String {
|
impl Renderable for String {
|
||||||
@ -84,3 +152,81 @@ impl Walkable for String {
|
|||||||
Err(WalkError::CantWalk)
|
Err(WalkError::CantWalk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CompareContextElement for String {
|
||||||
|
fn equals(&self, other: &dyn ContextElement) -> bool {
|
||||||
|
// If its a String then compare them directly, otherwise defer
|
||||||
|
// to the other type's implementation of CompareContextElement
|
||||||
|
// since the end user could add any type.
|
||||||
|
match other.to_any().downcast_ref::<Self>() {
|
||||||
|
None => other.equals(self),
|
||||||
|
Some(other_string) => self == other_string,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn partial_compare(&self, other: &dyn ContextElement) -> Option<Ordering> {
|
||||||
|
// If its a string then compare them directly, otherwise defer
|
||||||
|
// to the other type's implementation of CompareContextElement
|
||||||
|
// since the end user could add any type.
|
||||||
|
match other.to_any().downcast_ref::<Self>() {
|
||||||
|
None => match other.partial_compare(self) {
|
||||||
|
None => None,
|
||||||
|
Some(ord) => match ord {
|
||||||
|
Ordering::Equal => Some(Ordering::Equal),
|
||||||
|
Ordering::Greater => Some(Ordering::Less),
|
||||||
|
Ordering::Less => Some(Ordering::Greater),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Some(other_string) => self.partial_cmp(other_string),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContextElement for u64 {}
|
||||||
|
|
||||||
|
impl Renderable for u64 {
|
||||||
|
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
|
||||||
|
Ok(self.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Loopable for u64 {
|
||||||
|
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
|
||||||
|
vec![self]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Walkable for u64 {
|
||||||
|
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
|
||||||
|
Err(WalkError::CantWalk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompareContextElement for u64 {
|
||||||
|
fn equals(&self, other: &dyn ContextElement) -> bool {
|
||||||
|
// If its a u64 then compare them directly, otherwise defer
|
||||||
|
// to the other type's implementation of CompareContextElement
|
||||||
|
// since the end user could add any type.
|
||||||
|
match other.to_any().downcast_ref::<Self>() {
|
||||||
|
None => other.equals(self),
|
||||||
|
Some(other_num) => self == other_num,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn partial_compare(&self, other: &dyn ContextElement) -> Option<Ordering> {
|
||||||
|
// If its a u64 then compare them directly, otherwise defer
|
||||||
|
// to the other type's implementation of CompareContextElement
|
||||||
|
// since the end user could add any type.
|
||||||
|
match other.to_any().downcast_ref::<Self>() {
|
||||||
|
None => match other.partial_compare(self) {
|
||||||
|
None => None,
|
||||||
|
Some(ord) => match ord {
|
||||||
|
Ordering::Equal => Some(Ordering::Equal),
|
||||||
|
Ordering::Greater => Some(Ordering::Less),
|
||||||
|
Ordering::Less => Some(Ordering::Greater),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Some(other_num) => self.partial_cmp(other_num),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use crate::parser::template;
|
use crate::parser::template;
|
||||||
use crate::parser::Body;
|
use crate::parser::Body;
|
||||||
use crate::parser::DustTag;
|
use crate::parser::DustTag;
|
||||||
|
use crate::parser::KVPair;
|
||||||
|
use crate::parser::RValue;
|
||||||
use crate::parser::Special;
|
use crate::parser::Special;
|
||||||
use crate::parser::Template;
|
use crate::parser::Template;
|
||||||
use crate::parser::TemplateElement;
|
use crate::parser::TemplateElement;
|
||||||
@ -12,7 +14,7 @@ use crate::renderer::inline_partial_tree::extract_inline_partials;
|
|||||||
use crate::renderer::inline_partial_tree::InlinePartialTreeElement;
|
use crate::renderer::inline_partial_tree::InlinePartialTreeElement;
|
||||||
use crate::renderer::parameters_context::ParametersContext;
|
use crate::renderer::parameters_context::ParametersContext;
|
||||||
use crate::renderer::walking::walk_path;
|
use crate::renderer::walking::walk_path;
|
||||||
use std::collections::HashMap;
|
use std::{cmp::Ordering, collections::HashMap};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct CompiledTemplate<'a> {
|
pub struct CompiledTemplate<'a> {
|
||||||
@ -75,6 +77,18 @@ impl<'a> DustRenderer<'a> {
|
|||||||
self.render_body(&main_template.contents, breadcrumbs, &new_blocks)
|
self.render_body(&main_template.contents, breadcrumbs, &new_blocks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_maybe_body(
|
||||||
|
&'a self,
|
||||||
|
body: &'a Option<Body>,
|
||||||
|
breadcrumbs: &Vec<&'a dyn ContextElement>,
|
||||||
|
blocks: &'a InlinePartialTreeElement<'a>,
|
||||||
|
) -> Result<String, RenderError> {
|
||||||
|
match body {
|
||||||
|
None => Ok("".to_owned()),
|
||||||
|
Some(body) => Ok(self.render_body(body, breadcrumbs, blocks)?),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render_body(
|
fn render_body(
|
||||||
&'a self,
|
&'a self,
|
||||||
body: &'a Body,
|
body: &'a Body,
|
||||||
@ -128,16 +142,13 @@ impl<'a> DustRenderer<'a> {
|
|||||||
}
|
}
|
||||||
DustTag::DTSection(container) => {
|
DustTag::DTSection(container) => {
|
||||||
let val = walk_path(breadcrumbs, &container.path.keys);
|
let val = walk_path(breadcrumbs, &container.path.keys);
|
||||||
let loop_elements: Vec<&dyn ContextElement> = self.get_loop_elements(val);
|
let loop_elements: Vec<&dyn ContextElement> = Self::get_loop_elements(val);
|
||||||
if loop_elements.is_empty() {
|
if loop_elements.is_empty() {
|
||||||
// Oddly enough if the value is falsey (like
|
// Oddly enough if the value is falsey (like
|
||||||
// an empty array or null), Dust uses the
|
// an empty array or null), Dust uses the
|
||||||
// original context before walking the path as
|
// original context before walking the path as
|
||||||
// the context for rendering the else block
|
// the context for rendering the else block
|
||||||
return match &container.else_contents {
|
return self.render_maybe_body(&container.else_contents, breadcrumbs, blocks);
|
||||||
Some(body) => self.render_body(&body, breadcrumbs, blocks),
|
|
||||||
None => Ok("".to_owned()),
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
match &container.contents {
|
match &container.contents {
|
||||||
None => return Ok("".to_owned()),
|
None => return Ok("".to_owned()),
|
||||||
@ -158,33 +169,21 @@ impl<'a> DustRenderer<'a> {
|
|||||||
}
|
}
|
||||||
DustTag::DTExists(container) => {
|
DustTag::DTExists(container) => {
|
||||||
let val = walk_path(breadcrumbs, &container.path.keys);
|
let val = walk_path(breadcrumbs, &container.path.keys);
|
||||||
let loop_elements: Vec<&dyn ContextElement> = self.get_loop_elements(val);
|
let loop_elements: Vec<&dyn ContextElement> = Self::get_loop_elements(val);
|
||||||
if loop_elements.is_empty() {
|
return if loop_elements.is_empty() {
|
||||||
return match &container.else_contents {
|
self.render_maybe_body(&container.else_contents, breadcrumbs, blocks)
|
||||||
Some(body) => self.render_body(&body, breadcrumbs, blocks),
|
|
||||||
None => Ok("".to_owned()),
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
return match &container.contents {
|
self.render_maybe_body(&container.contents, breadcrumbs, blocks)
|
||||||
None => Ok("".to_owned()),
|
};
|
||||||
Some(body) => self.render_body(&body, breadcrumbs, blocks),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
DustTag::DTNotExists(container) => {
|
DustTag::DTNotExists(container) => {
|
||||||
let val = walk_path(breadcrumbs, &container.path.keys);
|
let val = walk_path(breadcrumbs, &container.path.keys);
|
||||||
let loop_elements: Vec<&dyn ContextElement> = self.get_loop_elements(val);
|
let loop_elements: Vec<&dyn ContextElement> = Self::get_loop_elements(val);
|
||||||
if !loop_elements.is_empty() {
|
return if !loop_elements.is_empty() {
|
||||||
return match &container.else_contents {
|
self.render_maybe_body(&container.else_contents, breadcrumbs, blocks)
|
||||||
Some(body) => self.render_body(&body, breadcrumbs, blocks),
|
|
||||||
None => Ok("".to_owned()),
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
return match &container.contents {
|
self.render_maybe_body(&container.contents, breadcrumbs, blocks)
|
||||||
None => Ok("".to_owned()),
|
};
|
||||||
Some(body) => self.render_body(&body, breadcrumbs, blocks),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
DustTag::DTPartial(partial) => {
|
DustTag::DTPartial(partial) => {
|
||||||
if partial.params.is_empty() {
|
if partial.params.is_empty() {
|
||||||
@ -200,28 +199,238 @@ impl<'a> DustRenderer<'a> {
|
|||||||
return Ok(rendered_content);
|
return Ok(rendered_content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DustTag::DTInlinePartial(named_block) => {
|
DustTag::DTInlinePartial(_named_block) => {
|
||||||
// Inline partials are blank during rendering (they get injected into blocks)
|
// Inline partials are blank during rendering (they get injected into blocks)
|
||||||
return Ok("".to_owned());
|
return Ok("".to_owned());
|
||||||
}
|
}
|
||||||
DustTag::DTBlock(named_block) => {
|
DustTag::DTBlock(named_block) => {
|
||||||
match blocks.get_block(named_block.name) {
|
return match blocks.get_block(named_block.name) {
|
||||||
None => match &named_block.contents {
|
None => self.render_maybe_body(&named_block.contents, breadcrumbs, blocks),
|
||||||
None => return Ok("".to_owned()),
|
Some(interior) => self.render_maybe_body(interior, breadcrumbs, blocks),
|
||||||
Some(body) => {
|
|
||||||
let rendered_content = self.render_body(body, breadcrumbs, blocks)?;
|
|
||||||
return Ok(rendered_content);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Some(interior) => match interior {
|
|
||||||
None => return Ok("".to_owned()),
|
|
||||||
Some(body) => {
|
|
||||||
let rendered_content = self.render_body(body, breadcrumbs, blocks)?;
|
|
||||||
return Ok(rendered_content);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
DustTag::DTHelperEquals(parameterized_block) => {
|
||||||
|
let param_map: HashMap<&str, &RValue<'a>> =
|
||||||
|
Self::get_rval_map(¶meterized_block.params);
|
||||||
|
// Special case: when comparing two RVPaths, if the
|
||||||
|
// path is equal then dust assumes the values are
|
||||||
|
// equal (otherwise, non-scalar values are
|
||||||
|
// automatically not equal)
|
||||||
|
if Self::are_paths_identical(¶m_map) {
|
||||||
|
return match ¶meterized_block.contents {
|
||||||
|
None => Ok("".to_owned()),
|
||||||
|
Some(body) => {
|
||||||
|
let rendered_content = self.render_body(body, breadcrumbs, blocks)?;
|
||||||
|
Ok(rendered_content)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let left_side: Result<&dyn ContextElement, WalkError> =
|
||||||
|
match Self::get_rval(breadcrumbs, ¶m_map, "key") {
|
||||||
|
None => return Ok("".to_owned()),
|
||||||
|
Some(res) => res,
|
||||||
|
};
|
||||||
|
let right_side: Result<&dyn ContextElement, WalkError> =
|
||||||
|
Self::get_rval(breadcrumbs, ¶m_map, "value")
|
||||||
|
.unwrap_or(Err(WalkError::CantWalk));
|
||||||
|
if left_side == right_side {
|
||||||
|
return self.render_maybe_body(
|
||||||
|
¶meterized_block.contents,
|
||||||
|
breadcrumbs,
|
||||||
|
blocks,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return self.render_maybe_body(
|
||||||
|
¶meterized_block.else_contents,
|
||||||
|
breadcrumbs,
|
||||||
|
blocks,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DustTag::DTHelperNotEquals(parameterized_block) => {
|
||||||
|
let param_map: HashMap<&str, &RValue<'a>> =
|
||||||
|
Self::get_rval_map(¶meterized_block.params);
|
||||||
|
// Special case: when comparing two RVPaths, if the
|
||||||
|
// path is equal then dust assumes the values are
|
||||||
|
// equal (otherwise, non-scalar values are
|
||||||
|
// automatically not equal)
|
||||||
|
if Self::are_paths_identical(¶m_map) {
|
||||||
|
return match ¶meterized_block.else_contents {
|
||||||
|
None => Ok("".to_owned()),
|
||||||
|
Some(body) => {
|
||||||
|
let rendered_content = self.render_body(body, breadcrumbs, blocks)?;
|
||||||
|
Ok(rendered_content)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let left_side: Result<&dyn ContextElement, WalkError> =
|
||||||
|
match Self::get_rval(breadcrumbs, ¶m_map, "key") {
|
||||||
|
None => return Ok("".to_owned()),
|
||||||
|
Some(res) => res,
|
||||||
|
};
|
||||||
|
let right_side: Result<&dyn ContextElement, WalkError> =
|
||||||
|
Self::get_rval(breadcrumbs, ¶m_map, "value")
|
||||||
|
.unwrap_or(Err(WalkError::CantWalk));
|
||||||
|
if left_side != right_side {
|
||||||
|
return self.render_maybe_body(
|
||||||
|
¶meterized_block.contents,
|
||||||
|
breadcrumbs,
|
||||||
|
blocks,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return self.render_maybe_body(
|
||||||
|
¶meterized_block.else_contents,
|
||||||
|
breadcrumbs,
|
||||||
|
blocks,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DustTag::DTHelperGreaterThan(parameterized_block) => {
|
||||||
|
let param_map: HashMap<&str, &RValue<'a>> =
|
||||||
|
Self::get_rval_map(¶meterized_block.params);
|
||||||
|
let left_side: Result<&dyn ContextElement, WalkError> =
|
||||||
|
match Self::get_rval(breadcrumbs, ¶m_map, "key") {
|
||||||
|
None => return Ok("".to_owned()),
|
||||||
|
Some(res) => res,
|
||||||
|
};
|
||||||
|
let right_side: Result<&dyn ContextElement, WalkError> =
|
||||||
|
Self::get_rval(breadcrumbs, ¶m_map, "value")
|
||||||
|
.unwrap_or(Err(WalkError::CantWalk));
|
||||||
|
match (left_side, right_side) {
|
||||||
|
(Err(_), _) | (_, Err(_)) => {
|
||||||
|
return self.render_maybe_body(
|
||||||
|
¶meterized_block.else_contents,
|
||||||
|
breadcrumbs,
|
||||||
|
blocks,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(Ok(left_side_unwrapped), Ok(right_side_unwrapped)) => {
|
||||||
|
if left_side_unwrapped > right_side_unwrapped {
|
||||||
|
return self.render_maybe_body(
|
||||||
|
¶meterized_block.contents,
|
||||||
|
breadcrumbs,
|
||||||
|
blocks,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return self.render_maybe_body(
|
||||||
|
¶meterized_block.else_contents,
|
||||||
|
breadcrumbs,
|
||||||
|
blocks,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DustTag::DTHelperGreaterThanOrEquals(parameterized_block) => {
|
||||||
|
let param_map: HashMap<&str, &RValue<'a>> =
|
||||||
|
Self::get_rval_map(¶meterized_block.params);
|
||||||
|
let left_side: Result<&dyn ContextElement, WalkError> =
|
||||||
|
match Self::get_rval(breadcrumbs, ¶m_map, "key") {
|
||||||
|
None => return Ok("".to_owned()),
|
||||||
|
Some(res) => res,
|
||||||
|
};
|
||||||
|
let right_side: Result<&dyn ContextElement, WalkError> =
|
||||||
|
Self::get_rval(breadcrumbs, ¶m_map, "value")
|
||||||
|
.unwrap_or(Err(WalkError::CantWalk));
|
||||||
|
match (left_side, right_side) {
|
||||||
|
(Err(_), _) | (_, Err(_)) => {
|
||||||
|
return self.render_maybe_body(
|
||||||
|
¶meterized_block.else_contents,
|
||||||
|
breadcrumbs,
|
||||||
|
blocks,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(Ok(left_side_unwrapped), Ok(right_side_unwrapped)) => {
|
||||||
|
return match left_side_unwrapped.partial_cmp(&right_side_unwrapped) {
|
||||||
|
Some(Ordering::Greater) | Some(Ordering::Equal) | None => self
|
||||||
|
.render_maybe_body(
|
||||||
|
¶meterized_block.contents,
|
||||||
|
breadcrumbs,
|
||||||
|
blocks,
|
||||||
|
),
|
||||||
|
Some(Ordering::Less) => self.render_maybe_body(
|
||||||
|
¶meterized_block.else_contents,
|
||||||
|
breadcrumbs,
|
||||||
|
blocks,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DustTag::DTHelperLessThan(parameterized_block) => {
|
||||||
|
let param_map: HashMap<&str, &RValue<'a>> =
|
||||||
|
Self::get_rval_map(¶meterized_block.params);
|
||||||
|
let left_side: Result<&dyn ContextElement, WalkError> =
|
||||||
|
match Self::get_rval(breadcrumbs, ¶m_map, "key") {
|
||||||
|
None => return Ok("".to_owned()),
|
||||||
|
Some(res) => res,
|
||||||
|
};
|
||||||
|
let right_side: Result<&dyn ContextElement, WalkError> =
|
||||||
|
Self::get_rval(breadcrumbs, ¶m_map, "value")
|
||||||
|
.unwrap_or(Err(WalkError::CantWalk));
|
||||||
|
match (left_side, right_side) {
|
||||||
|
(Err(_), _) | (_, Err(_)) => {
|
||||||
|
return self.render_maybe_body(
|
||||||
|
¶meterized_block.else_contents,
|
||||||
|
breadcrumbs,
|
||||||
|
blocks,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(Ok(left_side_unwrapped), Ok(right_side_unwrapped)) => {
|
||||||
|
if left_side_unwrapped < right_side_unwrapped {
|
||||||
|
return self.render_maybe_body(
|
||||||
|
¶meterized_block.contents,
|
||||||
|
breadcrumbs,
|
||||||
|
blocks,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return self.render_maybe_body(
|
||||||
|
¶meterized_block.else_contents,
|
||||||
|
breadcrumbs,
|
||||||
|
blocks,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DustTag::DTHelperLessThanOrEquals(parameterized_block) => {
|
||||||
|
let param_map: HashMap<&str, &RValue<'a>> =
|
||||||
|
Self::get_rval_map(¶meterized_block.params);
|
||||||
|
let left_side: Result<&dyn ContextElement, WalkError> =
|
||||||
|
match Self::get_rval(breadcrumbs, ¶m_map, "key") {
|
||||||
|
None => return Ok("".to_owned()),
|
||||||
|
Some(res) => res,
|
||||||
|
};
|
||||||
|
let right_side: Result<&dyn ContextElement, WalkError> =
|
||||||
|
Self::get_rval(breadcrumbs, ¶m_map, "value")
|
||||||
|
.unwrap_or(Err(WalkError::CantWalk));
|
||||||
|
match (left_side, right_side) {
|
||||||
|
(Err(_), _) | (_, Err(_)) => {
|
||||||
|
return self.render_maybe_body(
|
||||||
|
¶meterized_block.else_contents,
|
||||||
|
breadcrumbs,
|
||||||
|
blocks,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(Ok(left_side_unwrapped), Ok(right_side_unwrapped)) => {
|
||||||
|
return match left_side_unwrapped.partial_cmp(&right_side_unwrapped) {
|
||||||
|
Some(Ordering::Less) | Some(Ordering::Equal) | None => self
|
||||||
|
.render_maybe_body(
|
||||||
|
¶meterized_block.contents,
|
||||||
|
breadcrumbs,
|
||||||
|
blocks,
|
||||||
|
),
|
||||||
|
Some(Ordering::Greater) => self.render_maybe_body(
|
||||||
|
¶meterized_block.else_contents,
|
||||||
|
breadcrumbs,
|
||||||
|
blocks,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => (), // TODO: Implement the rest
|
_ => (), // TODO: Implement the rest
|
||||||
}
|
}
|
||||||
Ok("".to_owned())
|
Ok("".to_owned())
|
||||||
@ -232,7 +441,6 @@ impl<'a> DustRenderer<'a> {
|
|||||||
/// If the value is falsey, and therefore should render the else
|
/// If the value is falsey, and therefore should render the else
|
||||||
/// block, this will return an empty vector.
|
/// block, this will return an empty vector.
|
||||||
fn get_loop_elements<'b>(
|
fn get_loop_elements<'b>(
|
||||||
&'a self,
|
|
||||||
walk_result: Result<&'b dyn ContextElement, WalkError>,
|
walk_result: Result<&'b dyn ContextElement, WalkError>,
|
||||||
) -> Vec<&'b dyn ContextElement> {
|
) -> Vec<&'b dyn ContextElement> {
|
||||||
match walk_result {
|
match walk_result {
|
||||||
@ -240,6 +448,41 @@ impl<'a> DustRenderer<'a> {
|
|||||||
Ok(walk_target) => walk_target.get_loop_elements(),
|
Ok(walk_target) => walk_target.get_loop_elements(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn are_paths_identical<'b>(param_map: &HashMap<&str, &RValue<'b>>) -> bool {
|
||||||
|
match (param_map.get("key"), param_map.get("value")) {
|
||||||
|
(None, _) => false,
|
||||||
|
(_, None) => false,
|
||||||
|
(Some(key_rval), Some(value_rval)) => match (key_rval, value_rval) {
|
||||||
|
(RValue::RVPath(key_path), RValue::RVPath(value_path)) => {
|
||||||
|
key_path.keys == value_path.keys
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_rval_map<'b>(params: &'b Vec<KVPair<'b>>) -> HashMap<&'b str, &'b RValue<'b>> {
|
||||||
|
params
|
||||||
|
.iter()
|
||||||
|
.map(|pair: &KVPair<'b>| (pair.key, &pair.value))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_rval<'b>(
|
||||||
|
breadcrumbs: &Vec<&'b dyn ContextElement>,
|
||||||
|
param_map: &HashMap<&str, &'b RValue<'b>>,
|
||||||
|
key: &str,
|
||||||
|
) -> Option<Result<&'b dyn ContextElement, WalkError>> {
|
||||||
|
match param_map.get(key) {
|
||||||
|
None => None,
|
||||||
|
Some(rval) => match rval {
|
||||||
|
RValue::RVString(text) => Some(Ok(text)),
|
||||||
|
RValue::RVPath(path) => Some(walk_path(breadcrumbs, &path.keys)),
|
||||||
|
RValue::RVPositiveInteger(num) => Some(Ok(num)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -249,88 +492,76 @@ mod tests {
|
|||||||
use crate::renderer::context_element::Loopable;
|
use crate::renderer::context_element::Loopable;
|
||||||
use crate::renderer::context_element::Renderable;
|
use crate::renderer::context_element::Renderable;
|
||||||
use crate::renderer::context_element::Walkable;
|
use crate::renderer::context_element::Walkable;
|
||||||
|
use crate::renderer::CompareContextElement;
|
||||||
|
|
||||||
impl ContextElement for u32 {}
|
impl<I: 'static + ContextElement + Clone> ContextElement for HashMap<String, I> {}
|
||||||
impl ContextElement for &str {}
|
|
||||||
impl<I: ContextElement> ContextElement for HashMap<&str, I> {}
|
|
||||||
|
|
||||||
impl Renderable for u32 {
|
impl<I: ContextElement> Renderable for HashMap<String, I> {
|
||||||
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
|
|
||||||
// TODO: handle the filters
|
|
||||||
Ok(self.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Renderable for &str {
|
|
||||||
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
|
|
||||||
// TODO: handle the filters
|
|
||||||
Ok(self.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I: ContextElement> Renderable for HashMap<&str, I> {
|
|
||||||
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
|
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
|
||||||
// TODO: handle the filters
|
// TODO: handle the filters
|
||||||
Ok("[object Object]".to_owned())
|
Ok("[object Object]".to_owned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: ContextElement> Walkable for HashMap<&str, I> {
|
impl<I: ContextElement> Walkable for HashMap<String, I> {
|
||||||
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
|
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
|
||||||
let child = self.get(segment).ok_or(WalkError::CantWalk)?;
|
let child = self.get(segment).ok_or(WalkError::CantWalk)?;
|
||||||
Ok(child)
|
Ok(child)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Walkable for &str {
|
impl<I: 'static + ContextElement + Clone> Loopable for HashMap<String, I> {
|
||||||
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
|
|
||||||
Err(WalkError::CantWalk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Walkable for u32 {
|
|
||||||
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
|
|
||||||
Err(WalkError::CantWalk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Loopable for &str {
|
|
||||||
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
|
|
||||||
if self.is_empty() {
|
|
||||||
Vec::new()
|
|
||||||
} else {
|
|
||||||
vec![self]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Loopable for u32 {
|
|
||||||
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
|
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
|
||||||
vec![self]
|
vec![self]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: ContextElement> Loopable for HashMap<&str, I> {
|
impl<I: 'static + ContextElement + Clone> CompareContextElement for HashMap<String, I> {
|
||||||
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
|
fn equals(&self, other: &dyn ContextElement) -> bool {
|
||||||
vec![self]
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_walk_path() {
|
fn test_walk_path() {
|
||||||
let context: HashMap<&str, &str> =
|
let context: HashMap<String, String> = [
|
||||||
[("cat", "kitty"), ("dog", "doggy"), ("tiger", "murderkitty")]
|
("cat".to_string(), "kitty".to_string()),
|
||||||
.iter()
|
("dog".to_string(), "doggy".to_string()),
|
||||||
.cloned()
|
("tiger".to_string(), "murderkitty".to_string()),
|
||||||
.collect();
|
]
|
||||||
let number_context: HashMap<&str, u32> = [("cat", 1), ("dog", 2), ("tiger", 3)]
|
.iter()
|
||||||
.iter()
|
.cloned()
|
||||||
.cloned()
|
.collect();
|
||||||
.collect();
|
let number_context: HashMap<String, u64> = [
|
||||||
let deep_context: HashMap<&str, HashMap<&str, &str>> = [
|
("cat".to_string(), 1),
|
||||||
("cat", [("food", "meat")].iter().cloned().collect()),
|
("dog".to_string(), 2),
|
||||||
("dog", [("food", "meat")].iter().cloned().collect()),
|
("tiger".to_string(), 3),
|
||||||
("tiger", [("food", "people")].iter().cloned().collect()),
|
]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
let deep_context: HashMap<String, HashMap<String, String>> = [
|
||||||
|
(
|
||||||
|
"cat".to_string(),
|
||||||
|
[("food".to_string(), "meat".to_string())]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"dog".to_string(),
|
||||||
|
[("food".to_string(), "meat".to_string())]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"tiger".to_string(),
|
||||||
|
[("food".to_string(), "people".to_string())]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
|
@ -51,3 +51,23 @@ pub fn walk_path<'a>(
|
|||||||
}
|
}
|
||||||
Err(WalkError::CantWalk)
|
Err(WalkError::CantWalk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn owned_walk_path<'a>(
|
||||||
|
breadcrumbs: &'a Vec<Box<dyn ContextElement>>,
|
||||||
|
path: &Vec<String>,
|
||||||
|
) -> Result<&'a dyn ContextElement, WalkError> {
|
||||||
|
let path_reference: Vec<&str> = path.iter().map(|p| &p[..]).collect();
|
||||||
|
for context in breadcrumbs.iter().rev() {
|
||||||
|
match walk_path_from_single_level(context.as_ref(), &path_reference) {
|
||||||
|
// If no walking was done at all, keep looping
|
||||||
|
WalkResult::NoWalk => {}
|
||||||
|
// If we partially walked then stop trying to find
|
||||||
|
// anything
|
||||||
|
WalkResult::PartialWalk => {
|
||||||
|
return Err(WalkError::CantWalk);
|
||||||
|
}
|
||||||
|
WalkResult::FullyWalked(new_context) => return Ok(new_context),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(WalkError::CantWalk)
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user