From 0fcb70927c05be622b87ddfe351ae5f8c6e219d8 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 7 May 2020 19:12:45 -0400 Subject: [PATCH 01/27] Add an initial test of partial parameters --- js/test_cases/partial_simple/greeting.dust | 1 + js/test_cases/partial_simple/input1.json | 4 ++++ js/test_cases/partial_simple/main.dust | 3 +++ 3 files changed, 8 insertions(+) create mode 100644 js/test_cases/partial_simple/greeting.dust create mode 100644 js/test_cases/partial_simple/input1.json create mode 100644 js/test_cases/partial_simple/main.dust diff --git a/js/test_cases/partial_simple/greeting.dust b/js/test_cases/partial_simple/greeting.dust new file mode 100644 index 0000000..f5a4adb --- /dev/null +++ b/js/test_cases/partial_simple/greeting.dust @@ -0,0 +1 @@ +Hello {name}{?item}, nice {item}{/item}!{~n} diff --git a/js/test_cases/partial_simple/input1.json b/js/test_cases/partial_simple/input1.json new file mode 100644 index 0000000..272dfa5 --- /dev/null +++ b/js/test_cases/partial_simple/input1.json @@ -0,0 +1,4 @@ +{"people": [ + {"name": "Alice", "item": "cat"}, + {"name": "Bob"} +]} diff --git a/js/test_cases/partial_simple/main.dust b/js/test_cases/partial_simple/main.dust new file mode 100644 index 0000000..5961053 --- /dev/null +++ b/js/test_cases/partial_simple/main.dust @@ -0,0 +1,3 @@ +{#people} +{>greeting item="shoes"/} +{/people} From 7087157ed3ccbdc24fcfb2ed831826f960e265d7 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 7 May 2020 19:28:22 -0400 Subject: [PATCH 02/27] Add partial test for walking up context vs parameters --- js/test_cases/partial_simple/README.md | 1 + js/test_cases/partial_simple/input2.json | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 js/test_cases/partial_simple/README.md create mode 100644 js/test_cases/partial_simple/input2.json diff --git a/js/test_cases/partial_simple/README.md b/js/test_cases/partial_simple/README.md new file mode 100644 index 0000000..d8908e5 --- /dev/null +++ b/js/test_cases/partial_simple/README.md @@ -0,0 +1 @@ +Partial parameters do not take highest priority but also do not seem to take lowest priority. Current theory is parameters are inserted 1 level above the current context (so parameters would be references before walking up). diff --git a/js/test_cases/partial_simple/input2.json b/js/test_cases/partial_simple/input2.json new file mode 100644 index 0000000..02913d0 --- /dev/null +++ b/js/test_cases/partial_simple/input2.json @@ -0,0 +1,5 @@ +{"people": [ + {"name": "Alice"}, + {"name": "Bob"} +], + "item": "cat"} From f47b91f8e75932ff59dd84dc85c0adea94077f28 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 8 May 2020 18:19:33 -0400 Subject: [PATCH 03/27] Starting a more complicated test for partials to investigate scoping. --- js/test_cases/partial_jump_around/input1.json | 27 +++++++++++++++++++ js/test_cases/partial_jump_around/main.dust | 3 +++ .../partial_jump_around/partialone.dust | 3 +++ .../partial_jump_around/partialtwo.dust | 7 +++++ 4 files changed, 40 insertions(+) create mode 100644 js/test_cases/partial_jump_around/input1.json create mode 100644 js/test_cases/partial_jump_around/main.dust create mode 100644 js/test_cases/partial_jump_around/partialone.dust create mode 100644 js/test_cases/partial_jump_around/partialtwo.dust diff --git a/js/test_cases/partial_jump_around/input1.json b/js/test_cases/partial_jump_around/input1.json new file mode 100644 index 0000000..5b9dd75 --- /dev/null +++ b/js/test_cases/partial_jump_around/input1.json @@ -0,0 +1,27 @@ +{ + "level3": { + "v3": "3", + "v4": "3", + "v5": "3", + "level4": { + "v4": "4", + "v5": "4", + "level5": { + "v5": "5" + } + } + }, + "level1": { + "v1": "1", + "v2": "1", + "v3": "1", + "v4": "1", + "v5": "1", + "level2": { + "v2": "2", + "v3": "2", + "v4": "2", + "v5": "2" + } + } +} diff --git a/js/test_cases/partial_jump_around/main.dust b/js/test_cases/partial_jump_around/main.dust new file mode 100644 index 0000000..6e8dad8 --- /dev/null +++ b/js/test_cases/partial_jump_around/main.dust @@ -0,0 +1,3 @@ +{#level1.level2} +{>partialone /} +{/level1.level2} diff --git a/js/test_cases/partial_jump_around/partialone.dust b/js/test_cases/partial_jump_around/partialone.dust new file mode 100644 index 0000000..b7b377b --- /dev/null +++ b/js/test_cases/partial_jump_around/partialone.dust @@ -0,0 +1,3 @@ +{#level3.level4} + {>partialtwo /} +{/level3.level4} diff --git a/js/test_cases/partial_jump_around/partialtwo.dust b/js/test_cases/partial_jump_around/partialtwo.dust new file mode 100644 index 0000000..d8f7866 --- /dev/null +++ b/js/test_cases/partial_jump_around/partialtwo.dust @@ -0,0 +1,7 @@ +{#level5} + {v1}{~n} + {v2}{~n} + {v3}{~n} + {v4}{~n} + {v5}{~n} +{/level5} From 50c03b4f0f74cf551bff2c404628482f1b871d45 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 8 May 2020 18:34:49 -0400 Subject: [PATCH 04/27] Expanding the partial jump around tests. --- js/test_cases/partial_jump_around/main.dust | 3 --- .../input1.json | 0 .../partial_jump_around_one/main.dust | 3 +++ .../partialone.dust | 2 +- .../partialtwo.dust | 0 .../partial_jump_around_two/input1.json | 27 +++++++++++++++++++ .../partial_jump_around_two/main.dust | 3 +++ .../partial_jump_around_two/partialone.dust | 3 +++ .../partial_jump_around_two/partialtwo.dust | 7 +++++ 9 files changed, 44 insertions(+), 4 deletions(-) delete mode 100644 js/test_cases/partial_jump_around/main.dust rename js/test_cases/{partial_jump_around => partial_jump_around_one}/input1.json (100%) create mode 100644 js/test_cases/partial_jump_around_one/main.dust rename js/test_cases/{partial_jump_around => partial_jump_around_one}/partialone.dust (62%) rename js/test_cases/{partial_jump_around => partial_jump_around_one}/partialtwo.dust (100%) create mode 100644 js/test_cases/partial_jump_around_two/input1.json create mode 100644 js/test_cases/partial_jump_around_two/main.dust create mode 100644 js/test_cases/partial_jump_around_two/partialone.dust create mode 100644 js/test_cases/partial_jump_around_two/partialtwo.dust diff --git a/js/test_cases/partial_jump_around/main.dust b/js/test_cases/partial_jump_around/main.dust deleted file mode 100644 index 6e8dad8..0000000 --- a/js/test_cases/partial_jump_around/main.dust +++ /dev/null @@ -1,3 +0,0 @@ -{#level1.level2} -{>partialone /} -{/level1.level2} diff --git a/js/test_cases/partial_jump_around/input1.json b/js/test_cases/partial_jump_around_one/input1.json similarity index 100% rename from js/test_cases/partial_jump_around/input1.json rename to js/test_cases/partial_jump_around_one/input1.json diff --git a/js/test_cases/partial_jump_around_one/main.dust b/js/test_cases/partial_jump_around_one/main.dust new file mode 100644 index 0000000..e6ad4e4 --- /dev/null +++ b/js/test_cases/partial_jump_around_one/main.dust @@ -0,0 +1,3 @@ +{#level1.level2} +{>partialone v1="a" v2="a" v3="a" v4="a" v5="a"/} +{/level1.level2} diff --git a/js/test_cases/partial_jump_around/partialone.dust b/js/test_cases/partial_jump_around_one/partialone.dust similarity index 62% rename from js/test_cases/partial_jump_around/partialone.dust rename to js/test_cases/partial_jump_around_one/partialone.dust index b7b377b..a53b2eb 100644 --- a/js/test_cases/partial_jump_around/partialone.dust +++ b/js/test_cases/partial_jump_around_one/partialone.dust @@ -1,3 +1,3 @@ {#level3.level4} - {>partialtwo /} + {>partialtwo/} {/level3.level4} diff --git a/js/test_cases/partial_jump_around/partialtwo.dust b/js/test_cases/partial_jump_around_one/partialtwo.dust similarity index 100% rename from js/test_cases/partial_jump_around/partialtwo.dust rename to js/test_cases/partial_jump_around_one/partialtwo.dust diff --git a/js/test_cases/partial_jump_around_two/input1.json b/js/test_cases/partial_jump_around_two/input1.json new file mode 100644 index 0000000..5b9dd75 --- /dev/null +++ b/js/test_cases/partial_jump_around_two/input1.json @@ -0,0 +1,27 @@ +{ + "level3": { + "v3": "3", + "v4": "3", + "v5": "3", + "level4": { + "v4": "4", + "v5": "4", + "level5": { + "v5": "5" + } + } + }, + "level1": { + "v1": "1", + "v2": "1", + "v3": "1", + "v4": "1", + "v5": "1", + "level2": { + "v2": "2", + "v3": "2", + "v4": "2", + "v5": "2" + } + } +} diff --git a/js/test_cases/partial_jump_around_two/main.dust b/js/test_cases/partial_jump_around_two/main.dust new file mode 100644 index 0000000..e6ad4e4 --- /dev/null +++ b/js/test_cases/partial_jump_around_two/main.dust @@ -0,0 +1,3 @@ +{#level1.level2} +{>partialone v1="a" v2="a" v3="a" v4="a" v5="a"/} +{/level1.level2} diff --git a/js/test_cases/partial_jump_around_two/partialone.dust b/js/test_cases/partial_jump_around_two/partialone.dust new file mode 100644 index 0000000..0642002 --- /dev/null +++ b/js/test_cases/partial_jump_around_two/partialone.dust @@ -0,0 +1,3 @@ +{#level3.level4} + {>partialtwo v1="b" v2="b" v3="b" v4="b" v5="b" /} +{/level3.level4} diff --git a/js/test_cases/partial_jump_around_two/partialtwo.dust b/js/test_cases/partial_jump_around_two/partialtwo.dust new file mode 100644 index 0000000..d8f7866 --- /dev/null +++ b/js/test_cases/partial_jump_around_two/partialtwo.dust @@ -0,0 +1,7 @@ +{#level5} + {v1}{~n} + {v2}{~n} + {v3}{~n} + {v4}{~n} + {v5}{~n} +{/level5} From ec321a17ca09ab7029cf494e746e4fd2d756f8d4 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 8 May 2020 18:48:33 -0400 Subject: [PATCH 05/27] Adding a v0 to partial_jump_around_one to prove that the partial parameters are inserted 1 level above the current context. --- js/test_cases/partial_jump_around_one/input1.json | 1 + js/test_cases/partial_jump_around_one/main.dust | 2 +- js/test_cases/partial_jump_around_one/partialtwo.dust | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/js/test_cases/partial_jump_around_one/input1.json b/js/test_cases/partial_jump_around_one/input1.json index 5b9dd75..4170048 100644 --- a/js/test_cases/partial_jump_around_one/input1.json +++ b/js/test_cases/partial_jump_around_one/input1.json @@ -1,4 +1,5 @@ { + "v0": "0", "level3": { "v3": "3", "v4": "3", diff --git a/js/test_cases/partial_jump_around_one/main.dust b/js/test_cases/partial_jump_around_one/main.dust index e6ad4e4..004474e 100644 --- a/js/test_cases/partial_jump_around_one/main.dust +++ b/js/test_cases/partial_jump_around_one/main.dust @@ -1,3 +1,3 @@ {#level1.level2} -{>partialone v1="a" v2="a" v3="a" v4="a" v5="a"/} +{>partialone v0="a" v1="a" v2="a" v3="a" v4="a" v5="a"/} {/level1.level2} diff --git a/js/test_cases/partial_jump_around_one/partialtwo.dust b/js/test_cases/partial_jump_around_one/partialtwo.dust index d8f7866..bb751b3 100644 --- a/js/test_cases/partial_jump_around_one/partialtwo.dust +++ b/js/test_cases/partial_jump_around_one/partialtwo.dust @@ -1,4 +1,5 @@ {#level5} + {v0}{~n} {v1}{~n} {v2}{~n} {v3}{~n} From f240b877b86370ea0e79f41111f51a0712ed4825 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 8 May 2020 19:24:32 -0400 Subject: [PATCH 06/27] Start of rendering partials. --- src/parser/parser.rs | 2 +- src/renderer/renderer.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/parser/parser.rs b/src/parser/parser.rs index b6e19c8..2ecef7a 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -114,7 +114,7 @@ pub struct ParameterizedBlock<'a> { #[derive(Clone, Debug, PartialEq)] pub struct Partial<'a> { - name: String, + pub name: String, params: Vec>, } diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index f4b3edd..51bb7c0 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -172,6 +172,10 @@ impl<'a> DustRenderer<'a> { }; } } + DustTag::DTPartial(partial) => { + let rendered_content = self.render(&partial.name, breadcrumbs)?; + return Ok(rendered_content); + } _ => (), // TODO: Implement the rest } Ok("".to_owned()) From 1a6db195cbf765e5702f271f80d36f6ff81a1a54 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 8 May 2020 20:58:32 -0400 Subject: [PATCH 07/27] Starting a context for partial parameters. --- src/parser/mod.rs | 1 + src/parser/parser.rs | 2 +- src/renderer/renderer.rs | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 588c8f9..9a7f16b 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -6,6 +6,7 @@ pub use parser::template; pub use parser::Body; pub use parser::DustTag; pub use parser::Filter; +pub use parser::RValue; pub use parser::Special; pub use parser::Template; pub use parser::TemplateElement; diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 2ecef7a..090155f 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -119,7 +119,7 @@ pub struct Partial<'a> { } #[derive(Clone, Debug, PartialEq)] -enum RValue<'a> { +pub enum RValue<'a> { RVPath(Path<'a>), RVString(String), } diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 51bb7c0..4278135 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -1,6 +1,7 @@ use crate::parser::template; use crate::parser::Body; use crate::parser::DustTag; +use crate::parser::RValue; use crate::parser::Special; use crate::parser::Template; use crate::parser::TemplateElement; @@ -253,6 +254,11 @@ fn walk_path<'a>( }) } +struct ParametersContext<'a> { + params: HashMap<&'a str, RValue<'a>>, + breadcrumbs: &'a Vec<&'a dyn ContextElement>, +} + #[cfg(test)] mod tests { use super::*; From b45448edbdbd7cc9a8024b1a980aa0b72807501e Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 8 May 2020 22:12:35 -0400 Subject: [PATCH 08/27] Moved ParametersContext to its own file. --- src/parser/mod.rs | 1 + src/parser/parser.rs | 6 +++--- src/renderer/mod.rs | 1 + src/renderer/parameters_context.rs | 28 ++++++++++++++++++++++++++++ src/renderer/renderer.rs | 7 +------ 5 files changed, 34 insertions(+), 9 deletions(-) create mode 100644 src/renderer/parameters_context.rs diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 9a7f16b..202309e 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -6,6 +6,7 @@ pub use parser::template; pub use parser::Body; pub use parser::DustTag; pub use parser::Filter; +pub use parser::KVPair; pub use parser::RValue; pub use parser::Special; pub use parser::Template; diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 090155f..883a445 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -125,9 +125,9 @@ pub enum RValue<'a> { } #[derive(Clone, Debug, PartialEq)] -struct KVPair<'a> { - key: &'a str, - value: RValue<'a>, +pub struct KVPair<'a> { + pub key: &'a str, + pub value: RValue<'a>, } #[derive(Clone, Debug, PartialEq)] diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index ce43f4d..5c86aab 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -2,6 +2,7 @@ mod context_element; mod errors; +mod parameters_context; mod renderer; pub use context_element::ContextElement; diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs new file mode 100644 index 0000000..d301010 --- /dev/null +++ b/src/renderer/parameters_context.rs @@ -0,0 +1,28 @@ +use crate::parser::KVPair; +use crate::parser::RValue; +use crate::renderer::context_element::ContextElement; +use std::collections::HashMap; + +#[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> {} diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 4278135..32914a2 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -1,13 +1,13 @@ use crate::parser::template; use crate::parser::Body; use crate::parser::DustTag; -use crate::parser::RValue; use crate::parser::Special; use crate::parser::Template; use crate::parser::TemplateElement; use crate::renderer::context_element::ContextElement; use crate::renderer::errors::CompileError; use crate::renderer::errors::RenderError; +use crate::renderer::parameters_context::ParametersContext; use std::collections::HashMap; #[derive(Clone, Debug)] @@ -254,11 +254,6 @@ fn walk_path<'a>( }) } -struct ParametersContext<'a> { - params: HashMap<&'a str, RValue<'a>>, - breadcrumbs: &'a Vec<&'a dyn ContextElement>, -} - #[cfg(test)] mod tests { use super::*; From 2f515e068d0b05832791eb3c8619308e9d05e495 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 8 May 2020 22:22:30 -0400 Subject: [PATCH 09/27] Implemented the renderer logic. I should just need to implement ContextElement at this point. --- src/parser/parser.rs | 2 +- src/renderer/parameters_context.rs | 2 +- src/renderer/renderer.rs | 12 ++++++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 883a445..4a6db06 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -115,7 +115,7 @@ pub struct ParameterizedBlock<'a> { #[derive(Clone, Debug, PartialEq)] pub struct Partial<'a> { pub name: String, - params: Vec>, + pub params: Vec>, } #[derive(Clone, Debug, PartialEq)] diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index d301010..b4d75ef 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -25,4 +25,4 @@ impl<'a> ParametersContext<'a> { } } -impl<'a> ContextElement for ParametersContext<'a> {} +// impl<'a> ContextElement for ParametersContext<'a> {} diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 32914a2..1e18a8a 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -174,8 +174,16 @@ impl<'a> DustRenderer<'a> { } } DustTag::DTPartial(partial) => { - let rendered_content = self.render(&partial.name, breadcrumbs)?; - return Ok(rendered_content); + if partial.params.is_empty() { + let rendered_content = self.render(&partial.name, breadcrumbs)?; + return Ok(rendered_content); + } else { + 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 = self.render(&partial.name, &new_breadcrumbs)?; + return Ok(rendered_content); + } } _ => (), // TODO: Implement the rest } From efd103b84a6a1ad175b57616dc0d4808e060c71e Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Fri, 8 May 2020 22:34:58 -0400 Subject: [PATCH 10/27] Running into a lifetime issue --- src/renderer/parameters_context.rs | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index b4d75ef..5a9164d 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -1,6 +1,10 @@ use crate::parser::KVPair; -use crate::parser::RValue; +use crate::parser::{Filter, RValue}; use crate::renderer::context_element::ContextElement; +use crate::renderer::Loopable; +use crate::renderer::RenderError; +use crate::renderer::Renderable; +use crate::renderer::Walkable; use std::collections::HashMap; #[derive(Clone, Debug)] @@ -25,4 +29,26 @@ impl<'a> ParametersContext<'a> { } } -// impl<'a> ContextElement for ParametersContext<'a> {} +impl<'a> ContextElement for ParametersContext<'a> {} + +impl<'a> Renderable for ParametersContext<'a> { + fn render(&self, _filters: &Vec) -> Result { + Ok("[object Object]".to_owned()) + } +} + +impl<'a> Loopable for ParametersContext<'a> { + fn get_loop_elements(&self) -> Result, RenderError> { + Ok(vec![self]) + } +} + +impl<'a> Walkable for ParametersContext<'a> { + fn walk(&self, segment: &str) -> Result<&dyn ContextElement, RenderError> { + // TODO: Actually implement + Err(RenderError::CantWalk { + segment: segment.to_string(), + elem: self, + }) + } +} From 2108f5cace2a3fccbdc98476d7f7549e5ee4efd3 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 9 May 2020 13:46:12 -0400 Subject: [PATCH 11/27] Remove CantRender, it was unused. --- src/renderer/errors.rs | 6 ------ src/renderer/renderer.rs | 5 +++-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/renderer/errors.rs b/src/renderer/errors.rs index 8af8bb1..8041fc8 100644 --- a/src/renderer/errors.rs +++ b/src/renderer/errors.rs @@ -18,10 +18,6 @@ pub enum RenderError<'a> { path: &'a Vec<&'a str>, breadcrumbs: Vec<&'a dyn ContextElement>, }, - /// Attempting to render and unrenderable type (for example, an object without any filters) - CantRender { - elem: &'a dyn ContextElement, - }, } #[derive(Clone)] @@ -39,7 +35,6 @@ impl fmt::Display for RenderError<'_> { RenderError::WontWalk { segment, elem } => { write!(f, "Failed to walk to {} from {:?}", segment, elem) } - RenderError::CantRender { elem } => write!(f, "Cant render {:?}", elem), RenderError::NotFound { path, breadcrumbs } => { write!(f, "Could not find {:?} in {:?}", path, breadcrumbs) } @@ -57,7 +52,6 @@ impl fmt::Debug for RenderError<'_> { RenderError::WontWalk { segment, elem } => { write!(f, "Failed to walk to {} from {:?}", segment, elem) } - RenderError::CantRender { elem } => write!(f, "Cant render {:?}", elem), RenderError::NotFound { path, breadcrumbs } => { write!(f, "Could not find {:?} in {:?}", path, breadcrumbs) } diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 1e18a8a..33159f3 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -181,7 +181,8 @@ impl<'a> DustRenderer<'a> { 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 = self.render(&partial.name, &new_breadcrumbs)?; + // TODO: Change unwrap to ? + let rendered_content = self.render(&partial.name, &new_breadcrumbs).unwrap(); return Ok(rendered_content); } } @@ -291,7 +292,7 @@ mod tests { impl Renderable for HashMap<&str, I> { fn render(&self, _filters: &Vec) -> Result { // TODO: handle the filters - Err(RenderError::CantRender { elem: self }) + Ok("[object Object]".to_owned()) } } From 7d63d6ef7b540d6909c531cdf745f88c5efbb7b3 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 9 May 2020 13:51:02 -0400 Subject: [PATCH 12/27] Merge Cant and Wont Walk. --- src/bin.rs | 2 +- src/renderer/errors.rs | 13 +------------ src/renderer/renderer.rs | 6 ++---- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index bb1c1f1..6c314a3 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -116,7 +116,7 @@ impl Walkable for serde_json::Value { serde_json::Value::Object(obj) => { obj.get(segment) .map(|val| val as _) - .ok_or(RenderError::WontWalk { + .ok_or(RenderError::CantWalk { segment: segment.to_string(), elem: self, }) diff --git a/src/renderer/errors.rs b/src/renderer/errors.rs index 8041fc8..13f249e 100644 --- a/src/renderer/errors.rs +++ b/src/renderer/errors.rs @@ -4,13 +4,8 @@ use std::fmt; pub enum RenderError<'a> { Generic(String), - /// For when walking is absolutely impossible - CantWalk { - segment: String, - elem: &'a dyn ContextElement, - }, /// For when walking fails (example, a missing key on a map) - WontWalk { + CantWalk { segment: String, elem: &'a dyn ContextElement, }, @@ -32,9 +27,6 @@ impl fmt::Display for RenderError<'_> { RenderError::CantWalk { segment, elem } => { write!(f, "Tried to walk to {} from {:?}", segment, elem) } - RenderError::WontWalk { segment, elem } => { - write!(f, "Failed to walk to {} from {:?}", segment, elem) - } RenderError::NotFound { path, breadcrumbs } => { write!(f, "Could not find {:?} in {:?}", path, breadcrumbs) } @@ -49,9 +41,6 @@ impl fmt::Debug for RenderError<'_> { RenderError::CantWalk { segment, elem } => { write!(f, "Tried to walk to {} from {:?}", segment, elem) } - RenderError::WontWalk { segment, elem } => { - write!(f, "Failed to walk to {} from {:?}", segment, elem) - } RenderError::NotFound { path, breadcrumbs } => { write!(f, "Could not find {:?} in {:?}", path, breadcrumbs) } diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 33159f3..7d2e9da 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -226,9 +226,7 @@ fn walk_path_from_single_level<'a>( let mut output = context; for elem in path.iter() { let new_val = output.walk(elem); - if let Err(RenderError::WontWalk { .. }) = new_val { - return Ok(walk_failure); - } else if let Err(RenderError::CantWalk { .. }) = new_val { + if let Err(RenderError::CantWalk { .. }) = new_val { return Ok(walk_failure); } walk_failure = WalkResult::PartialWalk; @@ -298,7 +296,7 @@ mod tests { impl Walkable for HashMap<&str, I> { fn walk(&self, segment: &str) -> Result<&dyn ContextElement, RenderError> { - let child = self.get(segment).ok_or(RenderError::WontWalk { + let child = self.get(segment).ok_or(RenderError::CantWalk { segment: segment.to_string(), elem: self, })?; From b20368c586d09a0c15b5cab465a00e3c4f424426 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 9 May 2020 13:54:39 -0400 Subject: [PATCH 13/27] Starting to create a WalkError type for walking that will not bubble up. --- src/renderer/errors.rs | 43 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/renderer/errors.rs b/src/renderer/errors.rs index 13f249e..6eb65ad 100644 --- a/src/renderer/errors.rs +++ b/src/renderer/errors.rs @@ -15,6 +15,17 @@ pub enum RenderError<'a> { }, } +pub enum WalkError<'a> { + CantWalk { + segment: String, + elem: &'a dyn ContextElement, + }, + NotFound { + path: &'a Vec<&'a str>, + breadcrumbs: Vec<&'a dyn ContextElement>, + }, +} + #[derive(Clone)] pub struct CompileError { pub message: String, @@ -54,6 +65,38 @@ impl error::Error for RenderError<'_> { } } +impl fmt::Display for WalkError<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + WalkError::CantWalk { segment, elem } => { + write!(f, "Tried to walk to {} from {:?}", segment, elem) + } + WalkError::NotFound { path, breadcrumbs } => { + write!(f, "Could not find {:?} in {:?}", path, breadcrumbs) + } + } + } +} + +impl fmt::Debug for WalkError<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + WalkError::CantWalk { segment, elem } => { + write!(f, "Tried to walk to {} from {:?}", segment, elem) + } + WalkError::NotFound { path, breadcrumbs } => { + write!(f, "Could not find {:?} in {:?}", path, breadcrumbs) + } + } + } +} + +impl error::Error for WalkError<'_> { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + None + } +} + impl fmt::Display for CompileError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Error compiling: {}", self.message) From fcb2f3fc4d81c17707b6adf933c3009a8f4ae7f3 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 9 May 2020 14:00:19 -0400 Subject: [PATCH 14/27] Going extreme: removing all fields and unifying all walk errors. --- src/renderer/context_element.rs | 3 ++- src/renderer/errors.rs | 11 ++--------- src/renderer/renderer.rs | 3 ++- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index 14f1aae..bc12234 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -1,11 +1,12 @@ use crate::parser::Filter; use crate::renderer::errors::RenderError; +use crate::renderer::errors::WalkError; use std::fmt::Debug; pub trait ContextElement: Debug + Walkable + Renderable + Loopable {} pub trait Walkable { - fn walk(&self, segment: &str) -> Result<&dyn ContextElement, RenderError>; + fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError>; } pub trait Renderable { diff --git a/src/renderer/errors.rs b/src/renderer/errors.rs index 6eb65ad..d9fa346 100644 --- a/src/renderer/errors.rs +++ b/src/renderer/errors.rs @@ -15,15 +15,8 @@ pub enum RenderError<'a> { }, } -pub enum WalkError<'a> { - CantWalk { - segment: String, - elem: &'a dyn ContextElement, - }, - NotFound { - path: &'a Vec<&'a str>, - breadcrumbs: Vec<&'a dyn ContextElement>, - }, +pub enum WalkError { + CantWalk, } #[derive(Clone)] diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 7d2e9da..dbfd823 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -7,6 +7,7 @@ use crate::parser::TemplateElement; use crate::renderer::context_element::ContextElement; use crate::renderer::errors::CompileError; use crate::renderer::errors::RenderError; +use crate::renderer::errors::WalkError; use crate::renderer::parameters_context::ParametersContext; use std::collections::HashMap; @@ -226,7 +227,7 @@ fn walk_path_from_single_level<'a>( let mut output = context; for elem in path.iter() { let new_val = output.walk(elem); - if let Err(RenderError::CantWalk { .. }) = new_val { + if let Err(WalkError::CantWalk { .. }) = new_val { return Ok(walk_failure); } walk_failure = WalkResult::PartialWalk; From 2712126b3c5d65866351cbcb6df3c16aa2d40104 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 9 May 2020 14:10:38 -0400 Subject: [PATCH 15/27] Need to do loop elements. --- src/renderer/errors.rs | 20 +++++------------ src/renderer/mod.rs | 1 + src/renderer/parameters_context.rs | 8 +++---- src/renderer/renderer.rs | 35 ++++++++++++++---------------- 4 files changed, 25 insertions(+), 39 deletions(-) diff --git a/src/renderer/errors.rs b/src/renderer/errors.rs index d9fa346..407ff1b 100644 --- a/src/renderer/errors.rs +++ b/src/renderer/errors.rs @@ -58,33 +58,23 @@ impl error::Error for RenderError<'_> { } } -impl fmt::Display for WalkError<'_> { +impl fmt::Display for WalkError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - WalkError::CantWalk { segment, elem } => { - write!(f, "Tried to walk to {} from {:?}", segment, elem) - } - WalkError::NotFound { path, breadcrumbs } => { - write!(f, "Could not find {:?} in {:?}", path, breadcrumbs) - } + WalkError::CantWalk => write!(f, "Failed to walk"), } } } -impl fmt::Debug for WalkError<'_> { +impl fmt::Debug for WalkError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - WalkError::CantWalk { segment, elem } => { - write!(f, "Tried to walk to {} from {:?}", segment, elem) - } - WalkError::NotFound { path, breadcrumbs } => { - write!(f, "Could not find {:?} in {:?}", path, breadcrumbs) - } + WalkError::CantWalk => write!(f, "Failed to walk"), } } } -impl error::Error for WalkError<'_> { +impl error::Error for WalkError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { None } diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 5c86aab..6d0c041 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -11,6 +11,7 @@ pub use context_element::Renderable; pub use context_element::Walkable; pub use errors::CompileError; pub use errors::RenderError; +pub use errors::WalkError; pub use renderer::compile_template; pub use renderer::CompiledTemplate; pub use renderer::DustRenderer; diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 5a9164d..0a3937e 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -4,6 +4,7 @@ use crate::renderer::context_element::ContextElement; use crate::renderer::Loopable; use crate::renderer::RenderError; use crate::renderer::Renderable; +use crate::renderer::WalkError; use crate::renderer::Walkable; use std::collections::HashMap; @@ -44,11 +45,8 @@ impl<'a> Loopable for ParametersContext<'a> { } impl<'a> Walkable for ParametersContext<'a> { - fn walk(&self, segment: &str) -> Result<&dyn ContextElement, RenderError> { + fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> { // TODO: Actually implement - Err(RenderError::CantWalk { - segment: segment.to_string(), - elem: self, - }) + Err(WalkError::CantWalk) } } diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index dbfd823..89bd383 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -102,7 +102,7 @@ impl<'a> DustRenderer<'a> { DustTag::DTReference(reference) => { let val = walk_path(breadcrumbs, &reference.path.keys); match val { - Err(RenderError::NotFound { .. }) => return Ok("".to_owned()), + Err(WalkError::CantWalk) => return Ok("".to_owned()), Ok(final_val) => { let loop_elements = final_val.get_loop_elements()?; if loop_elements.is_empty() { @@ -111,7 +111,6 @@ impl<'a> DustRenderer<'a> { return final_val.render(&reference.filters); } } - Err(render_error) => return Err(render_error), } } DustTag::DTSection(container) => { @@ -218,48 +217,46 @@ enum WalkResult<'a> { fn walk_path_from_single_level<'a>( context: &'a dyn ContextElement, path: &Vec<&str>, -) -> Result, RenderError<'a>> { +) -> WalkResult<'a> { if path.is_empty() { - return Ok(WalkResult::FullyWalked(context)); + return WalkResult::FullyWalked(context); } let mut walk_failure = WalkResult::NoWalk; let mut output = context; for elem in path.iter() { let new_val = output.walk(elem); - if let Err(WalkError::CantWalk { .. }) = new_val { - return Ok(walk_failure); + match output.walk(elem) { + Err(WalkError::CantWalk { .. }) => { + return walk_failure; + } + Ok(new_val) => { + walk_failure = WalkResult::PartialWalk; + output = new_val; + } } - walk_failure = WalkResult::PartialWalk; - output = new_val?; } - Ok(WalkResult::FullyWalked(output)) + WalkResult::FullyWalked(output) } fn walk_path<'a>( breadcrumbs: &Vec<&'a dyn ContextElement>, path: &'a Vec<&str>, -) -> Result<&'a dyn ContextElement, RenderError<'a>> { +) -> Result<&'a dyn ContextElement, WalkError> { for context in breadcrumbs.iter().rev() { - match walk_path_from_single_level(*context, path)? { + match walk_path_from_single_level(*context, path) { // 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(RenderError::NotFound { - path: path, - breadcrumbs: breadcrumbs.clone(), - }); + return Err(WalkError::CantWalk); } WalkResult::FullyWalked(new_context) => return Ok(new_context), } } - Err(RenderError::NotFound { - path: path, - breadcrumbs: breadcrumbs.clone(), - }) + Err(WalkError::CantWalk) } #[cfg(test)] From d758a71fb4fe201b072642625b9b08b5e72d09f7 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 9 May 2020 14:14:22 -0400 Subject: [PATCH 16/27] Ported the get_loop_elements wrapper. --- src/bin.rs | 2 +- src/renderer/context_element.rs | 2 +- src/renderer/renderer.rs | 12 +++++------- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index 6c314a3..e02e3d9 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -126,7 +126,7 @@ impl Walkable for serde_json::Value { } impl Loopable for serde_json::Value { - fn get_loop_elements(&self) -> Result, RenderError> { + fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { match self { serde_json::Value::Null => Ok(Vec::new()), serde_json::Value::Bool(boolean) => { diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index bc12234..dee6680 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -23,5 +23,5 @@ pub trait Loopable { /// once with the context being the element at that path. Finally, /// if its an array-like value then it will render n-times, once /// for each element of the array. - fn get_loop_elements(&self) -> Result, RenderError>; + fn get_loop_elements(&self) -> Vec<&dyn ContextElement>; } diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 89bd383..8f74c93 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -197,13 +197,11 @@ impl<'a> DustRenderer<'a> { /// block, this will return an empty vector. fn get_loop_elements<'b>( &'a self, - walk_result: Result<&'b dyn ContextElement, RenderError<'b>>, - ) -> Result, RenderError<'b>> { - if let Err(RenderError::NotFound { .. }) = walk_result { - // If reference does not exist in the context, render the else block - Ok(vec![]) - } else { - Ok(walk_result?.get_loop_elements()?) + walk_result: Result<&'b dyn ContextElement, WalkError>, + ) -> Vec<&'b dyn ContextElement> { + match walk_result { + Err(WalkError::CantWalk) => Vec::new(), + Ok(walk_target) => walk_target.get_loop_elements(), } } } From f2f640401777fb915c7c09ee430b665b42305a27 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 9 May 2020 14:18:45 -0400 Subject: [PATCH 17/27] Compiles again. --- src/bin.rs | 58 +++++++++++------------------- src/renderer/parameters_context.rs | 5 +-- src/renderer/renderer.rs | 8 ++--- 3 files changed, 27 insertions(+), 44 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index e02e3d9..12e1bd3 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -8,6 +8,7 @@ use renderer::DustRenderer; use renderer::Loopable; use renderer::RenderError; use renderer::Renderable; +use renderer::WalkError; use renderer::Walkable; use std::env; use std::fs; @@ -91,36 +92,17 @@ impl Renderable for serde_json::Value { } impl Walkable for serde_json::Value { - fn walk(&self, segment: &str) -> Result<&dyn ContextElement, RenderError> { + fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> { match self { - serde_json::Value::Null => Err(RenderError::CantWalk { - segment: segment.to_string(), - elem: self, - }), - serde_json::Value::Bool(_boolean) => Err(RenderError::CantWalk { - segment: segment.to_string(), - elem: self, - }), - serde_json::Value::Number(_num) => Err(RenderError::CantWalk { - segment: segment.to_string(), - elem: self, - }), - serde_json::Value::String(_string) => Err(RenderError::CantWalk { - segment: segment.to_string(), - elem: self, - }), - serde_json::Value::Array(_arr) => Err(RenderError::CantWalk { - segment: segment.to_string(), - elem: self, - }), - serde_json::Value::Object(obj) => { - obj.get(segment) - .map(|val| val as _) - .ok_or(RenderError::CantWalk { - segment: segment.to_string(), - elem: self, - }) - } + serde_json::Value::Null => Err(WalkError::CantWalk), + serde_json::Value::Bool(_boolean) => Err(WalkError::CantWalk), + serde_json::Value::Number(_num) => Err(WalkError::CantWalk), + serde_json::Value::String(_string) => Err(WalkError::CantWalk), + serde_json::Value::Array(_arr) => Err(WalkError::CantWalk), + serde_json::Value::Object(obj) => obj + .get(segment) + .map(|val| val as _) + .ok_or(WalkError::CantWalk), } } } @@ -128,30 +110,30 @@ impl Walkable for serde_json::Value { impl Loopable for serde_json::Value { fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { match self { - serde_json::Value::Null => Ok(Vec::new()), + serde_json::Value::Null => Vec::new(), serde_json::Value::Bool(boolean) => { if *boolean { - Ok(vec![self]) + vec![self] } else { - Ok(Vec::new()) + Vec::new() } } - serde_json::Value::Number(_num) => Ok(vec![self]), + serde_json::Value::Number(_num) => vec![self], serde_json::Value::String(string_value) => { if string_value.is_empty() { - Ok(Vec::new()) + Vec::new() } else { - Ok(vec![self]) + vec![self] } } serde_json::Value::Array(array_value) => { if array_value.is_empty() { - Ok(Vec::new()) + Vec::new() } else { - Ok(array_value.iter().map(|x| x as _).collect()) + array_value.iter().map(|x| x as _).collect() } } - serde_json::Value::Object(_obj) => Ok(vec![self]), + serde_json::Value::Object(_obj) => vec![self], } } } diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 0a3937e..562933a 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -39,8 +39,9 @@ impl<'a> Renderable for ParametersContext<'a> { } impl<'a> Loopable for ParametersContext<'a> { - fn get_loop_elements(&self) -> Result, RenderError> { - Ok(vec![self]) + fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { + // TODO: Would this even ever be called? Won't matter, but I'd like to know. + vec![self] } } diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 8f74c93..0ba309b 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -104,7 +104,7 @@ impl<'a> DustRenderer<'a> { match val { Err(WalkError::CantWalk) => return Ok("".to_owned()), Ok(final_val) => { - let loop_elements = final_val.get_loop_elements()?; + let loop_elements = final_val.get_loop_elements(); if loop_elements.is_empty() { return Ok("".to_owned()); } else { @@ -115,7 +115,7 @@ 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 @@ -145,7 +145,7 @@ 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)?; + 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), @@ -160,7 +160,7 @@ impl<'a> DustRenderer<'a> { } DustTag::DTNotExists(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() { return match &container.else_contents { Some(body) => self.render_body(&body, breadcrumbs), From 05b56e83a9ff5663231e9a5ced3a31e634cb8532 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 9 May 2020 14:22:36 -0400 Subject: [PATCH 18/27] Finished creating WalkError. --- src/renderer/renderer.rs | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 0ba309b..ca46c1e 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -291,52 +291,43 @@ mod tests { } impl Walkable for HashMap<&str, I> { - fn walk(&self, segment: &str) -> Result<&dyn ContextElement, RenderError> { - let child = self.get(segment).ok_or(RenderError::CantWalk { - segment: segment.to_string(), - elem: self, - })?; + 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, RenderError> { - Err(RenderError::CantWalk { - segment: segment.to_string(), - elem: self, - }) + fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> { + Err(WalkError::CantWalk) } } impl Walkable for u32 { - fn walk(&self, segment: &str) -> Result<&dyn ContextElement, RenderError> { - Err(RenderError::CantWalk { - segment: segment.to_string(), - elem: self, - }) + fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> { + Err(WalkError::CantWalk) } } impl Loopable for &str { - fn get_loop_elements(&self) -> Result, RenderError> { + fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { if self.is_empty() { - Ok(Vec::new()) + Vec::new() } else { - Ok(vec![self]) + vec![self] } } } impl Loopable for u32 { - fn get_loop_elements(&self) -> Result, RenderError> { - Ok(vec![self]) + fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { + vec![self] } } impl Loopable for HashMap<&str, I> { - fn get_loop_elements(&self) -> Result, RenderError> { - Ok(vec![self]) + fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { + vec![self] } } From 5d7c991bf083706a739108b635861912256dbdf2 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 9 May 2020 14:27:42 -0400 Subject: [PATCH 19/27] Simplified the RenderError class. --- src/renderer/errors.rs | 33 +++++++++------------------------ src/renderer/renderer.rs | 14 +++++--------- 2 files changed, 14 insertions(+), 33 deletions(-) diff --git a/src/renderer/errors.rs b/src/renderer/errors.rs index 407ff1b..896805c 100644 --- a/src/renderer/errors.rs +++ b/src/renderer/errors.rs @@ -1,18 +1,9 @@ -use crate::renderer::context_element::ContextElement; use std::error; use std::fmt; -pub enum RenderError<'a> { +pub enum RenderError { Generic(String), - /// For when walking fails (example, a missing key on a map) - CantWalk { - segment: String, - elem: &'a dyn ContextElement, - }, - NotFound { - path: &'a Vec<&'a str>, - breadcrumbs: Vec<&'a dyn ContextElement>, - }, + TemplateNotFound(String), } pub enum WalkError { @@ -24,35 +15,29 @@ pub struct CompileError { pub message: String, } -impl fmt::Display for RenderError<'_> { +impl fmt::Display for RenderError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { RenderError::Generic(msg) => write!(f, "{}", msg), - RenderError::CantWalk { segment, elem } => { - write!(f, "Tried to walk to {} from {:?}", segment, elem) - } - RenderError::NotFound { path, breadcrumbs } => { - write!(f, "Could not find {:?} in {:?}", path, breadcrumbs) + RenderError::TemplateNotFound(name) => { + write!(f, "No template named {} in context", name) } } } } -impl fmt::Debug for RenderError<'_> { +impl fmt::Debug for RenderError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { RenderError::Generic(msg) => write!(f, "{}", msg), - RenderError::CantWalk { segment, elem } => { - write!(f, "Tried to walk to {} from {:?}", segment, elem) - } - RenderError::NotFound { path, breadcrumbs } => { - write!(f, "Could not find {:?} in {:?}", path, breadcrumbs) + RenderError::TemplateNotFound(name) => { + write!(f, "No template named {} in context", name) } } } } -impl error::Error for RenderError<'_> { +impl error::Error for RenderError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { None } diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index ca46c1e..1b4314b 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -51,14 +51,11 @@ impl<'a> DustRenderer<'a> { &'a self, name: &str, breadcrumbs: &Vec<&'a dyn ContextElement>, - ) -> Result> { + ) -> Result { let main_template = match self.templates.get(name) { Some(tmpl) => tmpl, None => { - return Err(RenderError::Generic(format!( - "No template named {} in context", - name - ))); + return Err(RenderError::TemplateNotFound(name.to_owned())); } }; self.render_body(&main_template.contents, breadcrumbs) @@ -68,7 +65,7 @@ impl<'a> DustRenderer<'a> { &'a self, body: &'a Body, breadcrumbs: &Vec<&'a dyn ContextElement>, - ) -> Result> { + ) -> Result { let mut output = String::new(); for elem in &body.elements { match elem { @@ -86,7 +83,7 @@ impl<'a> DustRenderer<'a> { &'a self, tag: &'a DustTag, breadcrumbs: &Vec<&'a dyn ContextElement>, - ) -> Result> { + ) -> Result { match tag { DustTag::DTComment(_comment) => (), DustTag::DTSpecial(special) => { @@ -181,8 +178,7 @@ impl<'a> DustRenderer<'a> { let injected_context = ParametersContext::new(breadcrumbs, &partial.params); let mut new_breadcrumbs = breadcrumbs.clone(); new_breadcrumbs.insert(new_breadcrumbs.len() - 1, &injected_context); - // TODO: Change unwrap to ? - let rendered_content = self.render(&partial.name, &new_breadcrumbs).unwrap(); + let rendered_content = self.render(&partial.name, &new_breadcrumbs)?; return Ok(rendered_content); } } From 98a0145760cc8d3c47a10f06a1c94e238eaaac0b Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 9 May 2020 14:33:13 -0400 Subject: [PATCH 20/27] Adding a comment. --- src/renderer/errors.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/renderer/errors.rs b/src/renderer/errors.rs index 896805c..5a38068 100644 --- a/src/renderer/errors.rs +++ b/src/renderer/errors.rs @@ -1,6 +1,9 @@ use std::error; use std::fmt; +/// Fatal errors while rendering. +/// +/// A RenderError will halt rendering. pub enum RenderError { Generic(String), TemplateNotFound(String), From bbb9b8d9d370412f9729836f6efed3dcc500c49a Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 9 May 2020 14:53:53 -0400 Subject: [PATCH 21/27] Partial simple is working. --- src/renderer/mod.rs | 1 + src/renderer/parameters_context.rs | 37 +++++++++++++++++++-- src/renderer/renderer.rs | 52 +----------------------------- 3 files changed, 37 insertions(+), 53 deletions(-) diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 6d0c041..13d2ede 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -4,6 +4,7 @@ mod context_element; mod errors; mod parameters_context; mod renderer; +mod walking; pub use context_element::ContextElement; pub use context_element::Loopable; diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 562933a..c2aa6d0 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::walking::walk_path; use crate::renderer::Loopable; use crate::renderer::RenderError; use crate::renderer::Renderable; @@ -34,20 +35,52 @@ 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. + // 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> { - // TODO: Actually implement + 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 { + fn render(&self, _filters: &Vec) -> Result { + Ok(self.clone()) + } +} + +impl Loopable for String { + fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { + if self.is_empty() { + Vec::new() + } else { + vec![self] + } + } +} + +impl Walkable for String { + fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> { Err(WalkError::CantWalk) } } diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 1b4314b..5a4f18e 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -9,6 +9,7 @@ use crate::renderer::errors::CompileError; use crate::renderer::errors::RenderError; use crate::renderer::errors::WalkError; use crate::renderer::parameters_context::ParametersContext; +use crate::renderer::walking::walk_path; use std::collections::HashMap; #[derive(Clone, Debug)] @@ -202,57 +203,6 @@ impl<'a> DustRenderer<'a> { } } -enum WalkResult<'a> { - NoWalk, - PartialWalk, - FullyWalked(&'a dyn ContextElement), -} - -fn walk_path_from_single_level<'a>( - context: &'a dyn ContextElement, - path: &Vec<&str>, -) -> WalkResult<'a> { - if path.is_empty() { - return WalkResult::FullyWalked(context); - } - - let mut walk_failure = WalkResult::NoWalk; - let mut output = context; - for elem in path.iter() { - let new_val = output.walk(elem); - match output.walk(elem) { - Err(WalkError::CantWalk { .. }) => { - return walk_failure; - } - Ok(new_val) => { - walk_failure = WalkResult::PartialWalk; - output = new_val; - } - } - } - - WalkResult::FullyWalked(output) -} - -fn walk_path<'a>( - breadcrumbs: &Vec<&'a dyn ContextElement>, - path: &'a Vec<&str>, -) -> Result<&'a dyn ContextElement, WalkError> { - for context in breadcrumbs.iter().rev() { - match walk_path_from_single_level(*context, path) { - // 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) -} - #[cfg(test)] mod tests { use super::*; From bb3449467a39d4c60eec916f555555e87c4c739a Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 9 May 2020 15:02:54 -0400 Subject: [PATCH 22/27] Running into an error parsing one of the partial test templates. --- src/parser/parser.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 4a6db06..20134ee 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -1052,4 +1052,19 @@ mod tests { )) ); } + + #[test] + fn test_full_document_temp() { + assert_eq!( + super::template( + r#"{#level3.level4}{>partialtwo v1="b" v2="b" v3="b" v4="b" v5="b" /}{/level3.level4}"# + ), + Ok::<_, nom::Err<(&str, ErrorKind)>>(( + "", + Template { + contents: Body { elements: vec![] } + } + )) + ); + } } From 569b4594be8e401ce8d689162dccb2cf128d8575 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 9 May 2020 15:05:29 -0400 Subject: [PATCH 23/27] works fine without the partial. --- src/parser/parser.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 20134ee..e7e6fe0 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -1056,13 +1056,19 @@ mod tests { #[test] fn test_full_document_temp() { assert_eq!( - super::template( - r#"{#level3.level4}{>partialtwo v1="b" v2="b" v3="b" v4="b" v5="b" /}{/level3.level4}"# - ), + super::template(r#"{#level3.level4}{/level3.level4}"#), Ok::<_, nom::Err<(&str, ErrorKind)>>(( "", Template { - contents: Body { elements: vec![] } + contents: Body { + elements: vec![TemplateElement::TETag(DustTag::DTSection(Container { + path: Path { + keys: vec!["level3", "level4"] + }, + contents: None, + else_contents: None + }))] + } } )) ); From 710785139aa54f99c73c5269f0cfccf82b29d0d9 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 9 May 2020 15:08:31 -0400 Subject: [PATCH 24/27] Works fine with the partial but without parameters. --- src/parser/parser.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/parser/parser.rs b/src/parser/parser.rs index e7e6fe0..467521f 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -1056,7 +1056,7 @@ mod tests { #[test] fn test_full_document_temp() { assert_eq!( - super::template(r#"{#level3.level4}{/level3.level4}"#), + super::template(r#"{#level3.level4}{>partialtwo/}{/level3.level4}"#), Ok::<_, nom::Err<(&str, ErrorKind)>>(( "", Template { @@ -1065,7 +1065,14 @@ mod tests { path: Path { keys: vec!["level3", "level4"] }, - contents: None, + contents: Some(Body { + elements: vec![TemplateElement::TETag(DustTag::DTPartial( + Partial { + name: "partialtwo".to_owned(), + params: vec![] + } + ))] + }), else_contents: None }))] } From 369fbaf579ba5900e07ebab155beabd5078ea1f6 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 9 May 2020 15:11:04 -0400 Subject: [PATCH 25/27] works fine with one parameter. --- src/parser/parser.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 467521f..44587ca 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -1054,9 +1054,9 @@ mod tests { } #[test] - fn test_full_document_temp() { + fn test_full_document_parameterized_partial() { assert_eq!( - super::template(r#"{#level3.level4}{>partialtwo/}{/level3.level4}"#), + super::template(r#"{#level3.level4}{>partialtwo v1="b"/}{/level3.level4}"#), Ok::<_, nom::Err<(&str, ErrorKind)>>(( "", Template { @@ -1069,7 +1069,10 @@ mod tests { elements: vec![TemplateElement::TETag(DustTag::DTPartial( Partial { name: "partialtwo".to_owned(), - params: vec![] + params: vec![KVPair { + key: "v1", + value: RValue::RVString("b".to_owned()) + }] } ))] }), From 2a9657e3d50d6c5a86b3b3e4e9a46b40c1afc0cb Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 9 May 2020 15:15:43 -0400 Subject: [PATCH 26/27] Turns out the issue was the trailing space on the parameters. --- src/parser/parser.rs | 50 +++++++++++++++++++++++++++++++------- src/renderer/walking.rs | 53 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 9 deletions(-) create mode 100644 src/renderer/walking.rs diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 44587ca..9422a6d 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -6,7 +6,7 @@ use nom::bytes::complete::{tag, take_until, take_until_parser_matches}; use nom::character::complete::line_ending; use nom::character::complete::multispace0; use nom::character::complete::one_of; -use nom::character::complete::space1; +use nom::character::complete::{space0, space1}; use nom::combinator::all_consuming; use nom::combinator::map; use nom::combinator::opt; @@ -387,7 +387,11 @@ where let (i, (name, params, inner, maybe_else, _closing_name)) = tuple(( preceded(tag(open_matcher), tag(tag_name)), terminated( - opt(preceded(space1, separated_list1(space1, key_value_pair))), + opt(delimited( + space1, + separated_list1(space1, key_value_pair), + space0, + )), tag("}"), ), opt(body), @@ -420,7 +424,11 @@ where tag(open_matcher), tuple(( tag(tag_name), - opt(preceded(space1, separated_list1(space1, key_value_pair))), + opt(delimited( + space1, + separated_list1(space1, key_value_pair), + space0, + )), )), tag("/}"), )(i)?; @@ -449,7 +457,11 @@ where tag(open_matcher), tuple(( alt((map(key, String::from), quoted_string)), - opt(preceded(space1, separated_list1(space1, key_value_pair))), + opt(delimited( + space1, + separated_list1(space1, key_value_pair), + space0, + )), )), tag("/}"), )(i)?; @@ -1056,7 +1068,9 @@ mod tests { #[test] fn test_full_document_parameterized_partial() { assert_eq!( - super::template(r#"{#level3.level4}{>partialtwo v1="b"/}{/level3.level4}"#), + super::template( + r#"{#level3.level4}{>partialtwo v1="b" v2="b" v3="b" v4="b" v5="b" /}{/level3.level4}"# + ), Ok::<_, nom::Err<(&str, ErrorKind)>>(( "", Template { @@ -1069,10 +1083,28 @@ mod tests { elements: vec![TemplateElement::TETag(DustTag::DTPartial( Partial { name: "partialtwo".to_owned(), - params: vec![KVPair { - key: "v1", - value: RValue::RVString("b".to_owned()) - }] + params: vec![ + KVPair { + key: "v1", + value: RValue::RVString("b".to_owned()) + }, + KVPair { + key: "v2", + value: RValue::RVString("b".to_owned()) + }, + KVPair { + key: "v3", + value: RValue::RVString("b".to_owned()) + }, + KVPair { + key: "v4", + value: RValue::RVString("b".to_owned()) + }, + KVPair { + key: "v5", + value: RValue::RVString("b".to_owned()) + } + ] } ))] }), diff --git a/src/renderer/walking.rs b/src/renderer/walking.rs new file mode 100644 index 0000000..feadce3 --- /dev/null +++ b/src/renderer/walking.rs @@ -0,0 +1,53 @@ +use crate::renderer::context_element::ContextElement; +use crate::renderer::WalkError; + +enum WalkResult<'a> { + NoWalk, + PartialWalk, + FullyWalked(&'a dyn ContextElement), +} + +fn walk_path_from_single_level<'a>( + context: &'a dyn ContextElement, + path: &Vec<&str>, +) -> WalkResult<'a> { + if path.is_empty() { + return WalkResult::FullyWalked(context); + } + + let mut walk_failure = WalkResult::NoWalk; + let mut output = context; + for elem in path.iter() { + let new_val = output.walk(elem); + match output.walk(elem) { + Err(WalkError::CantWalk { .. }) => { + return walk_failure; + } + Ok(new_val) => { + walk_failure = WalkResult::PartialWalk; + output = new_val; + } + } + } + + WalkResult::FullyWalked(output) +} + +pub fn walk_path<'a>( + breadcrumbs: &Vec<&'a dyn ContextElement>, + path: &'a Vec<&str>, +) -> Result<&'a dyn ContextElement, WalkError> { + for context in breadcrumbs.iter().rev() { + match walk_path_from_single_level(*context, path) { + // 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 97fbde630462934b1f25f16984c9cd00d868f765 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 9 May 2020 15:20:14 -0400 Subject: [PATCH 27/27] Add a test for partials referencing paths. --- js/test_cases/partial_path/greeting.dust | 1 + js/test_cases/partial_path/input1.json | 14 ++++++++++++++ js/test_cases/partial_path/main.dust | 3 +++ 3 files changed, 18 insertions(+) create mode 100644 js/test_cases/partial_path/greeting.dust create mode 100644 js/test_cases/partial_path/input1.json create mode 100644 js/test_cases/partial_path/main.dust diff --git a/js/test_cases/partial_path/greeting.dust b/js/test_cases/partial_path/greeting.dust new file mode 100644 index 0000000..f5a4adb --- /dev/null +++ b/js/test_cases/partial_path/greeting.dust @@ -0,0 +1 @@ +Hello {name}{?item}, nice {item}{/item}!{~n} diff --git a/js/test_cases/partial_path/input1.json b/js/test_cases/partial_path/input1.json new file mode 100644 index 0000000..232ddbe --- /dev/null +++ b/js/test_cases/partial_path/input1.json @@ -0,0 +1,14 @@ +{ + "people": [ + { + "name": "Alice", + "item": "cat" + }, + { + "name": "Bob" + } + ], + "globals": { + "item": "couch" + } +} diff --git a/js/test_cases/partial_path/main.dust b/js/test_cases/partial_path/main.dust new file mode 100644 index 0000000..87518e2 --- /dev/null +++ b/js/test_cases/partial_path/main.dust @@ -0,0 +1,3 @@ +{#people} +{>greeting item=globals.item/} +{/people}