From 012028b0bcb1db40585a996a57b1da3ed7415a1f Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 13:43:32 -0400 Subject: [PATCH 01/63] Integrate the dustjs official helpers into the test framework. --- Dockerfile | 4 ++-- js/dustjs_shim.js | 11 ++++++----- js/test_cases/helpers_eq/input1.json | 6 ++++++ js/test_cases/helpers_eq/main.dust | 10 ++++++++++ 4 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 js/test_cases/helpers_eq/input1.json create mode 100644 js/test_cases/helpers_eq/main.dust diff --git a/Dockerfile b/Dockerfile index f4fbe9a..0bb15a1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ RUN addgroup -S duster && adduser -S duster -G duster # Install LinkedIn DustJS. Installing it globally before copying in # the repo to avoid spamming the npm servers with every codebase # change. -RUN npm install -g dustjs-linkedin +RUN npm install -g dustjs-linkedin dustjs-helpers # Copy repo into duster user's directory @@ -22,5 +22,5 @@ RUN chown -R duster:duster /home/duster/duster USER duster RUN git clean -dfxq RUN cargo build -RUN npm link dustjs-linkedin +RUN npm link dustjs-linkedin dustjs-helpers ENTRYPOINT ["/home/duster/duster/js/run_compliance_suite.bash"] diff --git a/js/dustjs_shim.js b/js/dustjs_shim.js index 4e02180..c38f06d 100644 --- a/js/dustjs_shim.js +++ b/js/dustjs_shim.js @@ -1,20 +1,21 @@ -var dust = require('dustjs-linkedin'); -var fs = require('fs'); -const path = require('path'); +// var dust = require("dustjs-linkedin"); +var dust = require("dustjs-helpers"); +var fs = require("fs"); +const path = require("path"); var argv = process.argv.slice(2); if (argv.length < 1) { console.error("Expecting only 1 argument (a path to a template)"); 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"]; for (var i = 0, len = argv.length; i < len; ++i) { var filename = path.parse(argv[i])["name"]; try { - var template_source = fs.readFileSync(argv[i], 'utf-8'); + var template_source = fs.readFileSync(argv[i], "utf-8"); } catch (err) { console.error(err); process.exit(1); diff --git a/js/test_cases/helpers_eq/input1.json b/js/test_cases/helpers_eq/input1.json new file mode 100644 index 0000000..0ed8d16 --- /dev/null +++ b/js/test_cases/helpers_eq/input1.json @@ -0,0 +1,6 @@ +{ + "str": "master", + "int": 7, + "alpha": 21, + "beta": "21" +} diff --git a/js/test_cases/helpers_eq/main.dust b/js/test_cases/helpers_eq/main.dust new file mode 100644 index 0000000..37a9de8 --- /dev/null +++ b/js/test_cases/helpers_eq/main.dust @@ -0,0 +1,10 @@ +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} From 7a8247f38acdde1112d8b1de5b2b6c599183f50b Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 14:22:59 -0400 Subject: [PATCH 02/63] Getting the left and right sides. --- js/test_cases/helpers_eq/README.md | 5 +++++ js/test_cases/helpers_eq/main.dust | 5 +++++ src/parser/parser.rs | 8 ++++---- src/renderer/renderer.rs | 24 ++++++++++++++++++++++++ 4 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 js/test_cases/helpers_eq/README.md diff --git a/js/test_cases/helpers_eq/README.md b/js/test_cases/helpers_eq/README.md new file mode 100644 index 0000000..584c2f5 --- /dev/null +++ b/js/test_cases/helpers_eq/README.md @@ -0,0 +1,5 @@ +Without a key parameter, neither the main block nor the else block is rendered. + +Without a value parameter, it probably uses "null" but I haven't yet confirmed that with additional testing. Potentially could also just be making all falsey values equal. + +Literal values work in both keys and values. diff --git a/js/test_cases/helpers_eq/main.dust b/js/test_cases/helpers_eq/main.dust index 37a9de8..3d2c60b 100644 --- a/js/test_cases/helpers_eq/main.dust +++ b/js/test_cases/helpers_eq/main.dust @@ -8,3 +8,8 @@ beta is {beta}{~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} diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 768ec88..538e159 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -106,10 +106,10 @@ pub struct NamedBlock<'a> { #[derive(Clone, Debug, PartialEq)] pub struct ParameterizedBlock<'a> { - name: &'a str, - params: Vec>, - contents: Option>, - else_contents: Option>, + pub name: &'a str, + pub params: Vec>, + pub contents: Option>, + pub else_contents: Option>, } #[derive(Clone, Debug, PartialEq)] diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index e6b7bd4..a528662 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -1,6 +1,8 @@ use crate::parser::template; use crate::parser::Body; use crate::parser::DustTag; +use crate::parser::KVPair; +use crate::parser::RValue; use crate::parser::Special; use crate::parser::Template; use crate::parser::TemplateElement; @@ -222,6 +224,28 @@ impl<'a> DustRenderer<'a> { }, }; } + DustTag::DTHelperEquals(parameterized_block) => { + let param_map: HashMap<&'a str, &'a RValue<'a>> = parameterized_block + .params + .iter() + .map(|pair: &KVPair<'a>| (pair.key, &pair.value)) + .collect(); + let left_side: Result<&dyn ContextElement, WalkError> = match param_map.get("key") { + None => return Ok("".to_owned()), + Some(rval) => match rval { + RValue::RVString(text) => Ok(text), + RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys), + }, + }; + let right_side: Result<&dyn ContextElement, WalkError> = + match param_map.get("value") { + None => Err(WalkError::CantWalk), + Some(rval) => match rval { + RValue::RVString(text) => Ok(text), + RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys), + }, + }; + } _ => (), // TODO: Implement the rest } Ok("".to_owned()) From c8cf6a78a0346db3ce85e46eba1981a5df11bf36 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 14:33:17 -0400 Subject: [PATCH 03/63] Extending the equality helper test. --- js/test_cases/helpers_eq/README.md | 9 +++++++-- js/test_cases/helpers_eq/input1.json | 3 ++- js/test_cases/helpers_eq/main.dust | 3 +++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/js/test_cases/helpers_eq/README.md b/js/test_cases/helpers_eq/README.md index 584c2f5..42d393a 100644 --- a/js/test_cases/helpers_eq/README.md +++ b/js/test_cases/helpers_eq/README.md @@ -1,5 +1,10 @@ Without a key parameter, neither the main block nor the else block is rendered. -Without a value parameter, it probably uses "null" but I haven't yet confirmed that with additional testing. Potentially could also just be making all falsey values equal. - 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. diff --git a/js/test_cases/helpers_eq/input1.json b/js/test_cases/helpers_eq/input1.json index 0ed8d16..ece58d2 100644 --- a/js/test_cases/helpers_eq/input1.json +++ b/js/test_cases/helpers_eq/input1.json @@ -2,5 +2,6 @@ "str": "master", "int": 7, "alpha": 21, - "beta": "21" + "beta": "21", + "null": null } diff --git a/js/test_cases/helpers_eq/main.dust b/js/test_cases/helpers_eq/main.dust index 3d2c60b..8126b32 100644 --- a/js/test_cases/helpers_eq/main.dust +++ b/js/test_cases/helpers_eq/main.dust @@ -13,3 +13,6 @@ beta is {beta}{~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 if false{/eq}{~n} From 7e0e77648682a3b9173bb33c4ff22e2c14744cf8 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 14:53:12 -0400 Subject: [PATCH 04/63] Running into lifetime issues. --- src/renderer/context_element.rs | 6 ++++++ src/renderer/errors.rs | 1 + src/renderer/renderer.rs | 10 +++++++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index dee6680..2fb1a17 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -25,3 +25,9 @@ pub trait Loopable { /// for each element of the array. fn get_loop_elements(&self) -> Vec<&dyn ContextElement>; } + +impl PartialEq for dyn ContextElement { + fn eq(&self, other: &dyn ContextElement) -> bool { + todo!() + } +} diff --git a/src/renderer/errors.rs b/src/renderer/errors.rs index 5a38068..7dac064 100644 --- a/src/renderer/errors.rs +++ b/src/renderer/errors.rs @@ -9,6 +9,7 @@ pub enum RenderError { TemplateNotFound(String), } +#[derive(PartialEq)] pub enum WalkError { CantWalk, } diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index a528662..4e3d9f8 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -225,7 +225,7 @@ impl<'a> DustRenderer<'a> { }; } DustTag::DTHelperEquals(parameterized_block) => { - let param_map: HashMap<&'a str, &'a RValue<'a>> = parameterized_block + let param_map: HashMap<&str, &RValue<'a>> = parameterized_block .params .iter() .map(|pair: &KVPair<'a>| (pair.key, &pair.value)) @@ -245,6 +245,14 @@ impl<'a> DustRenderer<'a> { RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys), }, }; + // let x = WalkError::CantWalk; + // let y = WalkError::CantWalk; + // if x == y { + // panic!("placeholder"); + // } + // if left_side.unwrap() == right_side.unwrap() { + // panic!("placeholder"); + // } } _ => (), // TODO: Implement the rest } From c438653449e2b587e34db353d89d47679ca2ae29 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 15:02:59 -0400 Subject: [PATCH 05/63] No luck. --- src/renderer/context_element.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index 2fb1a17..d49c562 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -26,8 +26,8 @@ pub trait Loopable { fn get_loop_elements(&self) -> Vec<&dyn ContextElement>; } -impl PartialEq for dyn ContextElement { - fn eq(&self, other: &dyn ContextElement) -> bool { - todo!() - } -} +// impl PartialEq for dyn ContextElement { +// fn eq(&self, other: &dyn ContextElement) -> bool { +// todo!() +// } +// } From f386e5c31b820eb2f92aa1128d2ec237612a8b13 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 17:12:15 -0400 Subject: [PATCH 06/63] Adding trait to cast to Any. --- src/renderer/context_element.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index d49c562..f00f56c 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -1,6 +1,7 @@ use crate::parser::Filter; use crate::renderer::errors::RenderError; use crate::renderer::errors::WalkError; +use std::any::Any; use std::fmt::Debug; pub trait ContextElement: Debug + Walkable + Renderable + Loopable {} @@ -26,6 +27,10 @@ pub trait Loopable { fn get_loop_elements(&self) -> Vec<&dyn ContextElement>; } +pub trait CompareContextElement { + fn to_any(&self) -> &dyn Any; +} + // impl PartialEq for dyn ContextElement { // fn eq(&self, other: &dyn ContextElement) -> bool { // todo!() From 876bced18846b836921144b7ba7462d7c4acd5a7 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 18:10:17 -0400 Subject: [PATCH 07/63] Converting RValues. --- src/renderer/parameters_context.rs | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index c2aa6d0..289afba 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -9,6 +9,41 @@ use crate::renderer::WalkError; use crate::renderer::Walkable; use std::collections::HashMap; +/// Copy the data from an RValue to an Owned struct +/// +/// In order to get comparisons to work for our `ContextElement` trait +/// 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. +pub enum OwnedRValue { + RVPath(OwnedPath), + RVString(String), +} + +pub struct OwnedPath { + pub keys: Vec, +} + +impl From> for OwnedRValue { + fn from(original: RValue<'_>) -> Self { + match original { + RValue::RVString(text) => OwnedRValue::RVString(text.to_owned()), + RValue::RVPath(path) => OwnedRValue::RVPath(OwnedPath { + keys: path.keys.iter().map(|k| k.to_string()).collect(), + }), + } + } +} + +pub struct NewParametersContext { + params: HashMap, +} + #[derive(Clone, Debug)] pub struct ParametersContext<'a> { params: HashMap<&'a str, &'a RValue<'a>>, From 9573480973ae71ed6db51e445c4f5eb03eeacbf3 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 18:19:06 -0400 Subject: [PATCH 08/63] converting the parameters --- src/renderer/parameters_context.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 289afba..60b98d3 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -29,8 +29,8 @@ pub struct OwnedPath { pub keys: Vec, } -impl From> for OwnedRValue { - fn from(original: RValue<'_>) -> Self { +impl From<&RValue<'_>> for OwnedRValue { + fn from(original: &RValue<'_>) -> Self { match original { RValue::RVString(text) => OwnedRValue::RVString(text.to_owned()), RValue::RVPath(path) => OwnedRValue::RVPath(OwnedPath { @@ -42,6 +42,24 @@ impl From> for OwnedRValue { pub struct NewParametersContext { params: HashMap, + breadcrumbs: Vec>, +} + +impl NewParametersContext { + pub fn new( + breadcrumbs: &Vec<&dyn ContextElement>, + params: &Vec, + ) -> NewParametersContext { + let owned_params: HashMap = params + .iter() + .map(|kvpair| (kvpair.key.to_string(), OwnedRValue::from(&kvpair.value))) + .collect(); + + NewParametersContext { + params: owned_params, + breadcrumbs: Vec::new(), + } + } } #[derive(Clone, Debug)] From b9cfa56c2fa7a99b4fd70c477ec479dfadcfd356 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 18:35:24 -0400 Subject: [PATCH 09/63] May have to use unsafe code. --- src/renderer/parameters_context.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 60b98d3..2453d4a 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -55,6 +55,13 @@ impl NewParametersContext { .map(|kvpair| (kvpair.key.to_string(), OwnedRValue::from(&kvpair.value))) .collect(); + let x: String = "foo".to_owned(); + let y: &dyn ContextElement = &x as _; + let owned_y: Box = Box::new(*y.clone()); + + // let owned_breadcrumbs: Vec> = + // breadcrumbs.iter().map(|ce| Box::new(*ce.clone())).collect(); + NewParametersContext { params: owned_params, breadcrumbs: Vec::new(), From 6fb329388f55bae9dd174c1fc57dc19544ffbe06 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 18:45:54 -0400 Subject: [PATCH 10/63] Not having much luck. --- src/renderer/parameters_context.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 2453d4a..cf8feb3 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -57,7 +57,11 @@ impl NewParametersContext { let x: String = "foo".to_owned(); let y: &dyn ContextElement = &x as _; - let owned_y: Box = Box::new(*y.clone()); + // let owned_y: Box = Box::new(*y.clone()); + // unsafe { + // let ce = &mut *y.clone() as *mut dyn ContextElement; + // Box::from_raw(ce); + // } // let owned_breadcrumbs: Vec> = // breadcrumbs.iter().map(|ce| Box::new(*ce.clone())).collect(); From 5c79b436a0b8716861b27a02c5e116f7ac5d29b8 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 19:01:02 -0400 Subject: [PATCH 11/63] Commenting out the parameterscontext type. --- src/renderer/context_element.rs | 15 ++++- src/renderer/parameters_context.rs | 94 +++++++++++++++--------------- src/renderer/renderer.rs | 6 +- 3 files changed, 65 insertions(+), 50 deletions(-) diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index f00f56c..4440e31 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -4,7 +4,10 @@ use crate::renderer::errors::WalkError; use std::any::Any; use std::fmt::Debug; -pub trait ContextElement: Debug + Walkable + Renderable + Loopable {} +pub trait ContextElement: + Debug + Walkable + Renderable + Loopable + IntoBoxedContextElement +{ +} pub trait Walkable { fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError>; @@ -31,6 +34,16 @@ pub trait CompareContextElement { fn to_any(&self) -> &dyn Any; } +pub trait IntoBoxedContextElement { + fn to_box(self) -> Box; +} + +impl IntoBoxedContextElement for C { + fn to_box(self) -> Box { + Box::new(self) + } +} + // impl PartialEq for dyn ContextElement { // fn eq(&self, other: &dyn ContextElement) -> bool { // todo!() diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index cf8feb3..f69314d 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -1,6 +1,7 @@ use crate::parser::KVPair; use crate::parser::{Filter, RValue}; use crate::renderer::context_element::ContextElement; +use crate::renderer::context_element::IntoBoxedContextElement; use crate::renderer::walking::walk_path; use crate::renderer::Loopable; use crate::renderer::RenderError; @@ -57,6 +58,7 @@ impl NewParametersContext { let x: String = "foo".to_owned(); let y: &dyn ContextElement = &x as _; + let z: Box = y.clone().to_box(); // let owned_y: Box = Box::new(*y.clone()); // unsafe { // let ce = &mut *y.clone() as *mut dyn ContextElement; @@ -73,57 +75,57 @@ impl NewParametersContext { } } -#[derive(Clone, Debug)] -pub struct ParametersContext<'a> { - params: HashMap<&'a str, &'a RValue<'a>>, - breadcrumbs: &'a Vec<&'a dyn ContextElement>, -} +// #[derive(Clone, Debug)] +// pub struct ParametersContext<'a> { +// params: HashMap<&'a str, &'a RValue<'a>>, +// breadcrumbs: &'a Vec<&'a dyn ContextElement>, +// } -impl<'a> ParametersContext<'a> { - pub fn new( - breadcrumbs: &'a Vec<&'a dyn ContextElement>, - params: &'a Vec>, - ) -> ParametersContext<'a> { - let param_map = params - .iter() - .map(|pair: &KVPair<'a>| (pair.key, &pair.value)) - .collect(); - ParametersContext { - params: param_map, - breadcrumbs: breadcrumbs, - } - } -} +// impl<'a> ParametersContext<'a> { +// pub fn new( +// breadcrumbs: &'a Vec<&'a dyn ContextElement>, +// params: &'a Vec>, +// ) -> ParametersContext<'a> { +// let param_map = params +// .iter() +// .map(|pair: &KVPair<'a>| (pair.key, &pair.value)) +// .collect(); +// ParametersContext { +// params: param_map, +// breadcrumbs: breadcrumbs, +// } +// } +// } -impl<'a> ContextElement for ParametersContext<'a> {} +// impl<'a> ContextElement for ParametersContext<'a> {} -impl<'a> Renderable for ParametersContext<'a> { - fn render(&self, _filters: &Vec) -> Result { - // TODO: Would this even ever be called? Won't matter, but I'd - // like to know. Since it is injected 1 above the current - // context, we wouldn't be able to access it with `{.}`. - Ok("[object Object]".to_owned()) - } -} +// impl<'a> Renderable for ParametersContext<'a> { +// fn render(&self, _filters: &Vec) -> Result { +// // TODO: Would this even ever be called? Won't matter, but I'd +// // like to know. Since it is injected 1 above the current +// // context, we wouldn't be able to access it with `{.}`. +// Ok("[object Object]".to_owned()) +// } +// } -impl<'a> Loopable for ParametersContext<'a> { - fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { - // TODO: Would this even ever be called? Won't matter, but I'd - // like to know. Since it is injected 1 above the current - // context, we wouldn't be able to access it with `{.}`. - vec![self] - } -} +// impl<'a> Loopable for ParametersContext<'a> { +// fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { +// // TODO: Would this even ever be called? Won't matter, but I'd +// // like to know. Since it is injected 1 above the current +// // context, we wouldn't be able to access it with `{.}`. +// vec![self] +// } +// } -impl<'a> Walkable for ParametersContext<'a> { - fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> { - let rval = self.params.get(segment).ok_or(WalkError::CantWalk)?; - match rval { - RValue::RVPath(path) => walk_path(self.breadcrumbs, &path.keys), - RValue::RVString(text) => Ok(text), - } - } -} +// impl<'a> Walkable for ParametersContext<'a> { +// fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> { +// let rval = self.params.get(segment).ok_or(WalkError::CantWalk)?; +// match rval { +// RValue::RVPath(path) => walk_path(self.breadcrumbs, &path.keys), +// RValue::RVString(text) => Ok(text), +// } +// } +// } impl ContextElement for String {} diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 4e3d9f8..76e6317 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -12,7 +12,7 @@ use crate::renderer::errors::RenderError; use crate::renderer::errors::WalkError; use crate::renderer::inline_partial_tree::extract_inline_partials; 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 std::collections::HashMap; @@ -194,9 +194,9 @@ impl<'a> DustRenderer<'a> { self.render_template(&partial.name, breadcrumbs, Some(blocks))?; return Ok(rendered_content); } else { - let injected_context = ParametersContext::new(breadcrumbs, &partial.params); + // let injected_context = ParametersContext::new(breadcrumbs, &partial.params); let mut new_breadcrumbs = breadcrumbs.clone(); - new_breadcrumbs.insert(new_breadcrumbs.len() - 1, &injected_context); + // new_breadcrumbs.insert(new_breadcrumbs.len() - 1, &injected_context); let rendered_content = self.render_template(&partial.name, &new_breadcrumbs, Some(blocks))?; return Ok(rendered_content); From c96b2257d7b10540328c6ca3ba8552b001610608 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 19:07:41 -0400 Subject: [PATCH 12/63] Compiling. Need to see if I can do a generic implemented of IntoBoxedContextElement for all objects implementing Copy, and I need to implement the compare code to make sure this all works before I start integrating this more. --- src/bin.rs | 7 +++++++ src/renderer/context_element.rs | 8 +------- src/renderer/mod.rs | 1 + src/renderer/parameters_context.rs | 22 +++++++++------------- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index 12e1bd3..1f8fcd7 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -1,5 +1,6 @@ extern crate nom; +use crate::renderer::IntoBoxedContextElement; use parser::Filter; use renderer::compile_template; use renderer::CompiledTemplate; @@ -137,3 +138,9 @@ impl Loopable for serde_json::Value { } } } + +impl IntoBoxedContextElement for serde_json::Value { + fn clone_to_box(&self) -> Box { + Box::new(self.clone()) + } +} diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index 4440e31..7c53e8f 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -35,13 +35,7 @@ pub trait CompareContextElement { } pub trait IntoBoxedContextElement { - fn to_box(self) -> Box; -} - -impl IntoBoxedContextElement for C { - fn to_box(self) -> Box { - Box::new(self) - } + fn clone_to_box(&self) -> Box; } // impl PartialEq for dyn ContextElement { diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 70f423b..946f770 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -8,6 +8,7 @@ mod renderer; mod walking; pub use context_element::ContextElement; +pub use context_element::IntoBoxedContextElement; pub use context_element::Loopable; pub use context_element::Renderable; pub use context_element::Walkable; diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index f69314d..5f4c6a8 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -55,22 +55,12 @@ impl NewParametersContext { .iter() .map(|kvpair| (kvpair.key.to_string(), OwnedRValue::from(&kvpair.value))) .collect(); - - let x: String = "foo".to_owned(); - let y: &dyn ContextElement = &x as _; - let z: Box = y.clone().to_box(); - // let owned_y: Box = Box::new(*y.clone()); - // unsafe { - // let ce = &mut *y.clone() as *mut dyn ContextElement; - // Box::from_raw(ce); - // } - - // let owned_breadcrumbs: Vec> = - // breadcrumbs.iter().map(|ce| Box::new(*ce.clone())).collect(); + let owned_breadcrumbs: Vec> = + breadcrumbs.iter().map(|ce| ce.clone_to_box()).collect(); NewParametersContext { params: owned_params, - breadcrumbs: Vec::new(), + breadcrumbs: owned_breadcrumbs, } } } @@ -150,3 +140,9 @@ impl Walkable for String { Err(WalkError::CantWalk) } } + +impl IntoBoxedContextElement for String { + fn clone_to_box(&self) -> Box { + Box::new(self.clone()) + } +} From 8fd2a9cf39adbfe7a643c51b150f02ab047d90de Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 19:16:55 -0400 Subject: [PATCH 13/63] tentatively seems to be working. --- src/bin.rs | 15 +++++++++++++++ src/renderer/context_element.rs | 4 +++- src/renderer/mod.rs | 1 + src/renderer/parameters_context.rs | 15 +++++++++++++++ src/renderer/renderer.rs | 3 +++ 5 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/bin.rs b/src/bin.rs index 1f8fcd7..d6f9f64 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -1,5 +1,6 @@ extern crate nom; +use crate::renderer::CompareContextElement; use crate::renderer::IntoBoxedContextElement; use parser::Filter; use renderer::compile_template; @@ -11,6 +12,7 @@ use renderer::RenderError; use renderer::Renderable; use renderer::WalkError; use renderer::Walkable; +use std::any::Any; use std::env; use std::fs; use std::io::{self, Read}; @@ -144,3 +146,16 @@ impl IntoBoxedContextElement for serde_json::Value { Box::new(self.clone()) } } + +impl CompareContextElement for serde_json::Value { + fn to_any(&self) -> &dyn Any { + self + } + + fn equals(&self, other: &dyn ContextElement) -> bool { + other + .to_any() + .downcast_ref::() + .map_or(false, |a| self == a) + } +} diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index 7c53e8f..c201d4e 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -5,7 +5,7 @@ use std::any::Any; use std::fmt::Debug; pub trait ContextElement: - Debug + Walkable + Renderable + Loopable + IntoBoxedContextElement + Debug + Walkable + Renderable + Loopable + IntoBoxedContextElement + CompareContextElement { } @@ -32,6 +32,8 @@ pub trait Loopable { pub trait CompareContextElement { fn to_any(&self) -> &dyn Any; + + fn equals(&self, other: &dyn ContextElement) -> bool; } pub trait IntoBoxedContextElement { diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 946f770..82a97e6 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -7,6 +7,7 @@ mod parameters_context; mod renderer; mod walking; +pub use context_element::CompareContextElement; pub use context_element::ContextElement; pub use context_element::IntoBoxedContextElement; pub use context_element::Loopable; diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 5f4c6a8..6cdf05b 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -1,5 +1,6 @@ use crate::parser::KVPair; use crate::parser::{Filter, RValue}; +use crate::renderer::context_element::CompareContextElement; use crate::renderer::context_element::ContextElement; use crate::renderer::context_element::IntoBoxedContextElement; use crate::renderer::walking::walk_path; @@ -8,6 +9,7 @@ use crate::renderer::RenderError; use crate::renderer::Renderable; use crate::renderer::WalkError; use crate::renderer::Walkable; +use std::any::Any; use std::collections::HashMap; /// Copy the data from an RValue to an Owned struct @@ -146,3 +148,16 @@ impl IntoBoxedContextElement for String { Box::new(self.clone()) } } + +impl CompareContextElement for String { + fn to_any(&self) -> &dyn Any { + self + } + + fn equals(&self, other: &dyn ContextElement) -> bool { + other + .to_any() + .downcast_ref::() + .map_or(false, |a| self == a) + } +} diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 76e6317..4f39135 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -245,6 +245,9 @@ impl<'a> DustRenderer<'a> { RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys), }, }; + if left_side.unwrap().equals(right_side.unwrap()) { + panic!("testing"); + } // let x = WalkError::CantWalk; // let y = WalkError::CantWalk; // if x == y { From e986a1ba7acc299b2f758be73b763974aab2af24 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 20:58:47 -0400 Subject: [PATCH 14/63] PartialEq compiling with double lifetimes. --- src/renderer/context_element.rs | 10 +++++----- src/renderer/renderer.rs | 5 ++++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index c201d4e..823534a 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -40,8 +40,8 @@ pub trait IntoBoxedContextElement { fn clone_to_box(&self) -> Box; } -// impl PartialEq for dyn ContextElement { -// fn eq(&self, other: &dyn ContextElement) -> bool { -// todo!() -// } -// } +impl<'a, 'b> PartialEq<&'b dyn ContextElement> for &'a dyn ContextElement { + fn eq(&self, other: &&'b dyn ContextElement) -> bool { + false + } +} diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 4f39135..835cf2d 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -245,7 +245,10 @@ impl<'a> DustRenderer<'a> { RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys), }, }; - if left_side.unwrap().equals(right_side.unwrap()) { + // if left_side.unwrap().equals(right_side.unwrap()) { + // panic!("testing"); + // } + if left_side.unwrap() == right_side.unwrap() { panic!("testing"); } // let x = WalkError::CantWalk; From 7f896855376aef9a5fcdcd6b41d9599529b64ed6 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 21:00:06 -0400 Subject: [PATCH 15/63] PartialEq implemented too. --- src/renderer/context_element.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index 823534a..6279a58 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -42,6 +42,6 @@ pub trait IntoBoxedContextElement { impl<'a, 'b> PartialEq<&'b dyn ContextElement> for &'a dyn ContextElement { fn eq(&self, other: &&'b dyn ContextElement) -> bool { - false + self.equals(*other) } } From ba79369b5aab4a7b970745ccee65fa0e2bcac435 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 21:00:52 -0400 Subject: [PATCH 16/63] PartialEq works with results too. --- src/renderer/renderer.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 835cf2d..4b96703 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -248,7 +248,10 @@ impl<'a> DustRenderer<'a> { // if left_side.unwrap().equals(right_side.unwrap()) { // panic!("testing"); // } - if left_side.unwrap() == right_side.unwrap() { + // if left_side.unwrap() == right_side.unwrap() { + // panic!("testing"); + // } + if left_side == right_side { panic!("testing"); } // let x = WalkError::CantWalk; From 256051220d3a0b8ea2b0ac1389d7fe3a043afd46 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 21:07:31 -0400 Subject: [PATCH 17/63] Generic implementation of CloneIntoBoxedContextElement. --- src/bin.rs | 8 +------- src/renderer/context_element.rs | 10 ++++++++-- src/renderer/mod.rs | 2 +- src/renderer/parameters_context.rs | 8 +------- src/renderer/renderer.rs | 14 -------------- 5 files changed, 11 insertions(+), 31 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index d6f9f64..3f669d4 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -1,7 +1,7 @@ extern crate nom; +use crate::renderer::CloneIntoBoxedContextElement; use crate::renderer::CompareContextElement; -use crate::renderer::IntoBoxedContextElement; use parser::Filter; use renderer::compile_template; use renderer::CompiledTemplate; @@ -141,12 +141,6 @@ impl Loopable for serde_json::Value { } } -impl IntoBoxedContextElement for serde_json::Value { - fn clone_to_box(&self) -> Box { - Box::new(self.clone()) - } -} - impl CompareContextElement for serde_json::Value { fn to_any(&self) -> &dyn Any { self diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index 6279a58..a9ab8bd 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -5,7 +5,7 @@ use std::any::Any; use std::fmt::Debug; pub trait ContextElement: - Debug + Walkable + Renderable + Loopable + IntoBoxedContextElement + CompareContextElement + Debug + Walkable + Renderable + Loopable + CloneIntoBoxedContextElement + CompareContextElement { } @@ -36,10 +36,16 @@ pub trait CompareContextElement { fn equals(&self, other: &dyn ContextElement) -> bool; } -pub trait IntoBoxedContextElement { +pub trait CloneIntoBoxedContextElement { fn clone_to_box(&self) -> Box; } +impl CloneIntoBoxedContextElement for C { + fn clone_to_box(&self) -> Box { + Box::new(self.clone()) + } +} + impl<'a, 'b> PartialEq<&'b dyn ContextElement> for &'a dyn ContextElement { fn eq(&self, other: &&'b dyn ContextElement) -> bool { self.equals(*other) diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 82a97e6..45dbdfd 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -7,9 +7,9 @@ mod parameters_context; mod renderer; mod walking; +pub use context_element::CloneIntoBoxedContextElement; pub use context_element::CompareContextElement; pub use context_element::ContextElement; -pub use context_element::IntoBoxedContextElement; pub use context_element::Loopable; pub use context_element::Renderable; pub use context_element::Walkable; diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 6cdf05b..ca421bc 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -1,8 +1,8 @@ use crate::parser::KVPair; use crate::parser::{Filter, RValue}; +use crate::renderer::context_element::CloneIntoBoxedContextElement; use crate::renderer::context_element::CompareContextElement; use crate::renderer::context_element::ContextElement; -use crate::renderer::context_element::IntoBoxedContextElement; use crate::renderer::walking::walk_path; use crate::renderer::Loopable; use crate::renderer::RenderError; @@ -143,12 +143,6 @@ impl Walkable for String { } } -impl IntoBoxedContextElement for String { - fn clone_to_box(&self) -> Box { - Box::new(self.clone()) - } -} - impl CompareContextElement for String { fn to_any(&self) -> &dyn Any { self diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 4b96703..a5309f3 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -245,23 +245,9 @@ impl<'a> DustRenderer<'a> { RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys), }, }; - // if left_side.unwrap().equals(right_side.unwrap()) { - // panic!("testing"); - // } - // if left_side.unwrap() == right_side.unwrap() { - // panic!("testing"); - // } if left_side == right_side { panic!("testing"); } - // let x = WalkError::CantWalk; - // let y = WalkError::CantWalk; - // if x == y { - // panic!("placeholder"); - // } - // if left_side.unwrap() == right_side.unwrap() { - // panic!("placeholder"); - // } } _ => (), // TODO: Implement the rest } From 32abe41e1a3775bff71a620ae02367aff8b30499 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 21:10:48 -0400 Subject: [PATCH 18/63] Generic implementation of CompareContextElement. --- src/bin.rs | 13 ------------- src/renderer/context_element.rs | 13 +++++++++++++ src/renderer/parameters_context.rs | 13 ------------- 3 files changed, 13 insertions(+), 26 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index 3f669d4..661670d 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -140,16 +140,3 @@ impl Loopable for serde_json::Value { } } } - -impl CompareContextElement for serde_json::Value { - fn to_any(&self) -> &dyn Any { - self - } - - fn equals(&self, other: &dyn ContextElement) -> bool { - other - .to_any() - .downcast_ref::() - .map_or(false, |a| self == a) - } -} diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index a9ab8bd..29ca430 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -46,6 +46,19 @@ impl CloneIntoBoxedContextElement for C { } } +impl CompareContextElement for C { + fn to_any(&self) -> &dyn Any { + self + } + + fn equals(&self, other: &dyn ContextElement) -> bool { + other + .to_any() + .downcast_ref::() + .map_or(false, |a| self == a) + } +} + impl<'a, 'b> PartialEq<&'b dyn ContextElement> for &'a dyn ContextElement { fn eq(&self, other: &&'b dyn ContextElement) -> bool { self.equals(*other) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index ca421bc..e9dd02c 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -142,16 +142,3 @@ impl Walkable for String { Err(WalkError::CantWalk) } } - -impl CompareContextElement for String { - fn to_any(&self) -> &dyn Any { - self - } - - fn equals(&self, other: &dyn ContextElement) -> bool { - other - .to_any() - .downcast_ref::() - .map_or(false, |a| self == a) - } -} From 137e7887a03e4571ced1371eb1a0a497b1248075 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 21:14:51 -0400 Subject: [PATCH 19/63] Finish implementing DTHelperEquals. --- src/renderer/renderer.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index a5309f3..a0b8a73 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -246,7 +246,21 @@ impl<'a> DustRenderer<'a> { }, }; if left_side == right_side { - panic!("testing"); + match ¶meterized_block.contents { + None => return Ok("".to_owned()), + Some(body) => { + let rendered_content = self.render_body(body, breadcrumbs, blocks)?; + return Ok(rendered_content); + } + } + } else { + match ¶meterized_block.else_contents { + None => return Ok("".to_owned()), + Some(body) => { + let rendered_content = self.render_body(body, breadcrumbs, blocks)?; + return Ok(rendered_content); + } + } } } _ => (), // TODO: Implement the rest From 4e274b9ea53067a75953fd2ade933277e9059273 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 21:28:47 -0400 Subject: [PATCH 20/63] Running into the walking issue again. --- src/renderer/parameters_context.rs | 51 ++++++++++++++++++++++++++++++ src/renderer/renderer.rs | 6 ++-- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index e9dd02c..1a5af63 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -23,11 +23,13 @@ use std::collections::HashMap; /// 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), } +#[derive(Clone, Debug, PartialEq)] pub struct OwnedPath { pub keys: Vec, } @@ -43,6 +45,7 @@ impl From<&RValue<'_>> for OwnedRValue { } } +#[derive(Debug)] pub struct NewParametersContext { params: HashMap, breadcrumbs: Vec>, @@ -67,6 +70,54 @@ impl NewParametersContext { } } +impl ContextElement for NewParametersContext {} + +impl Renderable for NewParametersContext { + fn render(&self, _filters: &Vec) -> Result { + // TODO: Would this even ever be called? Won't matter, but I'd + // like to know. Since it is injected 1 above the current + // context, we wouldn't be able to access it with `{.}`. + Ok("[object Object]".to_owned()) + } +} + +impl Loopable for NewParametersContext { + fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { + // TODO: Would this even ever be called? Won't matter, but I'd + // like to know. Since it is injected 1 above the current + // context, we wouldn't be able to access it with `{.}`. + vec![self] + } +} + +impl Walkable for NewParametersContext { + fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> { + let rval = self.params.get(segment).ok_or(WalkError::CantWalk)?; + match rval { + OwnedRValue::RVPath(path) => walk_path(self.breadcrumbs, &path.keys), + OwnedRValue::RVString(text) => Ok(text), + } + } +} + +impl Clone for NewParametersContext { + fn clone(&self) -> Self { + // TODO: What is this doing, really? + *self + } +} + +impl CompareContextElement for NewParametersContext { + fn to_any(&self) -> &dyn Any { + self + } + + fn equals(&self, other: &dyn ContextElement) -> bool { + // TODO: Does this ever happen? perhaps I should have a panic here. + false + } +} + // #[derive(Clone, Debug)] // pub struct ParametersContext<'a> { // params: HashMap<&'a str, &'a RValue<'a>>, diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index a0b8a73..8d66251 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -12,7 +12,7 @@ use crate::renderer::errors::RenderError; use crate::renderer::errors::WalkError; use crate::renderer::inline_partial_tree::extract_inline_partials; use crate::renderer::inline_partial_tree::InlinePartialTreeElement; -// use crate::renderer::parameters_context::ParametersContext; +use crate::renderer::parameters_context::NewParametersContext; use crate::renderer::walking::walk_path; use std::collections::HashMap; @@ -194,9 +194,9 @@ impl<'a> DustRenderer<'a> { self.render_template(&partial.name, breadcrumbs, Some(blocks))?; return Ok(rendered_content); } else { - // let injected_context = ParametersContext::new(breadcrumbs, &partial.params); + let injected_context = NewParametersContext::new(breadcrumbs, &partial.params); let mut new_breadcrumbs = breadcrumbs.clone(); - // new_breadcrumbs.insert(new_breadcrumbs.len() - 1, &injected_context); + new_breadcrumbs.insert(new_breadcrumbs.len() - 1, &injected_context); let rendered_content = self.render_template(&partial.name, &new_breadcrumbs, Some(blocks))?; return Ok(rendered_content); From dade738f55bf2c6d142f060ad7f4b25e86d08e36 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 21:34:18 -0400 Subject: [PATCH 21/63] The structure is all there, just need to implement owned_walk_path and clone on NewParametersContext. --- src/renderer/parameters_context.rs | 10 +++++++--- src/renderer/walking.rs | 8 ++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 1a5af63..f6045b7 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -3,6 +3,7 @@ use crate::parser::{Filter, RValue}; use crate::renderer::context_element::CloneIntoBoxedContextElement; use crate::renderer::context_element::CompareContextElement; use crate::renderer::context_element::ContextElement; +use crate::renderer::walking::owned_walk_path; use crate::renderer::walking::walk_path; use crate::renderer::Loopable; use crate::renderer::RenderError; @@ -94,7 +95,7 @@ impl Walkable for NewParametersContext { fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> { let rval = self.params.get(segment).ok_or(WalkError::CantWalk)?; match rval { - OwnedRValue::RVPath(path) => walk_path(self.breadcrumbs, &path.keys), + OwnedRValue::RVPath(path) => owned_walk_path(&self.breadcrumbs, &path.keys), OwnedRValue::RVString(text) => Ok(text), } } @@ -102,8 +103,11 @@ impl Walkable for NewParametersContext { impl Clone for NewParametersContext { fn clone(&self) -> Self { - // TODO: What is this doing, really? - *self + // TODO: Implement clone + NewParametersContext { + params: HashMap::new(), + breadcrumbs: Vec::new(), + } } } diff --git a/src/renderer/walking.rs b/src/renderer/walking.rs index feadce3..942db87 100644 --- a/src/renderer/walking.rs +++ b/src/renderer/walking.rs @@ -51,3 +51,11 @@ pub fn walk_path<'a>( } Err(WalkError::CantWalk) } + +pub fn owned_walk_path<'a>( + breadcrumbs: &Vec>, + path: &Vec, +) -> Result<&'a dyn ContextElement, WalkError> { + // TODO: Implement owned_walk_path + Err(WalkError::CantWalk) +} From 798d84828ec1506648ad766e4a179e79580109e7 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 21:38:37 -0400 Subject: [PATCH 22/63] Implemented owned_walk_path. --- src/renderer/walking.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/renderer/walking.rs b/src/renderer/walking.rs index 942db87..cc91047 100644 --- a/src/renderer/walking.rs +++ b/src/renderer/walking.rs @@ -53,9 +53,21 @@ pub fn walk_path<'a>( } pub fn owned_walk_path<'a>( - breadcrumbs: &Vec>, + breadcrumbs: &'a Vec>, path: &Vec, ) -> Result<&'a dyn ContextElement, WalkError> { - // TODO: Implement owned_walk_path + 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) } From d5e0c93205c96dee7652f9afd9a4a9c8e25e7709 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 21:45:06 -0400 Subject: [PATCH 23/63] Comparisons between json strings vs rvalue literals is not working as I had hoped. --- src/renderer/renderer.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 8d66251..9f9b49b 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -245,6 +245,7 @@ impl<'a> DustRenderer<'a> { RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys), }, }; + println!("Comparing {:?} to {:?}", left_side, right_side); if left_side == right_side { match ¶meterized_block.contents { None => return Ok("".to_owned()), From 9baa669dea8e603b9dd6c48fbb3a539127ce70bf Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 22:04:41 -0400 Subject: [PATCH 24/63] Separating out CastToAny. --- src/bin.rs | 6 ++++++ src/renderer/context_element.rs | 26 +++++++++++++++++--------- src/renderer/parameters_context.rs | 17 ++++++++++++++++- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index 661670d..47c977b 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -140,3 +140,9 @@ impl Loopable for serde_json::Value { } } } + +impl CompareContextElement for serde_json::Value { + fn equals(&self, other: &dyn ContextElement) -> bool { + false + } +} diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index 29ca430..8701711 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -30,9 +30,11 @@ pub trait Loopable { fn get_loop_elements(&self) -> Vec<&dyn ContextElement>; } -pub trait CompareContextElement { +pub trait CastToAny { fn to_any(&self) -> &dyn Any; +} +pub trait CompareContextElement: CastToAny { fn equals(&self, other: &dyn ContextElement) -> bool; } @@ -46,19 +48,25 @@ impl CloneIntoBoxedContextElement for C { } } -impl CompareContextElement for C { +impl CastToAny for C { fn to_any(&self) -> &dyn Any { self } - - fn equals(&self, other: &dyn ContextElement) -> bool { - other - .to_any() - .downcast_ref::() - .map_or(false, |a| self == a) - } } +// impl CompareContextElement for C { +// fn to_any(&self) -> &dyn Any { +// self +// } + +// fn equals(&self, other: &dyn ContextElement) -> bool { +// other +// .to_any() +// .downcast_ref::() +// .map_or(false, |a| self == a) +// } +// } + impl<'a, 'b> PartialEq<&'b dyn ContextElement> for &'a dyn ContextElement { fn eq(&self, other: &&'b dyn ContextElement) -> bool { self.equals(*other) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index f6045b7..6d525ca 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -1,5 +1,6 @@ use crate::parser::KVPair; use crate::parser::{Filter, RValue}; +use crate::renderer::context_element::CastToAny; use crate::renderer::context_element::CloneIntoBoxedContextElement; use crate::renderer::context_element::CompareContextElement; use crate::renderer::context_element::ContextElement; @@ -111,11 +112,13 @@ impl Clone for NewParametersContext { } } -impl CompareContextElement for NewParametersContext { +impl CastToAny for NewParametersContext { fn to_any(&self) -> &dyn Any { self } +} +impl CompareContextElement for NewParametersContext { fn equals(&self, other: &dyn ContextElement) -> bool { // TODO: Does this ever happen? perhaps I should have a panic here. false @@ -197,3 +200,15 @@ impl Walkable for String { 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::() { + None => other.equals(self), + Some(other_string) => self == other_string, + } + } +} From 2fb7ca9db730d4c4b41edebf022ef5e844c330b4 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 22:05:48 -0400 Subject: [PATCH 25/63] Making CastToAny more generic. --- src/renderer/context_element.rs | 2 +- src/renderer/parameters_context.rs | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index 8701711..0b1c304 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -48,7 +48,7 @@ impl CloneIntoBoxedContextElement for C { } } -impl CastToAny for C { +impl CastToAny for C { fn to_any(&self) -> &dyn Any { self } diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 6d525ca..af08de0 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -112,12 +112,6 @@ impl Clone for NewParametersContext { } } -impl CastToAny for NewParametersContext { - fn to_any(&self) -> &dyn Any { - self - } -} - impl CompareContextElement for NewParametersContext { fn equals(&self, other: &dyn ContextElement) -> bool { // TODO: Does this ever happen? perhaps I should have a panic here. From 6261f7881cb477e7a3e6cfe9c7d985d95754cad7 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 22:14:07 -0400 Subject: [PATCH 26/63] Manual equals is working! --- src/bin.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/bin.rs b/src/bin.rs index 47c977b..9c17df6 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -143,6 +143,26 @@ impl Loopable for serde_json::Value { impl CompareContextElement for serde_json::Value { fn equals(&self, other: &dyn ContextElement) -> bool { - false + match self { + serde_json::Value::Null => false, + serde_json::Value::Bool(boolean) => false, + serde_json::Value::Number(_num) => false, + serde_json::Value::String(string_value) => { + // Handle json string + match other.to_any().downcast_ref::() { + None => (), + Some(other_json_value) => return self == other_json_value, + } + // Handle rust string (for string literals) + match other.to_any().downcast_ref::() { + None => (), + Some(other_string) => return string_value == other_string, + } + // Otherwise we know of no other string types + false + } + serde_json::Value::Array(array_value) => false, + serde_json::Value::Object(_obj) => false, + } } } From 75ba35a42277ecfb4f3741df927f841ceded60a3 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 22:20:39 -0400 Subject: [PATCH 27/63] Cleaning up. --- src/renderer/context_element.rs | 13 -------- src/renderer/parameters_context.rs | 52 ------------------------------ 2 files changed, 65 deletions(-) diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index 0b1c304..3db0f27 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -54,19 +54,6 @@ impl CastToAny for C { } } -// impl CompareContextElement for C { -// fn to_any(&self) -> &dyn Any { -// self -// } - -// fn equals(&self, other: &dyn ContextElement) -> bool { -// other -// .to_any() -// .downcast_ref::() -// .map_or(false, |a| self == a) -// } -// } - impl<'a, 'b> PartialEq<&'b dyn ContextElement> for &'a dyn ContextElement { fn eq(&self, other: &&'b dyn ContextElement) -> bool { self.equals(*other) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index af08de0..b2081d6 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -119,58 +119,6 @@ impl CompareContextElement for NewParametersContext { } } -// #[derive(Clone, Debug)] -// pub struct ParametersContext<'a> { -// params: HashMap<&'a str, &'a RValue<'a>>, -// breadcrumbs: &'a Vec<&'a dyn ContextElement>, -// } - -// impl<'a> ParametersContext<'a> { -// pub fn new( -// breadcrumbs: &'a Vec<&'a dyn ContextElement>, -// params: &'a Vec>, -// ) -> ParametersContext<'a> { -// let param_map = params -// .iter() -// .map(|pair: &KVPair<'a>| (pair.key, &pair.value)) -// .collect(); -// ParametersContext { -// params: param_map, -// breadcrumbs: breadcrumbs, -// } -// } -// } - -// impl<'a> ContextElement for ParametersContext<'a> {} - -// impl<'a> Renderable for ParametersContext<'a> { -// fn render(&self, _filters: &Vec) -> Result { -// // TODO: Would this even ever be called? Won't matter, but I'd -// // like to know. Since it is injected 1 above the current -// // context, we wouldn't be able to access it with `{.}`. -// Ok("[object Object]".to_owned()) -// } -// } - -// impl<'a> Loopable for ParametersContext<'a> { -// fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { -// // TODO: Would this even ever be called? Won't matter, but I'd -// // like to know. Since it is injected 1 above the current -// // context, we wouldn't be able to access it with `{.}`. -// vec![self] -// } -// } - -// impl<'a> Walkable for ParametersContext<'a> { -// fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> { -// let rval = self.params.get(segment).ok_or(WalkError::CantWalk)?; -// match rval { -// RValue::RVPath(path) => walk_path(self.breadcrumbs, &path.keys), -// RValue::RVString(text) => Ok(text), -// } -// } -// } - impl ContextElement for String {} impl Renderable for String { From 6297fa01898bddf163887f5a2f492c41a5ba9bd1 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 22:21:18 -0400 Subject: [PATCH 28/63] Rename NewParametersContext to ParametersContext. --- src/renderer/parameters_context.rs | 22 +++++++++++----------- src/renderer/renderer.rs | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index b2081d6..3649044 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -48,16 +48,16 @@ impl From<&RValue<'_>> for OwnedRValue { } #[derive(Debug)] -pub struct NewParametersContext { +pub struct ParametersContext { params: HashMap, breadcrumbs: Vec>, } -impl NewParametersContext { +impl ParametersContext { pub fn new( breadcrumbs: &Vec<&dyn ContextElement>, params: &Vec, - ) -> NewParametersContext { + ) -> ParametersContext { let owned_params: HashMap = params .iter() .map(|kvpair| (kvpair.key.to_string(), OwnedRValue::from(&kvpair.value))) @@ -65,16 +65,16 @@ impl NewParametersContext { let owned_breadcrumbs: Vec> = breadcrumbs.iter().map(|ce| ce.clone_to_box()).collect(); - NewParametersContext { + ParametersContext { params: owned_params, breadcrumbs: owned_breadcrumbs, } } } -impl ContextElement for NewParametersContext {} +impl ContextElement for ParametersContext {} -impl Renderable for NewParametersContext { +impl Renderable for ParametersContext { fn render(&self, _filters: &Vec) -> Result { // TODO: Would this even ever be called? Won't matter, but I'd // like to know. Since it is injected 1 above the current @@ -83,7 +83,7 @@ impl Renderable for NewParametersContext { } } -impl Loopable for NewParametersContext { +impl Loopable for ParametersContext { fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { // TODO: Would this even ever be called? Won't matter, but I'd // like to know. Since it is injected 1 above the current @@ -92,7 +92,7 @@ impl Loopable for NewParametersContext { } } -impl Walkable for NewParametersContext { +impl Walkable for ParametersContext { fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> { let rval = self.params.get(segment).ok_or(WalkError::CantWalk)?; match rval { @@ -102,17 +102,17 @@ impl Walkable for NewParametersContext { } } -impl Clone for NewParametersContext { +impl Clone for ParametersContext { fn clone(&self) -> Self { // TODO: Implement clone - NewParametersContext { + ParametersContext { params: HashMap::new(), breadcrumbs: Vec::new(), } } } -impl CompareContextElement for NewParametersContext { +impl CompareContextElement for ParametersContext { fn equals(&self, other: &dyn ContextElement) -> bool { // TODO: Does this ever happen? perhaps I should have a panic here. false diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 9f9b49b..c91587b 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -12,7 +12,7 @@ use crate::renderer::errors::RenderError; use crate::renderer::errors::WalkError; use crate::renderer::inline_partial_tree::extract_inline_partials; use crate::renderer::inline_partial_tree::InlinePartialTreeElement; -use crate::renderer::parameters_context::NewParametersContext; +use crate::renderer::parameters_context::ParametersContext; use crate::renderer::walking::walk_path; use std::collections::HashMap; @@ -194,7 +194,7 @@ impl<'a> DustRenderer<'a> { self.render_template(&partial.name, breadcrumbs, Some(blocks))?; return Ok(rendered_content); } else { - let injected_context = NewParametersContext::new(breadcrumbs, &partial.params); + let injected_context = ParametersContext::new(breadcrumbs, &partial.params); let mut new_breadcrumbs = breadcrumbs.clone(); new_breadcrumbs.insert(new_breadcrumbs.len() - 1, &injected_context); let rendered_content = From 46bb5558ac829de7d724a5d62b9e74174670f812 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 22:26:47 -0400 Subject: [PATCH 29/63] Going to a more generic equality comparison for json. --- src/bin.rs | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index 9c17df6..5555061 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -143,26 +143,16 @@ impl Loopable for serde_json::Value { impl CompareContextElement for serde_json::Value { fn equals(&self, other: &dyn ContextElement) -> bool { - match self { - serde_json::Value::Null => false, - serde_json::Value::Bool(boolean) => false, - serde_json::Value::Number(_num) => false, - serde_json::Value::String(string_value) => { - // Handle json string - match other.to_any().downcast_ref::() { - None => (), - Some(other_json_value) => return self == other_json_value, - } - // Handle rust string (for string literals) - match other.to_any().downcast_ref::() { - None => (), - Some(other_string) => return string_value == other_string, - } - // Otherwise we know of no other string types - false - } - serde_json::Value::Array(array_value) => false, - serde_json::Value::Object(_obj) => false, + // Handle other serde_json::Value + match other.to_any().downcast_ref::() { + None => (), + Some(other_json_value) => return self == other_json_value, } + // Handle string literals + match other.to_any().downcast_ref::() { + None => (), + Some(other_string) => return self.as_str().map_or(false, |s| s == other_string), + } + false } } From 68639481f535566bfd125b6bb3a283d97f27e3b8 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 22:35:17 -0400 Subject: [PATCH 30/63] Implement a real clone for ParametersContext. --- src/renderer/parameters_context.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 3649044..a9b52f5 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -54,10 +54,7 @@ pub struct ParametersContext { } impl ParametersContext { - pub fn new( - breadcrumbs: &Vec<&dyn ContextElement>, - params: &Vec, - ) -> ParametersContext { + pub fn new(breadcrumbs: &Vec<&dyn ContextElement>, params: &Vec) -> ParametersContext { let owned_params: HashMap = params .iter() .map(|kvpair| (kvpair.key.to_string(), OwnedRValue::from(&kvpair.value))) @@ -104,10 +101,19 @@ impl Walkable for ParametersContext { impl Clone for ParametersContext { fn clone(&self) -> Self { - // TODO: Implement clone + let new_params: HashMap = self + .params + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect(); + let new_breadcrumbs: Vec> = self + .breadcrumbs + .iter() + .map(|bread| bread.clone_to_box()) + .collect(); ParametersContext { - params: HashMap::new(), - breadcrumbs: Vec::new(), + params: new_params, + breadcrumbs: new_breadcrumbs, } } } From 7dd824ab4f82ce6870deca6987c376b5ccecc9e3 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 22:40:47 -0400 Subject: [PATCH 31/63] cleaning up. --- src/bin.rs | 2 -- src/renderer/parameters_context.rs | 4 ---- src/renderer/renderer.rs | 1 - 3 files changed, 7 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index 5555061..298751c 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -1,6 +1,5 @@ extern crate nom; -use crate::renderer::CloneIntoBoxedContextElement; use crate::renderer::CompareContextElement; use parser::Filter; use renderer::compile_template; @@ -12,7 +11,6 @@ use renderer::RenderError; use renderer::Renderable; use renderer::WalkError; use renderer::Walkable; -use std::any::Any; use std::env; use std::fs; use std::io::{self, Read}; diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index a9b52f5..59d97de 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -1,17 +1,13 @@ use crate::parser::KVPair; use crate::parser::{Filter, RValue}; -use crate::renderer::context_element::CastToAny; -use crate::renderer::context_element::CloneIntoBoxedContextElement; use crate::renderer::context_element::CompareContextElement; use crate::renderer::context_element::ContextElement; use crate::renderer::walking::owned_walk_path; -use crate::renderer::walking::walk_path; use crate::renderer::Loopable; use crate::renderer::RenderError; use crate::renderer::Renderable; use crate::renderer::WalkError; use crate::renderer::Walkable; -use std::any::Any; use std::collections::HashMap; /// Copy the data from an RValue to an Owned struct diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index c91587b..ad622a7 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -245,7 +245,6 @@ impl<'a> DustRenderer<'a> { RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys), }, }; - println!("Comparing {:?} to {:?}", left_side, right_side); if left_side == right_side { match ¶meterized_block.contents { None => return Ok("".to_owned()), From 79099a8654fbeef74699e147bceb3d5ebc1c540e Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 23:01:14 -0400 Subject: [PATCH 32/63] Parsing positive integers. --- src/parser/parser.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 538e159..b4f533e 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -2,11 +2,11 @@ use nom::branch::alt; use nom::bytes::complete::escaped_transform; use nom::bytes::complete::is_a; 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::multispace0; 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::map; use nom::combinator::opt; @@ -122,6 +122,7 @@ pub struct Partial<'a> { pub enum RValue<'a> { RVPath(Path<'a>), RVString(String), + RVPositiveInteger(u64), } #[derive(Clone, Debug, PartialEq)] @@ -210,6 +211,16 @@ fn path(i: &str) -> IResult<&str, Path> { ))(i) } +fn postitive_integer_literal(i: &str) -> IResult<&str, RValue> { + map( + verify( + map(digit1, |number_string: &str| number_string.parse::()), + |parse_result| parse_result.is_ok(), + ), + |parsed_number| RValue::RVPositiveInteger(parsed_number.unwrap()), + )(i) +} + /// Either a literal or a path to a value fn rvalue(i: &str) -> IResult<&str, RValue> { alt(( @@ -906,6 +917,8 @@ mod tests { ); } + // TODO: Add test for integer literals + #[test] fn test_quoted_partial() { assert_eq!( From 9ee8f41022edb497f7a5d31ccbf6ad9ba0083feb Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 23:11:16 -0400 Subject: [PATCH 33/63] Implement the render send of integer literals. --- src/bin.rs | 5 +++++ src/renderer/parameters_context.rs | 35 ++++++++++++++++++++++++++++++ src/renderer/renderer.rs | 2 ++ 3 files changed, 42 insertions(+) diff --git a/src/bin.rs b/src/bin.rs index 298751c..f764ebd 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -151,6 +151,11 @@ impl CompareContextElement for serde_json::Value { None => (), Some(other_string) => return self.as_str().map_or(false, |s| s == other_string), } + // Handle numeric literals + match other.to_any().downcast_ref::() { + None => (), + Some(other_num) => return self.as_u64().map_or(false, |n| n == *other_num), + } false } } diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 59d97de..9d5d749 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -25,6 +25,7 @@ use std::collections::HashMap; pub enum OwnedRValue { RVPath(OwnedPath), RVString(String), + RVPositiveInteger(u64), } #[derive(Clone, Debug, PartialEq)] @@ -39,6 +40,7 @@ impl From<&RValue<'_>> for OwnedRValue { RValue::RVPath(path) => OwnedRValue::RVPath(OwnedPath { keys: path.keys.iter().map(|k| k.to_string()).collect(), }), + RValue::RVPositiveInteger(num) => OwnedRValue::RVPositiveInteger(*num), } } } @@ -91,6 +93,7 @@ impl Walkable for ParametersContext { match rval { OwnedRValue::RVPath(path) => owned_walk_path(&self.breadcrumbs, &path.keys), OwnedRValue::RVString(text) => Ok(text), + OwnedRValue::RVPositiveInteger(num) => Ok(num), } } } @@ -156,3 +159,35 @@ impl CompareContextElement for String { } } } + +impl ContextElement for u64 {} + +impl Renderable for u64 { + fn render(&self, _filters: &Vec) -> Result { + 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::() { + None => other.equals(self), + Some(other_num) => self == other_num, + } + } +} diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index ad622a7..117f882 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -235,6 +235,7 @@ impl<'a> DustRenderer<'a> { Some(rval) => match rval { RValue::RVString(text) => Ok(text), RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys), + RValue::RVPositiveInteger(num) => Ok(num), }, }; let right_side: Result<&dyn ContextElement, WalkError> = @@ -243,6 +244,7 @@ impl<'a> DustRenderer<'a> { Some(rval) => match rval { RValue::RVString(text) => Ok(text), RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys), + RValue::RVPositiveInteger(num) => Ok(num), }, }; if left_side == right_side { From c88cab8316943769d64d7243f50dedab5675fecb Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 23:13:25 -0400 Subject: [PATCH 34/63] Hook in the integer parser. --- src/parser/parser.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/parser/parser.rs b/src/parser/parser.rs index b4f533e..6f14407 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -211,13 +211,14 @@ fn path(i: &str) -> IResult<&str, Path> { ))(i) } -fn postitive_integer_literal(i: &str) -> IResult<&str, RValue> { +/// 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::()), |parse_result| parse_result.is_ok(), ), - |parsed_number| RValue::RVPositiveInteger(parsed_number.unwrap()), + |parsed_number| parsed_number.unwrap(), )(i) } @@ -226,6 +227,7 @@ fn rvalue(i: &str) -> IResult<&str, RValue> { alt(( map(path, RValue::RVPath), map(quoted_string, RValue::RVString), + map(postitive_integer_literal, RValue::RVPositiveInteger), ))(i) } From 6e6560c7427b198ef33a6e27f3a7292cf7832bd8 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 23:26:15 -0400 Subject: [PATCH 35/63] Revived the tests. --- src/renderer/renderer.rs | 104 +++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 58 deletions(-) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 117f882..e612cf5 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -292,88 +292,76 @@ mod tests { use crate::renderer::context_element::Loopable; use crate::renderer::context_element::Renderable; use crate::renderer::context_element::Walkable; + use crate::renderer::CompareContextElement; - impl ContextElement for u32 {} - impl ContextElement for &str {} - impl ContextElement for HashMap<&str, I> {} + impl ContextElement for HashMap {} - impl Renderable for u32 { - fn render(&self, _filters: &Vec) -> Result { - // TODO: handle the filters - Ok(self.to_string()) - } - } - - impl Renderable for &str { - fn render(&self, _filters: &Vec) -> Result { - // TODO: handle the filters - Ok(self.to_string()) - } - } - - impl Renderable for HashMap<&str, I> { + impl Renderable for HashMap { fn render(&self, _filters: &Vec) -> Result { // TODO: handle the filters Ok("[object Object]".to_owned()) } } - impl Walkable for HashMap<&str, I> { + impl Walkable for HashMap { fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> { let child = self.get(segment).ok_or(WalkError::CantWalk)?; Ok(child) } } - impl Walkable for &str { - 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 { + impl Loopable for HashMap { fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { vec![self] } } - impl Loopable for HashMap<&str, I> { - fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { - vec![self] + impl CompareContextElement for HashMap { + fn equals(&self, other: &dyn ContextElement) -> bool { + false } } #[test] fn test_walk_path() { - let context: HashMap<&str, &str> = - [("cat", "kitty"), ("dog", "doggy"), ("tiger", "murderkitty")] - .iter() - .cloned() - .collect(); - let number_context: HashMap<&str, u32> = [("cat", 1), ("dog", 2), ("tiger", 3)] - .iter() - .cloned() - .collect(); - let deep_context: HashMap<&str, HashMap<&str, &str>> = [ - ("cat", [("food", "meat")].iter().cloned().collect()), - ("dog", [("food", "meat")].iter().cloned().collect()), - ("tiger", [("food", "people")].iter().cloned().collect()), + let context: HashMap = [ + ("cat".to_string(), "kitty".to_string()), + ("dog".to_string(), "doggy".to_string()), + ("tiger".to_string(), "murderkitty".to_string()), + ] + .iter() + .cloned() + .collect(); + let number_context: HashMap = [ + ("cat".to_string(), 1), + ("dog".to_string(), 2), + ("tiger".to_string(), 3), + ] + .iter() + .cloned() + .collect(); + let deep_context: HashMap> = [ + ( + "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() .cloned() From 1b63bc4083fa17eec94637d4fb6575a8a86c8357 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 10 May 2020 23:42:56 -0400 Subject: [PATCH 36/63] Add a test for integer literals. --- src/parser/parser.rs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 6f14407..3b8b30c 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -919,8 +919,6 @@ mod tests { ); } - // TODO: Add test for integer literals - #[test] fn test_quoted_partial() { assert_eq!( @@ -944,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] fn test_helper() { assert_eq!( From e04a0be5e70b670cee5c997fdbeb5b92adc800f9 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 12:13:40 -0400 Subject: [PATCH 37/63] Add the rest of the helpers to the recursive extract inline partials function. --- src/renderer/inline_partial_tree.rs | 61 ++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/src/renderer/inline_partial_tree.rs b/src/renderer/inline_partial_tree.rs index c58a5bb..b37d40b 100644 --- a/src/renderer/inline_partial_tree.rs +++ b/src/renderer/inline_partial_tree.rs @@ -99,6 +99,65 @@ fn extract_inline_partials_from_tag<'a, 'b>( blocks.insert(&named_block.name, &named_block.contents); } 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::DTHelperGreaterThenOrEquals(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::DTHelperLessThenOrEquals(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), + }; + } } } From 94cedef3ef66787df227930cb0c7ba26b5b799ca Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 12:23:08 -0400 Subject: [PATCH 38/63] Added a test for not equals which is roughly a copy of the equals test. --- js/test_cases/helpers_ne/README.md | 10 ++++++++++ js/test_cases/helpers_ne/input1.json | 7 +++++++ js/test_cases/helpers_ne/main.dust | 18 ++++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 js/test_cases/helpers_ne/README.md create mode 100644 js/test_cases/helpers_ne/input1.json create mode 100644 js/test_cases/helpers_ne/main.dust diff --git a/js/test_cases/helpers_ne/README.md b/js/test_cases/helpers_ne/README.md new file mode 100644 index 0000000..42d393a --- /dev/null +++ b/js/test_cases/helpers_ne/README.md @@ -0,0 +1,10 @@ +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. diff --git a/js/test_cases/helpers_ne/input1.json b/js/test_cases/helpers_ne/input1.json new file mode 100644 index 0000000..ece58d2 --- /dev/null +++ b/js/test_cases/helpers_ne/input1.json @@ -0,0 +1,7 @@ +{ + "str": "master", + "int": 7, + "alpha": 21, + "beta": "21", + "null": null +} diff --git a/js/test_cases/helpers_ne/main.dust b/js/test_cases/helpers_ne/main.dust new file mode 100644 index 0000000..f9ed426 --- /dev/null +++ b/js/test_cases/helpers_ne/main.dust @@ -0,0 +1,18 @@ +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 if false{/ne}{~n} From f4a935224c53874c19c423fde4657f2704edaffd Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 12:26:28 -0400 Subject: [PATCH 39/63] Implemented the not equals helper. --- src/renderer/renderer.rs | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index e612cf5..addfa85 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -265,6 +265,47 @@ impl<'a> DustRenderer<'a> { } } } + DustTag::DTHelperNotEquals(parameterized_block) => { + let param_map: HashMap<&str, &RValue<'a>> = parameterized_block + .params + .iter() + .map(|pair: &KVPair<'a>| (pair.key, &pair.value)) + .collect(); + let left_side: Result<&dyn ContextElement, WalkError> = match param_map.get("key") { + None => return Ok("".to_owned()), + Some(rval) => match rval { + RValue::RVString(text) => Ok(text), + RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys), + RValue::RVPositiveInteger(num) => Ok(num), + }, + }; + let right_side: Result<&dyn ContextElement, WalkError> = + match param_map.get("value") { + None => Err(WalkError::CantWalk), + Some(rval) => match rval { + RValue::RVString(text) => Ok(text), + RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys), + RValue::RVPositiveInteger(num) => Ok(num), + }, + }; + if left_side != right_side { + match ¶meterized_block.contents { + None => return Ok("".to_owned()), + Some(body) => { + let rendered_content = self.render_body(body, breadcrumbs, blocks)?; + return Ok(rendered_content); + } + } + } else { + match ¶meterized_block.else_contents { + None => return Ok("".to_owned()), + Some(body) => { + let rendered_content = self.render_body(body, breadcrumbs, blocks)?; + return Ok(rendered_content); + } + } + } + } _ => (), // TODO: Implement the rest } Ok("".to_owned()) From 196740ae349fbf419a0e693040ead7b7b65ae360 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 12:51:27 -0400 Subject: [PATCH 40/63] Starting the greater than helper tests. --- js/test_cases/helpers_gt/README.md | 3 +++ js/test_cases/helpers_gt/input1.json | 7 +++++++ js/test_cases/helpers_gt/main.dust | 21 +++++++++++++++++++++ js/test_cases/helpers_ne/README.md | 10 ---------- 4 files changed, 31 insertions(+), 10 deletions(-) create mode 100644 js/test_cases/helpers_gt/README.md create mode 100644 js/test_cases/helpers_gt/input1.json create mode 100644 js/test_cases/helpers_gt/main.dust delete mode 100644 js/test_cases/helpers_ne/README.md diff --git a/js/test_cases/helpers_gt/README.md b/js/test_cases/helpers_gt/README.md new file mode 100644 index 0000000..09c6441 --- /dev/null +++ b/js/test_cases/helpers_gt/README.md @@ -0,0 +1,3 @@ +Since "master" is greater than "7", dust is probably ordering based on either alphabetic sorting or ascii values. + +All comparisons between non-matching types (for example, int vs string) appear to render the else block. diff --git a/js/test_cases/helpers_gt/input1.json b/js/test_cases/helpers_gt/input1.json new file mode 100644 index 0000000..ece58d2 --- /dev/null +++ b/js/test_cases/helpers_gt/input1.json @@ -0,0 +1,7 @@ +{ + "str": "master", + "int": 7, + "alpha": 21, + "beta": "21", + "null": null +} diff --git a/js/test_cases/helpers_gt/main.dust b/js/test_cases/helpers_gt/main.dust new file mode 100644 index 0000000..d752542 --- /dev/null +++ b/js/test_cases/helpers_gt/main.dust @@ -0,0 +1,21 @@ +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 equal to "master"{:else}"master" does not equal "master"{/gt}{~n} +{@gt key=null}null equals a missing value{:else}null does not equal a missing value{/gt}{~n} +{@gt key=null value=gamma}null equals a non-existent value{:else}null does not equal a non-existent value{/gt}{~n} +{@gt}no parameters is true{:else}no parameters if false{/gt}{~n} +!} diff --git a/js/test_cases/helpers_ne/README.md b/js/test_cases/helpers_ne/README.md deleted file mode 100644 index 42d393a..0000000 --- a/js/test_cases/helpers_ne/README.md +++ /dev/null @@ -1,10 +0,0 @@ -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. From f390c05a4c504fd21051b1db79c6b8f5845f84b3 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 13:00:09 -0400 Subject: [PATCH 41/63] Finished porting over the equality test to greater than. --- js/test_cases/helpers_eq/main.dust | 2 +- js/test_cases/helpers_gt/README.md | 2 ++ js/test_cases/helpers_gt/main.dust | 11 +++++------ js/test_cases/helpers_ne/main.dust | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/js/test_cases/helpers_eq/main.dust b/js/test_cases/helpers_eq/main.dust index 8126b32..ab68cf3 100644 --- a/js/test_cases/helpers_eq/main.dust +++ b/js/test_cases/helpers_eq/main.dust @@ -15,4 +15,4 @@ beta is {beta}{~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 if false{/eq}{~n} +{@eq}no parameters is true{:else}no parameters is false{/eq}{~n} diff --git a/js/test_cases/helpers_gt/README.md b/js/test_cases/helpers_gt/README.md index 09c6441..0b8a68b 100644 --- a/js/test_cases/helpers_gt/README.md +++ b/js/test_cases/helpers_gt/README.md @@ -1,3 +1,5 @@ Since "master" is greater than "7", dust is probably ordering based on either alphabetic sorting or ascii values. All comparisons between non-matching types (for example, int vs string) appear to render the else block. + +Greater than follows the same pattern for not rendering when key is omitted or null. diff --git a/js/test_cases/helpers_gt/main.dust b/js/test_cases/helpers_gt/main.dust index d752542..ae88ded 100644 --- a/js/test_cases/helpers_gt/main.dust +++ b/js/test_cases/helpers_gt/main.dust @@ -9,13 +9,12 @@ beta is {beta}{~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 equal to "master"{:else}"master" does not equal "master"{/gt}{~n} -{@gt key=null}null equals a missing value{:else}null does not equal a missing value{/gt}{~n} -{@gt key=null value=gamma}null equals a non-existent value{:else}null does not equal a non-existent value{/gt}{~n} -{@gt}no parameters is true{:else}no parameters if 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} + diff --git a/js/test_cases/helpers_ne/main.dust b/js/test_cases/helpers_ne/main.dust index f9ed426..67f8644 100644 --- a/js/test_cases/helpers_ne/main.dust +++ b/js/test_cases/helpers_ne/main.dust @@ -15,4 +15,4 @@ beta is {beta}{~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 if false{/ne}{~n} +{@ne}no parameters is true{:else}no parameters is false{/ne}{~n} From 8a44bc6fd9e9ef7a64caf14880604168ba6b2e23 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 13:06:52 -0400 Subject: [PATCH 42/63] Add greater-than-specific tests to investigate string ordering. --- js/test_cases/helpers_gt/README.md | 2 +- js/test_cases/helpers_gt/main.dust | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/js/test_cases/helpers_gt/README.md b/js/test_cases/helpers_gt/README.md index 0b8a68b..d982ece 100644 --- a/js/test_cases/helpers_gt/README.md +++ b/js/test_cases/helpers_gt/README.md @@ -1,4 +1,4 @@ -Since "master" is greater than "7", dust is probably ordering based on either alphabetic sorting or ascii values. +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. All comparisons between non-matching types (for example, int vs string) appear to render the else block. diff --git a/js/test_cases/helpers_gt/main.dust b/js/test_cases/helpers_gt/main.dust index ae88ded..a801230 100644 --- a/js/test_cases/helpers_gt/main.dust +++ b/js/test_cases/helpers_gt/main.dust @@ -17,4 +17,7 @@ beta is {beta}{~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} +{@gt key="☃" value="☄"}"☃" is greater than "☄"{:else}"☃" is less than or equal to "☄"{/gt}{~n} From 8748cb7063f6c21acc8cb767a53fbbffd0c8a74f Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 13:31:52 -0400 Subject: [PATCH 43/63] Structure for ordering, need to implement for serde_json::Value. --- src/bin.rs | 6 +++++ src/renderer/context_element.rs | 10 +++++++- src/renderer/parameters_context.rs | 41 +++++++++++++++++++++++++++++- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index f764ebd..c72d00d 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -11,6 +11,7 @@ use renderer::RenderError; use renderer::Renderable; use renderer::WalkError; use renderer::Walkable; +use std::cmp::Ordering; use std::env; use std::fs; use std::io::{self, Read}; @@ -158,4 +159,9 @@ impl CompareContextElement for serde_json::Value { } false } + + fn partial_compare(&self, other: &dyn ContextElement) -> Option { + // TODO: Implement + None + } } diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index 3db0f27..bb43cf8 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -2,7 +2,7 @@ use crate::parser::Filter; use crate::renderer::errors::RenderError; use crate::renderer::errors::WalkError; use std::any::Any; -use std::fmt::Debug; +use std::{cmp::Ordering, fmt::Debug}; pub trait ContextElement: Debug + Walkable + Renderable + Loopable + CloneIntoBoxedContextElement + CompareContextElement @@ -36,6 +36,8 @@ pub trait CastToAny { pub trait CompareContextElement: CastToAny { fn equals(&self, other: &dyn ContextElement) -> bool; + + fn partial_compare(&self, other: &dyn ContextElement) -> Option; } pub trait CloneIntoBoxedContextElement { @@ -59,3 +61,9 @@ impl<'a, 'b> PartialEq<&'b dyn ContextElement> for &'a dyn ContextElement { self.equals(*other) } } + +impl<'a, 'b> PartialOrd<&'b dyn ContextElement> for &'a dyn ContextElement { + fn partial_cmp(&self, other: &&'b dyn ContextElement) -> Option { + self.partial_compare(*other) + } +} diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 9d5d749..5812ce3 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -8,7 +8,7 @@ use crate::renderer::RenderError; use crate::renderer::Renderable; use crate::renderer::WalkError; use crate::renderer::Walkable; -use std::collections::HashMap; +use std::{cmp::Ordering, collections::HashMap}; /// Copy the data from an RValue to an Owned struct /// @@ -122,6 +122,11 @@ impl CompareContextElement for ParametersContext { // TODO: Does this ever happen? perhaps I should have a panic here. false } + + fn partial_compare(&self, other: &dyn ContextElement) -> Option { + // TODO: Does this ever happen? perhaps I should have a panic here. + None + } } impl ContextElement for String {} @@ -158,6 +163,23 @@ impl CompareContextElement for String { Some(other_string) => self == other_string, } } + + fn partial_compare(&self, other: &dyn ContextElement) -> Option { + // 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::() { + 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 {} @@ -190,4 +212,21 @@ impl CompareContextElement for u64 { Some(other_num) => self == other_num, } } + + fn partial_compare(&self, other: &dyn ContextElement) -> Option { + // 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::() { + 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), + } + } } From 9851a2556dbf24516bdb6ec3548fc34b9fa91875 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 13:40:56 -0400 Subject: [PATCH 44/63] Ordering implementation for comparison between json and literals. --- src/bin.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/bin.rs b/src/bin.rs index c72d00d..7e41d8d 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -161,7 +161,25 @@ impl CompareContextElement for serde_json::Value { } fn partial_compare(&self, other: &dyn ContextElement) -> Option { - // TODO: Implement + // Handle other serde_json::Value + match other.to_any().downcast_ref::() { + None => (), + Some(other_json_value) => return None, // TODO: Implement + } + // Handle string literals + match other.to_any().downcast_ref::() { + 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::() { + None => (), + Some(other_num) => return self.as_u64().map_or(None, |n| n.partial_cmp(other_num)), + } None } } From 563fd1f6dbdd332bfcff1bc2b57857394ed4a535 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 13:44:34 -0400 Subject: [PATCH 45/63] Starting comparison of json value to json value. --- src/bin.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/bin.rs b/src/bin.rs index 7e41d8d..afcf538 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -164,7 +164,12 @@ impl CompareContextElement for serde_json::Value { // Handle other serde_json::Value match other.to_any().downcast_ref::() { None => (), - Some(other_json_value) => return None, // TODO: Implement + Some(other_json_value) => { + return match (self, other_json_value) { + // TODO: Implement + _ => None, + }; + } } // Handle string literals match other.to_any().downcast_ref::() { From 71e6da39eef2a464c4c4fd6937f26fc886b59def Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 13:46:31 -0400 Subject: [PATCH 46/63] Add a test for boolean comparison. --- js/test_cases/helpers_gt/input1.json | 4 +++- js/test_cases/helpers_gt/main.dust | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/js/test_cases/helpers_gt/input1.json b/js/test_cases/helpers_gt/input1.json index ece58d2..3a631f3 100644 --- a/js/test_cases/helpers_gt/input1.json +++ b/js/test_cases/helpers_gt/input1.json @@ -3,5 +3,7 @@ "int": 7, "alpha": 21, "beta": "21", - "null": null + "null": null, + "true_value": true, + "false_value": false } diff --git a/js/test_cases/helpers_gt/main.dust b/js/test_cases/helpers_gt/main.dust index a801230..d92bc99 100644 --- a/js/test_cases/helpers_gt/main.dust +++ b/js/test_cases/helpers_gt/main.dust @@ -21,3 +21,4 @@ beta is {beta}{~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} {@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} From 2b5bec05be0f74fc33aaad6687438c1e40905829 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 14:08:59 -0400 Subject: [PATCH 47/63] Implemented partial_compare for scalar json values. --- src/bin.rs | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/bin.rs b/src/bin.rs index afcf538..659faf5 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -166,7 +166,52 @@ impl CompareContextElement for serde_json::Value { None => (), Some(other_json_value) => { return match (self, other_json_value) { - // TODO: Implement + ( + 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), _ => None, }; } From 6dbeb77a289a82db6ee9dad241405b0462c25969 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 14:15:44 -0400 Subject: [PATCH 48/63] Add arrays to all helpers tests. --- js/test_cases/helpers_eq/input1.json | 11 ++++++++++- js/test_cases/helpers_eq/main.dust | 2 ++ js/test_cases/helpers_gt/README.md | 2 ++ js/test_cases/helpers_gt/input1.json | 11 ++++++++++- js/test_cases/helpers_gt/main.dust | 2 ++ js/test_cases/helpers_ne/input1.json | 11 ++++++++++- js/test_cases/helpers_ne/main.dust | 2 ++ src/bin.rs | 1 + 8 files changed, 39 insertions(+), 3 deletions(-) diff --git a/js/test_cases/helpers_eq/input1.json b/js/test_cases/helpers_eq/input1.json index ece58d2..0c96e44 100644 --- a/js/test_cases/helpers_eq/input1.json +++ b/js/test_cases/helpers_eq/input1.json @@ -3,5 +3,14 @@ "int": 7, "alpha": 21, "beta": "21", - "null": null + "null": null, + "array_lower": [ + 3, + 5, + 7 + ], + "array_higher": [ + 8, + 9 + ] } diff --git a/js/test_cases/helpers_eq/main.dust b/js/test_cases/helpers_eq/main.dust index ab68cf3..fd065f8 100644 --- a/js/test_cases/helpers_eq/main.dust +++ b/js/test_cases/helpers_eq/main.dust @@ -16,3 +16,5 @@ beta is {beta}{~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} +{@eq key=array_lower value=array_lower}[3,5,7] is equal to [3,5,7]{:else}[3,5,7] does not equal [3,5,7]{/eq}{~n} diff --git a/js/test_cases/helpers_gt/README.md b/js/test_cases/helpers_gt/README.md index d982ece..29c57b3 100644 --- a/js/test_cases/helpers_gt/README.md +++ b/js/test_cases/helpers_gt/README.md @@ -2,4 +2,6 @@ Based on my tests, it appears dust is sorting based on ascii-table values. This 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 follows the same pattern for not rendering when key is omitted or null. diff --git a/js/test_cases/helpers_gt/input1.json b/js/test_cases/helpers_gt/input1.json index 3a631f3..4de7a58 100644 --- a/js/test_cases/helpers_gt/input1.json +++ b/js/test_cases/helpers_gt/input1.json @@ -5,5 +5,14 @@ "beta": "21", "null": null, "true_value": true, - "false_value": false + "false_value": false, + "array_lower": [ + 3, + 5, + 7 + ], + "array_higher": [ + 8, + 9 + ] } diff --git a/js/test_cases/helpers_gt/main.dust b/js/test_cases/helpers_gt/main.dust index d92bc99..894cc88 100644 --- a/js/test_cases/helpers_gt/main.dust +++ b/js/test_cases/helpers_gt/main.dust @@ -22,3 +22,5 @@ beta is {beta}{~n} {@gt key="a" value="}"}"a" is greater than "}"{:else}"a" is less than or equal to "}"{/gt}{~n} {@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} diff --git a/js/test_cases/helpers_ne/input1.json b/js/test_cases/helpers_ne/input1.json index ece58d2..0c96e44 100644 --- a/js/test_cases/helpers_ne/input1.json +++ b/js/test_cases/helpers_ne/input1.json @@ -3,5 +3,14 @@ "int": 7, "alpha": 21, "beta": "21", - "null": null + "null": null, + "array_lower": [ + 3, + 5, + 7 + ], + "array_higher": [ + 8, + 9 + ] } diff --git a/js/test_cases/helpers_ne/main.dust b/js/test_cases/helpers_ne/main.dust index 67f8644..9796b47 100644 --- a/js/test_cases/helpers_ne/main.dust +++ b/js/test_cases/helpers_ne/main.dust @@ -16,3 +16,5 @@ beta is {beta}{~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} +{@ne key=array_lower value=array_lower}[3,5,7] does not equal [3,5,7]{:else}[3,5,7] is equal to [3,5,7]{/ne}{~n} diff --git a/src/bin.rs b/src/bin.rs index 659faf5..2971ffa 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -212,6 +212,7 @@ impl CompareContextElement for serde_json::Value { serde_json::Value::String(self_string), serde_json::Value::String(other_string), ) => self_string.partial_cmp(other_string), + // TODO: Non-scalar types _ => None, }; } From d751df6fd5a5ccb264631ab7042808c3b3d59b1e Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 14:48:04 -0400 Subject: [PATCH 49/63] Add tests for copied values. --- js/test_cases/helpers_eq/README.md | 5 ++++ js/test_cases/helpers_eq/input1.json | 44 ++++++++++++++++++++++++++++ js/test_cases/helpers_eq/main.dust | 10 ++++++- js/test_cases/helpers_gt/input1.json | 42 ++++++++++++++++++++++++++ js/test_cases/helpers_gt/main.dust | 9 ++++++ js/test_cases/helpers_ne/input1.json | 44 ++++++++++++++++++++++++++++ js/test_cases/helpers_ne/main.dust | 10 ++++++- 7 files changed, 162 insertions(+), 2 deletions(-) diff --git a/js/test_cases/helpers_eq/README.md b/js/test_cases/helpers_eq/README.md index 42d393a..ddcf8e1 100644 --- a/js/test_cases/helpers_eq/README.md +++ b/js/test_cases/helpers_eq/README.md @@ -8,3 +8,8 @@ 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. diff --git a/js/test_cases/helpers_eq/input1.json b/js/test_cases/helpers_eq/input1.json index 0c96e44..bfbbd94 100644 --- a/js/test_cases/helpers_eq/input1.json +++ b/js/test_cases/helpers_eq/input1.json @@ -4,13 +4,57 @@ "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" ] } diff --git a/js/test_cases/helpers_eq/main.dust b/js/test_cases/helpers_eq/main.dust index fd065f8..c1b8562 100644 --- a/js/test_cases/helpers_eq/main.dust +++ b/js/test_cases/helpers_eq/main.dust @@ -17,4 +17,12 @@ beta is {beta}{~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} -{@eq key=array_lower value=array_lower}[3,5,7] is equal to [3,5,7]{:else}[3,5,7] does not equal [3,5,7]{/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} diff --git a/js/test_cases/helpers_gt/input1.json b/js/test_cases/helpers_gt/input1.json index 4de7a58..bfbbd94 100644 --- a/js/test_cases/helpers_gt/input1.json +++ b/js/test_cases/helpers_gt/input1.json @@ -11,8 +11,50 @@ 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" ] } diff --git a/js/test_cases/helpers_gt/main.dust b/js/test_cases/helpers_gt/main.dust index 894cc88..9a4ffa8 100644 --- a/js/test_cases/helpers_gt/main.dust +++ b/js/test_cases/helpers_gt/main.dust @@ -24,3 +24,12 @@ beta is {beta}{~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} diff --git a/js/test_cases/helpers_ne/input1.json b/js/test_cases/helpers_ne/input1.json index 0c96e44..bfbbd94 100644 --- a/js/test_cases/helpers_ne/input1.json +++ b/js/test_cases/helpers_ne/input1.json @@ -4,13 +4,57 @@ "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" ] } diff --git a/js/test_cases/helpers_ne/main.dust b/js/test_cases/helpers_ne/main.dust index 9796b47..d31e4b7 100644 --- a/js/test_cases/helpers_ne/main.dust +++ b/js/test_cases/helpers_ne/main.dust @@ -17,4 +17,12 @@ beta is {beta}{~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} -{@ne key=array_lower value=array_lower}[3,5,7] does not equal [3,5,7]{:else}[3,5,7] is equal to [3,5,7]{/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} From 41e4874d75e67e0a75eefa40e9056fcb97b1e86b Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 15:22:48 -0400 Subject: [PATCH 50/63] Add a check to the equality helper to mark identical paths as equal. --- js/run_compliance_suite.bash | 2 +- src/renderer/renderer.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/js/run_compliance_suite.bash b/js/run_compliance_suite.bash index 4423242..a13d025 100755 --- a/js/run_compliance_suite.bash +++ b/js/run_compliance_suite.bash @@ -38,7 +38,7 @@ while read -r test_group; do else echo "$test_group_name::$test_case_name FAILED" 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 exit 1 fi diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index addfa85..712ad63 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -230,6 +230,20 @@ impl<'a> DustRenderer<'a> { .iter() .map(|pair: &KVPair<'a>| (pair.key, &pair.value)) .collect(); + // 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 param_map.get("key") { None => return Ok("".to_owned()), Some(rval) => match rval { @@ -324,6 +338,19 @@ impl<'a> DustRenderer<'a> { 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, + }, + } + } } #[cfg(test)] From 7d4cb14530273e22f3862bcc4016d327780db666 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 15:30:17 -0400 Subject: [PATCH 51/63] Add that check to the not-equals helper and update the json value implementation to mark all non-scalars as not-equals. --- src/bin.rs | 10 +++++++++- src/renderer/renderer.rs | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/bin.rs b/src/bin.rs index 2971ffa..1f698a9 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -145,7 +145,15 @@ impl CompareContextElement for serde_json::Value { // Handle other serde_json::Value match other.to_any().downcast_ref::() { None => (), - Some(other_json_value) => return self == other_json_value, + 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::() { diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 712ad63..aeee5cb 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -285,6 +285,20 @@ impl<'a> DustRenderer<'a> { .iter() .map(|pair: &KVPair<'a>| (pair.key, &pair.value)) .collect(); + // 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 param_map.get("key") { None => return Ok("".to_owned()), Some(rval) => match rval { From 02abee1c531a9a2e967c1c5ee08ad7d1faa489da Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 15:45:57 -0400 Subject: [PATCH 52/63] Add a test for gte. --- js/test_cases/helpers_gt/README.md | 16 ++++++- js/test_cases/helpers_gte/input1.json | 60 +++++++++++++++++++++++++++ js/test_cases/helpers_gte/main.dust | 35 ++++++++++++++++ src/renderer/renderer.rs | 7 ++++ 4 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 js/test_cases/helpers_gte/input1.json create mode 100644 js/test_cases/helpers_gte/main.dust diff --git a/js/test_cases/helpers_gt/README.md b/js/test_cases/helpers_gt/README.md index 29c57b3..58a764a 100644 --- a/js/test_cases/helpers_gt/README.md +++ b/js/test_cases/helpers_gt/README.md @@ -1,7 +1,19 @@ 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. + + +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. +Comparisons between non-scalar types (like arrays) appears to render the else block -Greater than follows the same pattern for not rendering when key is omitted or null. + +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 diff --git a/js/test_cases/helpers_gte/input1.json b/js/test_cases/helpers_gte/input1.json new file mode 100644 index 0000000..bfbbd94 --- /dev/null +++ b/js/test_cases/helpers_gte/input1.json @@ -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" + ] +} diff --git a/js/test_cases/helpers_gte/main.dust b/js/test_cases/helpers_gte/main.dust new file mode 100644 index 0000000..a3eb601 --- /dev/null +++ b/js/test_cases/helpers_gte/main.dust @@ -0,0 +1,35 @@ +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} +{@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} diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index aeee5cb..7ecfc14 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -334,6 +334,13 @@ impl<'a> DustRenderer<'a> { } } } + DustTag::DTHelperGreaterThan(parameterized_block) => { + let param_map: HashMap<&str, &RValue<'a>> = parameterized_block + .params + .iter() + .map(|pair: &KVPair<'a>| (pair.key, &pair.value)) + .collect(); + } _ => (), // TODO: Implement the rest } Ok("".to_owned()) From 48e35c54bbf700487e6dc6101a9fca5a1865379f Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 15:51:40 -0400 Subject: [PATCH 53/63] Add tests for less than and less than or equal to. --- js/test_cases/helpers_lt/input1.json | 60 +++++++++++++++++++++++++++ js/test_cases/helpers_lt/main.dust | 35 ++++++++++++++++ js/test_cases/helpers_lte/input1.json | 60 +++++++++++++++++++++++++++ js/test_cases/helpers_lte/main.dust | 35 ++++++++++++++++ 4 files changed, 190 insertions(+) create mode 100644 js/test_cases/helpers_lt/input1.json create mode 100644 js/test_cases/helpers_lt/main.dust create mode 100644 js/test_cases/helpers_lte/input1.json create mode 100644 js/test_cases/helpers_lte/main.dust diff --git a/js/test_cases/helpers_lt/input1.json b/js/test_cases/helpers_lt/input1.json new file mode 100644 index 0000000..bfbbd94 --- /dev/null +++ b/js/test_cases/helpers_lt/input1.json @@ -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" + ] +} diff --git a/js/test_cases/helpers_lt/main.dust b/js/test_cases/helpers_lt/main.dust new file mode 100644 index 0000000..305c3c4 --- /dev/null +++ b/js/test_cases/helpers_lt/main.dust @@ -0,0 +1,35 @@ +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} +{@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} diff --git a/js/test_cases/helpers_lte/input1.json b/js/test_cases/helpers_lte/input1.json new file mode 100644 index 0000000..bfbbd94 --- /dev/null +++ b/js/test_cases/helpers_lte/input1.json @@ -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" + ] +} diff --git a/js/test_cases/helpers_lte/main.dust b/js/test_cases/helpers_lte/main.dust new file mode 100644 index 0000000..dcf0ab5 --- /dev/null +++ b/js/test_cases/helpers_lte/main.dust @@ -0,0 +1,35 @@ +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} +{@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} From 6758d515f15cbf622f6457417504118db025646f Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 16:24:36 -0400 Subject: [PATCH 54/63] Have greater than helper very close to correct. Just need to make it compare arrays of scalars. --- js/test_cases/helpers_gt/README.md | 15 ++++++++++ js/test_cases/helpers_gt/main.dust | 3 ++ src/renderer/renderer.rs | 47 ++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/js/test_cases/helpers_gt/README.md b/js/test_cases/helpers_gt/README.md index 58a764a..f418aed 100644 --- a/js/test_cases/helpers_gt/README.md +++ b/js/test_cases/helpers_gt/README.md @@ -17,3 +17,18 @@ 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 diff --git a/js/test_cases/helpers_gt/main.dust b/js/test_cases/helpers_gt/main.dust index 9a4ffa8..c7b3699 100644 --- a/js/test_cases/helpers_gt/main.dust +++ b/js/test_cases/helpers_gt/main.dust @@ -20,7 +20,10 @@ beta is {beta}{~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 not working in rust implementation yet {@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} diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 7ecfc14..cd83681 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -340,6 +340,53 @@ impl<'a> DustRenderer<'a> { .iter() .map(|pair: &KVPair<'a>| (pair.key, &pair.value)) .collect(); + let left_side: Result<&dyn ContextElement, WalkError> = match param_map.get("key") { + None => return Ok("".to_owned()), + Some(rval) => match rval { + RValue::RVString(text) => Ok(text), + RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys), + RValue::RVPositiveInteger(num) => Ok(num), + }, + }; + let right_side: Result<&dyn ContextElement, WalkError> = + match param_map.get("value") { + None => Err(WalkError::CantWalk), + Some(rval) => match rval { + RValue::RVString(text) => Ok(text), + RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys), + RValue::RVPositiveInteger(num) => Ok(num), + }, + }; + match (left_side, right_side) { + (Err(_), _) | (_, Err(_)) => match ¶meterized_block.else_contents { + None => return Ok("".to_owned()), + Some(body) => { + let rendered_content = self.render_body(body, breadcrumbs, blocks)?; + return Ok(rendered_content); + } + }, + (Ok(left_side_unwrapped), Ok(right_side_unwrapped)) => { + if left_side_unwrapped > right_side_unwrapped { + match ¶meterized_block.contents { + None => return Ok("".to_owned()), + Some(body) => { + let rendered_content = + self.render_body(body, breadcrumbs, blocks)?; + return Ok(rendered_content); + } + } + } else { + match ¶meterized_block.else_contents { + None => return Ok("".to_owned()), + Some(body) => { + let rendered_content = + self.render_body(body, breadcrumbs, blocks)?; + return Ok(rendered_content); + } + } + } + } + } } _ => (), // TODO: Implement the rest } From 0f90fa2c7e1f1b30d9b2fab9239bda2336d4e5df Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 16:46:39 -0400 Subject: [PATCH 55/63] Add support for ignoring tests by prefixing their name with an underscore. --- js/run_compliance_suite.bash | 7 ++++++- js/test_cases/{helpers_gte => _helpers_gte}/input1.json | 0 js/test_cases/{helpers_gte => _helpers_gte}/main.dust | 0 js/test_cases/{helpers_lt => _helpers_lt}/input1.json | 0 js/test_cases/{helpers_lt => _helpers_lt}/main.dust | 0 js/test_cases/{helpers_lte => _helpers_lte}/input1.json | 0 js/test_cases/{helpers_lte => _helpers_lte}/main.dust | 0 7 files changed, 6 insertions(+), 1 deletion(-) rename js/test_cases/{helpers_gte => _helpers_gte}/input1.json (100%) rename js/test_cases/{helpers_gte => _helpers_gte}/main.dust (100%) rename js/test_cases/{helpers_lt => _helpers_lt}/input1.json (100%) rename js/test_cases/{helpers_lt => _helpers_lt}/main.dust (100%) rename js/test_cases/{helpers_lte => _helpers_lte}/input1.json (100%) rename js/test_cases/{helpers_lte => _helpers_lte}/main.dust (100%) diff --git a/js/run_compliance_suite.bash b/js/run_compliance_suite.bash index a13d025..c7d5296 100755 --- a/js/run_compliance_suite.bash +++ b/js/run_compliance_suite.bash @@ -48,7 +48,12 @@ while read -r test_group; do fi set -e 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 echo "$failed_count failed tests" diff --git a/js/test_cases/helpers_gte/input1.json b/js/test_cases/_helpers_gte/input1.json similarity index 100% rename from js/test_cases/helpers_gte/input1.json rename to js/test_cases/_helpers_gte/input1.json diff --git a/js/test_cases/helpers_gte/main.dust b/js/test_cases/_helpers_gte/main.dust similarity index 100% rename from js/test_cases/helpers_gte/main.dust rename to js/test_cases/_helpers_gte/main.dust diff --git a/js/test_cases/helpers_lt/input1.json b/js/test_cases/_helpers_lt/input1.json similarity index 100% rename from js/test_cases/helpers_lt/input1.json rename to js/test_cases/_helpers_lt/input1.json diff --git a/js/test_cases/helpers_lt/main.dust b/js/test_cases/_helpers_lt/main.dust similarity index 100% rename from js/test_cases/helpers_lt/main.dust rename to js/test_cases/_helpers_lt/main.dust diff --git a/js/test_cases/helpers_lte/input1.json b/js/test_cases/_helpers_lte/input1.json similarity index 100% rename from js/test_cases/helpers_lte/input1.json rename to js/test_cases/_helpers_lte/input1.json diff --git a/js/test_cases/helpers_lte/main.dust b/js/test_cases/_helpers_lte/main.dust similarity index 100% rename from js/test_cases/helpers_lte/main.dust rename to js/test_cases/_helpers_lte/main.dust From b53a9e1837b6af676513a305b9b74adef0ec4184 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 17:17:43 -0400 Subject: [PATCH 56/63] Cleaning up and factoring out reused code in the renderer. --- src/renderer/renderer.rs | 258 ++++++++++++++++----------------------- 1 file changed, 107 insertions(+), 151 deletions(-) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index cd83681..dc190dc 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -77,6 +77,18 @@ impl<'a> DustRenderer<'a> { self.render_body(&main_template.contents, breadcrumbs, &new_blocks) } + fn render_maybe_body( + &'a self, + body: &'a Option, + breadcrumbs: &Vec<&'a dyn ContextElement>, + blocks: &'a InlinePartialTreeElement<'a>, + ) -> Result { + match body { + None => Ok("".to_owned()), + Some(body) => Ok(self.render_body(body, breadcrumbs, blocks)?), + } + } + fn render_body( &'a self, body: &'a Body, @@ -130,16 +142,13 @@ impl<'a> DustRenderer<'a> { } DustTag::DTSection(container) => { 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() { // Oddly enough if the value is falsey (like // an empty array or null), Dust uses the // original context before walking the path as // the context for rendering the else block - return match &container.else_contents { - Some(body) => self.render_body(&body, breadcrumbs, blocks), - None => Ok("".to_owned()), - }; + return self.render_maybe_body(&container.else_contents, breadcrumbs, blocks); } else { match &container.contents { None => return Ok("".to_owned()), @@ -160,33 +169,21 @@ impl<'a> DustRenderer<'a> { } DustTag::DTExists(container) => { let val = walk_path(breadcrumbs, &container.path.keys); - let loop_elements: Vec<&dyn ContextElement> = self.get_loop_elements(val); - if loop_elements.is_empty() { - return match &container.else_contents { - Some(body) => self.render_body(&body, breadcrumbs, blocks), - None => Ok("".to_owned()), - }; + let loop_elements: Vec<&dyn ContextElement> = Self::get_loop_elements(val); + return if loop_elements.is_empty() { + self.render_maybe_body(&container.else_contents, breadcrumbs, blocks) } else { - return match &container.contents { - None => Ok("".to_owned()), - Some(body) => self.render_body(&body, breadcrumbs, blocks), - }; - } + self.render_maybe_body(&container.contents, breadcrumbs, blocks) + }; } DustTag::DTNotExists(container) => { let val = walk_path(breadcrumbs, &container.path.keys); - let loop_elements: Vec<&dyn ContextElement> = self.get_loop_elements(val); - if !loop_elements.is_empty() { - return match &container.else_contents { - Some(body) => self.render_body(&body, breadcrumbs, blocks), - None => Ok("".to_owned()), - }; + let loop_elements: Vec<&dyn ContextElement> = Self::get_loop_elements(val); + return if !loop_elements.is_empty() { + self.render_maybe_body(&container.else_contents, breadcrumbs, blocks) } else { - return match &container.contents { - None => Ok("".to_owned()), - Some(body) => self.render_body(&body, breadcrumbs, blocks), - }; - } + self.render_maybe_body(&container.contents, breadcrumbs, blocks) + }; } DustTag::DTPartial(partial) => { if partial.params.is_empty() { @@ -202,34 +199,19 @@ impl<'a> DustRenderer<'a> { return Ok(rendered_content); } } - DustTag::DTInlinePartial(named_block) => { + DustTag::DTInlinePartial(_named_block) => { // Inline partials are blank during rendering (they get injected into blocks) return Ok("".to_owned()); } DustTag::DTBlock(named_block) => { - match blocks.get_block(named_block.name) { - None => match &named_block.contents { - None => return Ok("".to_owned()), - 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); - } - }, + return match blocks.get_block(named_block.name) { + None => self.render_maybe_body(&named_block.contents, breadcrumbs, blocks), + Some(interior) => self.render_maybe_body(interior, breadcrumbs, blocks), }; } DustTag::DTHelperEquals(parameterized_block) => { - let param_map: HashMap<&str, &RValue<'a>> = parameterized_block - .params - .iter() - .map(|pair: &KVPair<'a>| (pair.key, &pair.value)) - .collect(); + 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 @@ -244,47 +226,31 @@ impl<'a> DustRenderer<'a> { }; } - let left_side: Result<&dyn ContextElement, WalkError> = match param_map.get("key") { - None => return Ok("".to_owned()), - Some(rval) => match rval { - RValue::RVString(text) => Ok(text), - RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys), - RValue::RVPositiveInteger(num) => Ok(num), - }, - }; - let right_side: Result<&dyn ContextElement, WalkError> = - match param_map.get("value") { - None => Err(WalkError::CantWalk), - Some(rval) => match rval { - RValue::RVString(text) => Ok(text), - RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys), - RValue::RVPositiveInteger(num) => Ok(num), - }, + 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 { - match ¶meterized_block.contents { - None => return Ok("".to_owned()), - Some(body) => { - let rendered_content = self.render_body(body, breadcrumbs, blocks)?; - return Ok(rendered_content); - } - } + return self.render_maybe_body( + ¶meterized_block.contents, + breadcrumbs, + blocks, + ); } else { - match ¶meterized_block.else_contents { - None => return Ok("".to_owned()), - Some(body) => { - let rendered_content = self.render_body(body, breadcrumbs, blocks)?; - return Ok(rendered_content); - } - } + return self.render_maybe_body( + ¶meterized_block.else_contents, + breadcrumbs, + blocks, + ); } } DustTag::DTHelperNotEquals(parameterized_block) => { - let param_map: HashMap<&str, &RValue<'a>> = parameterized_block - .params - .iter() - .map(|pair: &KVPair<'a>| (pair.key, &pair.value)) - .collect(); + 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 @@ -299,64 +265,39 @@ impl<'a> DustRenderer<'a> { }; } - let left_side: Result<&dyn ContextElement, WalkError> = match param_map.get("key") { - None => return Ok("".to_owned()), - Some(rval) => match rval { - RValue::RVString(text) => Ok(text), - RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys), - RValue::RVPositiveInteger(num) => Ok(num), - }, - }; - let right_side: Result<&dyn ContextElement, WalkError> = - match param_map.get("value") { - None => Err(WalkError::CantWalk), - Some(rval) => match rval { - RValue::RVString(text) => Ok(text), - RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys), - RValue::RVPositiveInteger(num) => Ok(num), - }, + 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 { - match ¶meterized_block.contents { - None => return Ok("".to_owned()), - Some(body) => { - let rendered_content = self.render_body(body, breadcrumbs, blocks)?; - return Ok(rendered_content); - } - } + return self.render_maybe_body( + ¶meterized_block.contents, + breadcrumbs, + blocks, + ); } else { - match ¶meterized_block.else_contents { - None => return Ok("".to_owned()), - Some(body) => { - let rendered_content = self.render_body(body, breadcrumbs, blocks)?; - return Ok(rendered_content); - } - } + return self.render_maybe_body( + ¶meterized_block.else_contents, + breadcrumbs, + blocks, + ); } } DustTag::DTHelperGreaterThan(parameterized_block) => { - let param_map: HashMap<&str, &RValue<'a>> = parameterized_block - .params - .iter() - .map(|pair: &KVPair<'a>| (pair.key, &pair.value)) - .collect(); - let left_side: Result<&dyn ContextElement, WalkError> = match param_map.get("key") { - None => return Ok("".to_owned()), - Some(rval) => match rval { - RValue::RVString(text) => Ok(text), - RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys), - RValue::RVPositiveInteger(num) => Ok(num), - }, - }; - let right_side: Result<&dyn ContextElement, WalkError> = - match param_map.get("value") { - None => Err(WalkError::CantWalk), - Some(rval) => match rval { - RValue::RVString(text) => Ok(text), - RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys), - RValue::RVPositiveInteger(num) => Ok(num), - }, + 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(_)) => match ¶meterized_block.else_contents { None => return Ok("".to_owned()), @@ -367,23 +308,17 @@ impl<'a> DustRenderer<'a> { }, (Ok(left_side_unwrapped), Ok(right_side_unwrapped)) => { if left_side_unwrapped > right_side_unwrapped { - match ¶meterized_block.contents { - None => return Ok("".to_owned()), - Some(body) => { - let rendered_content = - self.render_body(body, breadcrumbs, blocks)?; - return Ok(rendered_content); - } - } + return self.render_maybe_body( + ¶meterized_block.contents, + breadcrumbs, + blocks, + ); } else { - match ¶meterized_block.else_contents { - None => return Ok("".to_owned()), - Some(body) => { - let rendered_content = - self.render_body(body, breadcrumbs, blocks)?; - return Ok(rendered_content); - } - } + return self.render_maybe_body( + ¶meterized_block.else_contents, + breadcrumbs, + blocks, + ); } } } @@ -398,7 +333,6 @@ impl<'a> DustRenderer<'a> { /// If the value is falsey, and therefore should render the else /// block, this will return an empty vector. fn get_loop_elements<'b>( - &'a self, walk_result: Result<&'b dyn ContextElement, WalkError>, ) -> Vec<&'b dyn ContextElement> { match walk_result { @@ -419,6 +353,28 @@ impl<'a> DustRenderer<'a> { }, } } + + fn get_rval_map<'b>(params: &'b Vec>) -> 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> { + 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)] From 7126e83d9ac492c24557a5902ca491dff6697e4c Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 17:53:57 -0400 Subject: [PATCH 57/63] Added tests for non-congruous arrays. --- js/test_cases/_helpers_gte/input1.json | 12 ++++++++++++ js/test_cases/_helpers_gte/main.dust | 3 +++ js/test_cases/_helpers_lt/input1.json | 12 ++++++++++++ js/test_cases/_helpers_lt/main.dust | 3 +++ js/test_cases/_helpers_lte/input1.json | 12 ++++++++++++ js/test_cases/_helpers_lte/main.dust | 3 +++ js/test_cases/helpers_gt/README.md | 3 +++ js/test_cases/helpers_gt/input1.json | 12 ++++++++++++ js/test_cases/helpers_gt/main.dust | 10 +++++++--- 9 files changed, 67 insertions(+), 3 deletions(-) diff --git a/js/test_cases/_helpers_gte/input1.json b/js/test_cases/_helpers_gte/input1.json index bfbbd94..de2ac09 100644 --- a/js/test_cases/_helpers_gte/input1.json +++ b/js/test_cases/_helpers_gte/input1.json @@ -11,6 +11,13 @@ 5, 7 ], + "array_lower_with_array": [ + 3, + 5, + [ + 7 + ] + ], "copy_array_lower": [ 3, 5, @@ -20,6 +27,11 @@ 8, 9 ], + "array_higher_longer": [ + 8, + 9, + 1 + ], "some_obj": { "name": "cat" }, diff --git a/js/test_cases/_helpers_gte/main.dust b/js/test_cases/_helpers_gte/main.dust index a3eb601..ed6938c 100644 --- a/js/test_cases/_helpers_gte/main.dust +++ b/js/test_cases/_helpers_gte/main.dust @@ -33,3 +33,6 @@ beta is {beta}{~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} diff --git a/js/test_cases/_helpers_lt/input1.json b/js/test_cases/_helpers_lt/input1.json index bfbbd94..de2ac09 100644 --- a/js/test_cases/_helpers_lt/input1.json +++ b/js/test_cases/_helpers_lt/input1.json @@ -11,6 +11,13 @@ 5, 7 ], + "array_lower_with_array": [ + 3, + 5, + [ + 7 + ] + ], "copy_array_lower": [ 3, 5, @@ -20,6 +27,11 @@ 8, 9 ], + "array_higher_longer": [ + 8, + 9, + 1 + ], "some_obj": { "name": "cat" }, diff --git a/js/test_cases/_helpers_lt/main.dust b/js/test_cases/_helpers_lt/main.dust index 305c3c4..c1ab4e4 100644 --- a/js/test_cases/_helpers_lt/main.dust +++ b/js/test_cases/_helpers_lt/main.dust @@ -33,3 +33,6 @@ beta is {beta}{~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} diff --git a/js/test_cases/_helpers_lte/input1.json b/js/test_cases/_helpers_lte/input1.json index bfbbd94..de2ac09 100644 --- a/js/test_cases/_helpers_lte/input1.json +++ b/js/test_cases/_helpers_lte/input1.json @@ -11,6 +11,13 @@ 5, 7 ], + "array_lower_with_array": [ + 3, + 5, + [ + 7 + ] + ], "copy_array_lower": [ 3, 5, @@ -20,6 +27,11 @@ 8, 9 ], + "array_higher_longer": [ + 8, + 9, + 1 + ], "some_obj": { "name": "cat" }, diff --git a/js/test_cases/_helpers_lte/main.dust b/js/test_cases/_helpers_lte/main.dust index dcf0ab5..2ea2f24 100644 --- a/js/test_cases/_helpers_lte/main.dust +++ b/js/test_cases/_helpers_lte/main.dust @@ -33,3 +33,6 @@ beta is {beta}{~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} diff --git a/js/test_cases/helpers_gt/README.md b/js/test_cases/helpers_gt/README.md index f418aed..527bae3 100644 --- a/js/test_cases/helpers_gt/README.md +++ b/js/test_cases/helpers_gt/README.md @@ -2,6 +2,9 @@ Based on my tests, it appears dust is sorting based on ascii-table values. This 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 ------------ diff --git a/js/test_cases/helpers_gt/input1.json b/js/test_cases/helpers_gt/input1.json index bfbbd94..de2ac09 100644 --- a/js/test_cases/helpers_gt/input1.json +++ b/js/test_cases/helpers_gt/input1.json @@ -11,6 +11,13 @@ 5, 7 ], + "array_lower_with_array": [ + 3, + 5, + [ + 7 + ] + ], "copy_array_lower": [ 3, 5, @@ -20,6 +27,11 @@ 8, 9 ], + "array_higher_longer": [ + 8, + 9, + 1 + ], "some_obj": { "name": "cat" }, diff --git a/js/test_cases/helpers_gt/main.dust b/js/test_cases/helpers_gt/main.dust index c7b3699..758b231 100644 --- a/js/test_cases/helpers_gt/main.dust +++ b/js/test_cases/helpers_gt/main.dust @@ -21,9 +21,9 @@ beta is {beta}{~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 not working in rust implementation yet -{@gt key="☃" value="☄"}"☃" is greater than "☄"{:else}"☃" is less than or equal to "☄"{/gt}{~n} -!} + Commented out because unicode not working in rust implementation yet + {@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} @@ -36,3 +36,7 @@ Commented out because unicode not working in rust implementation yet {@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} From f640cb044097b5eb5c62678c123796dd9ebfb86d Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 18:15:03 -0400 Subject: [PATCH 58/63] Fixed greater than helper by using rust's PartialOrd implementation for Vec. --- src/bin.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/bin.rs b/src/bin.rs index 1f698a9..4274501 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -220,7 +220,7 @@ impl CompareContextElement for serde_json::Value { serde_json::Value::String(self_string), serde_json::Value::String(other_string), ) => self_string.partial_cmp(other_string), - // TODO: Non-scalar types + (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, }; } @@ -242,3 +242,17 @@ impl CompareContextElement for serde_json::Value { 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) -> Vec<&dyn ContextElement> { + array.iter().map(|v| v as _).collect() +} From 0cfe67311ea7fbe17501f71954677a8010655c73 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 18:21:10 -0400 Subject: [PATCH 59/63] Comment out the unicode literals test because unicode breaks nom. Will have to write my own parsers to handle unicode. --- js/test_cases/_helpers_gte/main.dust | 3 +++ js/test_cases/_helpers_lt/main.dust | 3 +++ js/test_cases/_helpers_lte/main.dust | 3 +++ js/test_cases/helpers_gt/main.dust | 6 +++--- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/js/test_cases/_helpers_gte/main.dust b/js/test_cases/_helpers_gte/main.dust index ed6938c..b0ee031 100644 --- a/js/test_cases/_helpers_gte/main.dust +++ b/js/test_cases/_helpers_gte/main.dust @@ -20,7 +20,10 @@ beta is {beta}{~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} diff --git a/js/test_cases/_helpers_lt/main.dust b/js/test_cases/_helpers_lt/main.dust index c1ab4e4..f2b81d1 100644 --- a/js/test_cases/_helpers_lt/main.dust +++ b/js/test_cases/_helpers_lt/main.dust @@ -20,7 +20,10 @@ beta is {beta}{~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} diff --git a/js/test_cases/_helpers_lte/main.dust b/js/test_cases/_helpers_lte/main.dust index 2ea2f24..72ee337 100644 --- a/js/test_cases/_helpers_lte/main.dust +++ b/js/test_cases/_helpers_lte/main.dust @@ -20,7 +20,10 @@ beta is {beta}{~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} diff --git a/js/test_cases/helpers_gt/main.dust b/js/test_cases/helpers_gt/main.dust index 758b231..0bd9fca 100644 --- a/js/test_cases/helpers_gt/main.dust +++ b/js/test_cases/helpers_gt/main.dust @@ -21,9 +21,9 @@ beta is {beta}{~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 not working in rust implementation yet - {@gt key="☃" value="☄"}"☃" is greater than "☄"{:else}"☃" 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} From 62cd73f9c9c38d9999db0416dd4fcb92b66cc10e Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 18:28:23 -0400 Subject: [PATCH 60/63] Initial attempt at implementation, tests not working. --- .../{_helpers_gte => helpers_gte}/input1.json | 0 .../{_helpers_gte => helpers_gte}/main.dust | 0 src/renderer/renderer.rs | 50 ++++++++++++++++--- 3 files changed, 43 insertions(+), 7 deletions(-) rename js/test_cases/{_helpers_gte => helpers_gte}/input1.json (100%) rename js/test_cases/{_helpers_gte => helpers_gte}/main.dust (100%) diff --git a/js/test_cases/_helpers_gte/input1.json b/js/test_cases/helpers_gte/input1.json similarity index 100% rename from js/test_cases/_helpers_gte/input1.json rename to js/test_cases/helpers_gte/input1.json diff --git a/js/test_cases/_helpers_gte/main.dust b/js/test_cases/helpers_gte/main.dust similarity index 100% rename from js/test_cases/_helpers_gte/main.dust rename to js/test_cases/helpers_gte/main.dust diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index dc190dc..d7c1df6 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -299,13 +299,13 @@ impl<'a> DustRenderer<'a> { Self::get_rval(breadcrumbs, ¶m_map, "value") .unwrap_or(Err(WalkError::CantWalk)); match (left_side, right_side) { - (Err(_), _) | (_, Err(_)) => match ¶meterized_block.else_contents { - None => return Ok("".to_owned()), - Some(body) => { - let rendered_content = self.render_body(body, breadcrumbs, blocks)?; - return Ok(rendered_content); - } - }, + (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( @@ -323,6 +323,42 @@ impl<'a> DustRenderer<'a> { } } } + DustTag::DTHelperGreaterThenOrEquals(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.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, + ); + } + } + } + } _ => (), // TODO: Implement the rest } Ok("".to_owned()) From 496a5e0ac7096b4eb61a0b0153a6760771da80c2 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 18:36:47 -0400 Subject: [PATCH 61/63] Had to manually interpret the `partial_cmp` output instead of using the built-in operators since gte confusingly returns true for mismatched values. --- src/renderer/renderer.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index d7c1df6..ec645f0 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -14,7 +14,7 @@ use crate::renderer::inline_partial_tree::extract_inline_partials; use crate::renderer::inline_partial_tree::InlinePartialTreeElement; use crate::renderer::parameters_context::ParametersContext; use crate::renderer::walking::walk_path; -use std::collections::HashMap; +use std::{cmp::Ordering, collections::HashMap}; #[derive(Clone, Debug)] pub struct CompiledTemplate<'a> { @@ -337,25 +337,25 @@ impl<'a> DustRenderer<'a> { match (left_side, right_side) { (Err(_), _) | (_, Err(_)) => { return self.render_maybe_body( - ¶meterized_block.contents, + ¶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( + 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, - ); - } + ), + }; } } } From 30b732404971a5320164e82c97784ad316c2bf83 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 19:05:03 -0400 Subject: [PATCH 62/63] Implement less-than. --- .../{_helpers_lt => helpers_lt}/input1.json | 0 .../{_helpers_lt => helpers_lt}/main.dust | 0 src/parser/parser.rs | 8 ++-- src/renderer/inline_partial_tree.rs | 4 +- src/renderer/renderer.rs | 38 ++++++++++++++++++- 5 files changed, 43 insertions(+), 7 deletions(-) rename js/test_cases/{_helpers_lt => helpers_lt}/input1.json (100%) rename js/test_cases/{_helpers_lt => helpers_lt}/main.dust (100%) diff --git a/js/test_cases/_helpers_lt/input1.json b/js/test_cases/helpers_lt/input1.json similarity index 100% rename from js/test_cases/_helpers_lt/input1.json rename to js/test_cases/helpers_lt/input1.json diff --git a/js/test_cases/_helpers_lt/main.dust b/js/test_cases/helpers_lt/main.dust similarity index 100% rename from js/test_cases/_helpers_lt/main.dust rename to js/test_cases/helpers_lt/main.dust diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 3b8b30c..a742fa3 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -38,8 +38,8 @@ pub enum DustTag<'a> { DTHelperNotEquals(ParameterizedBlock<'a>), DTHelperGreaterThan(ParameterizedBlock<'a>), DTHelperLessThan(ParameterizedBlock<'a>), - DTHelperGreaterThenOrEquals(ParameterizedBlock<'a>), - DTHelperLessThenOrEquals(ParameterizedBlock<'a>), + DTHelperGreaterThanOrEquals(ParameterizedBlock<'a>), + DTHelperLessThanOrEquals(ParameterizedBlock<'a>), } #[derive(Clone, Debug, PartialEq)] @@ -162,8 +162,8 @@ fn dust_tag(i: &str) -> IResult<&str, DustTag> { named_block("{+", DustTag::DTBlock), named_block("{<", DustTag::DTInlinePartial), partial("{>", DustTag::DTPartial), - parameterized_block("{@", "gte", DustTag::DTHelperGreaterThenOrEquals), - parameterized_block("{@", "lte", DustTag::DTHelperLessThenOrEquals), + parameterized_block("{@", "gte", DustTag::DTHelperGreaterThanOrEquals), + parameterized_block("{@", "lte", DustTag::DTHelperLessThanOrEquals), parameterized_block("{@", "eq", DustTag::DTHelperEquals), parameterized_block("{@", "ne", DustTag::DTHelperNotEquals), parameterized_block("{@", "gt", DustTag::DTHelperGreaterThan), diff --git a/src/renderer/inline_partial_tree.rs b/src/renderer/inline_partial_tree.rs index b37d40b..ff7220b 100644 --- a/src/renderer/inline_partial_tree.rs +++ b/src/renderer/inline_partial_tree.rs @@ -139,7 +139,7 @@ fn extract_inline_partials_from_tag<'a, 'b>( Some(body) => extract_inline_partials_from_body(blocks, &body), }; } - DustTag::DTHelperGreaterThenOrEquals(parameterized_block) => { + DustTag::DTHelperGreaterThanOrEquals(parameterized_block) => { match ¶meterized_block.contents { None => (), Some(body) => extract_inline_partials_from_body(blocks, &body), @@ -149,7 +149,7 @@ fn extract_inline_partials_from_tag<'a, 'b>( Some(body) => extract_inline_partials_from_body(blocks, &body), }; } - DustTag::DTHelperLessThenOrEquals(parameterized_block) => { + DustTag::DTHelperLessThanOrEquals(parameterized_block) => { match ¶meterized_block.contents { None => (), Some(body) => extract_inline_partials_from_body(blocks, &body), diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index ec645f0..3e29c6f 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -323,7 +323,7 @@ impl<'a> DustRenderer<'a> { } } } - DustTag::DTHelperGreaterThenOrEquals(parameterized_block) => { + 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> = @@ -359,6 +359,42 @@ impl<'a> DustRenderer<'a> { } } } + 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, + ); + } + } + } + } _ => (), // TODO: Implement the rest } Ok("".to_owned()) From ce8b73f87ab085b3ee8a75b614e415377d185a4e Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 16 May 2020 19:06:40 -0400 Subject: [PATCH 63/63] Implement less than or equals. --- .../{_helpers_lte => helpers_lte}/input1.json | 0 .../{_helpers_lte => helpers_lte}/main.dust | 0 src/renderer/renderer.rs | 36 +++++++++++++++++++ 3 files changed, 36 insertions(+) rename js/test_cases/{_helpers_lte => helpers_lte}/input1.json (100%) rename js/test_cases/{_helpers_lte => helpers_lte}/main.dust (100%) diff --git a/js/test_cases/_helpers_lte/input1.json b/js/test_cases/helpers_lte/input1.json similarity index 100% rename from js/test_cases/_helpers_lte/input1.json rename to js/test_cases/helpers_lte/input1.json diff --git a/js/test_cases/_helpers_lte/main.dust b/js/test_cases/helpers_lte/main.dust similarity index 100% rename from js/test_cases/_helpers_lte/main.dust rename to js/test_cases/helpers_lte/main.dust diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 3e29c6f..f44022c 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -395,6 +395,42 @@ impl<'a> DustRenderer<'a> { } } } + 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 } Ok("".to_owned())