From a578b57f120637b8ef4d6c3d6725b16ecaadeb7a Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 13:21:47 -0400 Subject: [PATCH 01/67] Switch the call for IntoContextElement back to a static integer so it compiles again. --- src/renderer/parameters_context.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 0a85635..0fb4abf 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -247,12 +247,13 @@ impl IntoContextElement for Vec { renderer: &DustRenderer, breadcrumbs: &Vec<&dyn IntoContextElement>, ) -> &dyn ContextElement { - OwnedLiteral::LString( - renderer - .render_partial_name(self, breadcrumbs) - .expect("TODO: Make into_context_element return a RenderError"), - ) - // &OwnedLiteral::LPositiveInteger(1) + // OwnedLiteral::LString( + // renderer + // .render_partial_name(self, breadcrumbs) + // .expect("TODO: Make into_context_element return a RenderError"), + // ) + // TODO + &OwnedLiteral::LPositiveInteger(1) } } From 645b251f13640d9e3b04f6f1db368ff0469cd8b1 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 13:52:20 -0400 Subject: [PATCH 02/67] Initial setup for a heterogenous tree of IntoContextElement. --- src/renderer/breadcrumb_tree.rs | 39 +++++++++++++++++++++++++++++++++ src/renderer/mod.rs | 1 + 2 files changed, 40 insertions(+) create mode 100644 src/renderer/breadcrumb_tree.rs diff --git a/src/renderer/breadcrumb_tree.rs b/src/renderer/breadcrumb_tree.rs new file mode 100644 index 0000000..2402c2f --- /dev/null +++ b/src/renderer/breadcrumb_tree.rs @@ -0,0 +1,39 @@ +use crate::renderer::context_element::IntoContextElement; +use std::borrow::Borrow; + +pub trait BreadcrumbTree { + fn get_ice(&self) -> &dyn IntoContextElement; + + fn get_parent(&self) -> Option<&dyn BreadcrumbTree>; +} + +struct BreadcrumbTreeNode<'a, C: IntoContextElement> { + parent: Option<&'a dyn BreadcrumbTree>, + element: BreadcrumbTreeNodeElement<'a, C>, +} + +enum BreadcrumbTreeNodeElement<'a, C: IntoContextElement> { + Owned(C), + Borrowed(&'a C), +} + +impl<'a, C: IntoContextElement> Borrow + for BreadcrumbTreeNodeElement<'a, C> +{ + fn borrow(&self) -> &(dyn IntoContextElement + 'a) { + match self { + BreadcrumbTreeNodeElement::Owned(ice) => ice, + BreadcrumbTreeNodeElement::Borrowed(ice) => *ice, + } + } +} + +impl<'a, C: IntoContextElement> BreadcrumbTree for BreadcrumbTreeNode<'a, C> { + fn get_ice(&self) -> &dyn IntoContextElement { + self.element.borrow() + } + + fn get_parent(&self) -> Option<&dyn BreadcrumbTree> { + self.parent + } +} diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index dfbbbe6..3439bc5 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -1,5 +1,6 @@ //! This module contains a renderer for a rust implementation of LinkedIn Dust +mod breadcrumb_tree; mod context_element; mod errors; mod inline_partial_tree; From 1be60511edb374c40dc026af614ae70bea355771 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 14:17:21 -0400 Subject: [PATCH 03/67] Implement an iterator over the tree. --- src/renderer/breadcrumb_tree.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/renderer/breadcrumb_tree.rs b/src/renderer/breadcrumb_tree.rs index 2402c2f..9b00fea 100644 --- a/src/renderer/breadcrumb_tree.rs +++ b/src/renderer/breadcrumb_tree.rs @@ -37,3 +37,15 @@ impl<'a, C: IntoContextElement> BreadcrumbTree for BreadcrumbTreeNode<'a, C> { self.parent } } + +struct BreadcrumbTreeIterator<'a>(Option<&'a dyn BreadcrumbTree>); + +impl<'a> Iterator for BreadcrumbTreeIterator<'a> { + type Item = &'a dyn IntoContextElement; + + fn next(&mut self) -> Option { + let ret = self.0; + self.0 = self.0.map(|node| node.get_parent()).flatten(); + ret.map(|node| node.get_ice()) + } +} From bebedf56e43a5d9035965b6c6963687a9072b79a Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 14:27:31 -0400 Subject: [PATCH 04/67] Implement IntoIterator for references to a BreadcrumbTreeNode. --- src/renderer/breadcrumb_tree.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/renderer/breadcrumb_tree.rs b/src/renderer/breadcrumb_tree.rs index 9b00fea..414cc96 100644 --- a/src/renderer/breadcrumb_tree.rs +++ b/src/renderer/breadcrumb_tree.rs @@ -49,3 +49,12 @@ impl<'a> Iterator for BreadcrumbTreeIterator<'a> { ret.map(|node| node.get_ice()) } } + +impl<'a, C: IntoContextElement> IntoIterator for &'a BreadcrumbTreeNode<'a, C> { + type Item = &'a dyn IntoContextElement; + type IntoIter = BreadcrumbTreeIterator<'a>; + + fn into_iter(self) -> BreadcrumbTreeIterator<'a> { + BreadcrumbTreeIterator(Some(self)) + } +} From 456da98bed4182bb4155d2212bed333dd15ecd48 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 14:29:59 -0400 Subject: [PATCH 05/67] Added an iter function which I think will let me automatically make iterators without having to explicitly take a reference to the tree. --- src/renderer/breadcrumb_tree.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/renderer/breadcrumb_tree.rs b/src/renderer/breadcrumb_tree.rs index 414cc96..1faf0c3 100644 --- a/src/renderer/breadcrumb_tree.rs +++ b/src/renderer/breadcrumb_tree.rs @@ -17,6 +17,12 @@ enum BreadcrumbTreeNodeElement<'a, C: IntoContextElement> { Borrowed(&'a C), } +impl<'a, C: IntoContextElement> BreadcrumbTreeNode<'a, C> { + pub fn iter(&'a self) -> BreadcrumbTreeIterator<'a> { + BreadcrumbTreeIterator(Some(self)) + } +} + impl<'a, C: IntoContextElement> Borrow for BreadcrumbTreeNodeElement<'a, C> { @@ -55,6 +61,6 @@ impl<'a, C: IntoContextElement> IntoIterator for &'a BreadcrumbTreeNode<'a, C> { type IntoIter = BreadcrumbTreeIterator<'a>; fn into_iter(self) -> BreadcrumbTreeIterator<'a> { - BreadcrumbTreeIterator(Some(self)) + self.iter() } } From 3f49519bea099787429de28a5deb364e1a8626e1 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 15:44:09 -0400 Subject: [PATCH 06/67] Initial structure for new implementation of walking functions using the heterogeneous tree. --- src/renderer/breadcrumb_tree.rs | 4 ++-- src/renderer/mod.rs | 1 + src/renderer/tree_walking.rs | 37 +++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 src/renderer/tree_walking.rs diff --git a/src/renderer/breadcrumb_tree.rs b/src/renderer/breadcrumb_tree.rs index 1faf0c3..3535983 100644 --- a/src/renderer/breadcrumb_tree.rs +++ b/src/renderer/breadcrumb_tree.rs @@ -7,7 +7,7 @@ pub trait BreadcrumbTree { fn get_parent(&self) -> Option<&dyn BreadcrumbTree>; } -struct BreadcrumbTreeNode<'a, C: IntoContextElement> { +pub struct BreadcrumbTreeNode<'a, C: IntoContextElement> { parent: Option<&'a dyn BreadcrumbTree>, element: BreadcrumbTreeNodeElement<'a, C>, } @@ -44,7 +44,7 @@ impl<'a, C: IntoContextElement> BreadcrumbTree for BreadcrumbTreeNode<'a, C> { } } -struct BreadcrumbTreeIterator<'a>(Option<&'a dyn BreadcrumbTree>); +pub struct BreadcrumbTreeIterator<'a>(Option<&'a dyn BreadcrumbTree>); impl<'a> Iterator for BreadcrumbTreeIterator<'a> { type Item = &'a dyn IntoContextElement; diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 3439bc5..7eecacb 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -7,6 +7,7 @@ mod inline_partial_tree; mod iteration_context; mod parameters_context; mod renderer; +mod tree_walking; mod walking; pub use context_element::CloneIntoBoxedContextElement; diff --git a/src/renderer/tree_walking.rs b/src/renderer/tree_walking.rs new file mode 100644 index 0000000..5050652 --- /dev/null +++ b/src/renderer/tree_walking.rs @@ -0,0 +1,37 @@ +use crate::renderer::breadcrumb_tree::BreadcrumbTree; +use crate::renderer::breadcrumb_tree::BreadcrumbTreeNode; +use crate::renderer::context_element::IntoContextElement; +use crate::renderer::WalkError; +use std::borrow::Borrow; + +enum WalkResult<'a> { + NoWalk, + PartialWalk, + FullyWalked(&'a dyn IntoContextElement), +} + +fn walk_path_from_single_level<'a, P, C>(context: &'a C, path: &[P]) -> WalkResult<'a> +where + P: Borrow, + C: Borrow, +{ + todo!() +} + +pub fn walk_path<'a, B, P>( + maybe_breadcrumbs: Option<&'a BreadcrumbTreeNode>, + path: &Vec

, +) -> Result<&'a dyn IntoContextElement, WalkError> +where + B: IntoContextElement, + P: Borrow, +{ + match (maybe_breadcrumbs, path.first()) { + (None, _) => return Err(WalkError::CantWalk), + (Some(breadcrumbs), None) => return Ok(breadcrumbs.get_ice()), + (Some(breadcrumbs), Some(path_first)) if path_first.borrow() == "." => (), + (Some(breadcrumbs), Some(path_first)) => (), + } + + Err(WalkError::CantWalk) +} From 89c751e849b186b5a9b143ab1d04e3285ab890d4 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 15:51:18 -0400 Subject: [PATCH 07/67] I think I need to implement iterating on the tree nodes. --- src/renderer/tree_walking.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/renderer/tree_walking.rs b/src/renderer/tree_walking.rs index 5050652..574c99e 100644 --- a/src/renderer/tree_walking.rs +++ b/src/renderer/tree_walking.rs @@ -18,6 +18,15 @@ where todo!() } +fn get_first_non_pseudo_element<'a, B>( + breadcrumbs: &'a BreadcrumbTreeNode, +) -> Option<&'a dyn IntoContextElement> +where + B: IntoContextElement, +{ + breadcrumbs.iter().filter(|b| b.is_pseudo_element()).next() +} + pub fn walk_path<'a, B, P>( maybe_breadcrumbs: Option<&'a BreadcrumbTreeNode>, path: &Vec

, @@ -29,7 +38,13 @@ where match (maybe_breadcrumbs, path.first()) { (None, _) => return Err(WalkError::CantWalk), (Some(breadcrumbs), None) => return Ok(breadcrumbs.get_ice()), - (Some(breadcrumbs), Some(path_first)) if path_first.borrow() == "." => (), + (Some(breadcrumbs), Some(path_first)) if path_first.borrow() == "." => { + let first_non_pseudo_element = get_first_non_pseudo_element(breadcrumbs); + // return match first_non_pseudo_element { + // None => Err(WalkError::CantWalk), + // Some(current_context) + // } + } (Some(breadcrumbs), Some(path_first)) => (), } From ff13d22ab65530b1941d12fd618c6bd65710e031 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 15:52:46 -0400 Subject: [PATCH 08/67] Renamed BreadcrumbTreeIterator to IceTreeIterator. --- src/renderer/breadcrumb_tree.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/renderer/breadcrumb_tree.rs b/src/renderer/breadcrumb_tree.rs index 3535983..00e1804 100644 --- a/src/renderer/breadcrumb_tree.rs +++ b/src/renderer/breadcrumb_tree.rs @@ -18,8 +18,8 @@ enum BreadcrumbTreeNodeElement<'a, C: IntoContextElement> { } impl<'a, C: IntoContextElement> BreadcrumbTreeNode<'a, C> { - pub fn iter(&'a self) -> BreadcrumbTreeIterator<'a> { - BreadcrumbTreeIterator(Some(self)) + pub fn iter(&'a self) -> IceTreeIterator<'a> { + IceTreeIterator(Some(self)) } } @@ -44,9 +44,9 @@ impl<'a, C: IntoContextElement> BreadcrumbTree for BreadcrumbTreeNode<'a, C> { } } -pub struct BreadcrumbTreeIterator<'a>(Option<&'a dyn BreadcrumbTree>); +pub struct IceTreeIterator<'a>(Option<&'a dyn BreadcrumbTree>); -impl<'a> Iterator for BreadcrumbTreeIterator<'a> { +impl<'a> Iterator for IceTreeIterator<'a> { type Item = &'a dyn IntoContextElement; fn next(&mut self) -> Option { @@ -58,9 +58,9 @@ impl<'a> Iterator for BreadcrumbTreeIterator<'a> { impl<'a, C: IntoContextElement> IntoIterator for &'a BreadcrumbTreeNode<'a, C> { type Item = &'a dyn IntoContextElement; - type IntoIter = BreadcrumbTreeIterator<'a>; + type IntoIter = IceTreeIterator<'a>; - fn into_iter(self) -> BreadcrumbTreeIterator<'a> { + fn into_iter(self) -> IceTreeIterator<'a> { self.iter() } } From 781b8f645a1681040c39ac676586c07d727b7c50 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 15:58:29 -0400 Subject: [PATCH 09/67] Switch IntoIter over to the breadcrumb iterator. --- src/renderer/breadcrumb_tree.rs | 26 +++++++++++++++++++++----- src/renderer/tree_walking.rs | 3 ++- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/renderer/breadcrumb_tree.rs b/src/renderer/breadcrumb_tree.rs index 00e1804..b4bc32a 100644 --- a/src/renderer/breadcrumb_tree.rs +++ b/src/renderer/breadcrumb_tree.rs @@ -18,9 +18,13 @@ enum BreadcrumbTreeNodeElement<'a, C: IntoContextElement> { } impl<'a, C: IntoContextElement> BreadcrumbTreeNode<'a, C> { - pub fn iter(&'a self) -> IceTreeIterator<'a> { + pub fn ice_iter(&'a self) -> IceTreeIterator<'a> { IceTreeIterator(Some(self)) } + + pub fn breadcrumb_iter(&'a self) -> BreadcrumbTreeIterator<'a> { + BreadcrumbTreeIterator(Some(self)) + } } impl<'a, C: IntoContextElement> Borrow @@ -57,10 +61,22 @@ impl<'a> Iterator for IceTreeIterator<'a> { } impl<'a, C: IntoContextElement> IntoIterator for &'a BreadcrumbTreeNode<'a, C> { - type Item = &'a dyn IntoContextElement; - type IntoIter = IceTreeIterator<'a>; + type Item = &'a dyn BreadcrumbTree; + type IntoIter = BreadcrumbTreeIterator<'a>; - fn into_iter(self) -> IceTreeIterator<'a> { - self.iter() + fn into_iter(self) -> BreadcrumbTreeIterator<'a> { + self.breadcrumb_iter() + } +} + +pub struct BreadcrumbTreeIterator<'a>(Option<&'a dyn BreadcrumbTree>); + +impl<'a> Iterator for BreadcrumbTreeIterator<'a> { + type Item = &'a dyn BreadcrumbTree; + + fn next(&mut self) -> Option { + let ret = self.0; + self.0 = self.0.map(|node| node.get_parent()).flatten(); + ret } } diff --git a/src/renderer/tree_walking.rs b/src/renderer/tree_walking.rs index 574c99e..65f6cbd 100644 --- a/src/renderer/tree_walking.rs +++ b/src/renderer/tree_walking.rs @@ -24,7 +24,8 @@ fn get_first_non_pseudo_element<'a, B>( where B: IntoContextElement, { - breadcrumbs.iter().filter(|b| b.is_pseudo_element()).next() + todo!() + // breadcrumbs.iter().filter(|b| b.is_pseudo_element()).next() } pub fn walk_path<'a, B, P>( From 863744c42dd932015f1fd96c73bd0ae01cc970f2 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 16:02:30 -0400 Subject: [PATCH 10/67] Got rid of the IceTreeIterator by using map(). --- src/renderer/breadcrumb_tree.rs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/renderer/breadcrumb_tree.rs b/src/renderer/breadcrumb_tree.rs index b4bc32a..b84eb10 100644 --- a/src/renderer/breadcrumb_tree.rs +++ b/src/renderer/breadcrumb_tree.rs @@ -18,8 +18,8 @@ enum BreadcrumbTreeNodeElement<'a, C: IntoContextElement> { } impl<'a, C: IntoContextElement> BreadcrumbTreeNode<'a, C> { - pub fn ice_iter(&'a self) -> IceTreeIterator<'a> { - IceTreeIterator(Some(self)) + pub fn ice_iter(&'a self) -> impl Iterator { + self.breadcrumb_iter().map(|b| b.get_ice()) } pub fn breadcrumb_iter(&'a self) -> BreadcrumbTreeIterator<'a> { @@ -48,18 +48,6 @@ impl<'a, C: IntoContextElement> BreadcrumbTree for BreadcrumbTreeNode<'a, C> { } } -pub struct IceTreeIterator<'a>(Option<&'a dyn BreadcrumbTree>); - -impl<'a> Iterator for IceTreeIterator<'a> { - type Item = &'a dyn IntoContextElement; - - fn next(&mut self) -> Option { - let ret = self.0; - self.0 = self.0.map(|node| node.get_parent()).flatten(); - ret.map(|node| node.get_ice()) - } -} - impl<'a, C: IntoContextElement> IntoIterator for &'a BreadcrumbTreeNode<'a, C> { type Item = &'a dyn BreadcrumbTree; type IntoIter = BreadcrumbTreeIterator<'a>; From 65445cc8fcbb456e85f4adf9f7fb62516533858a Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 16:16:21 -0400 Subject: [PATCH 11/67] Finished implementing the new tree walking. --- src/renderer/tree_walking.rs | 65 ++++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 11 deletions(-) diff --git a/src/renderer/tree_walking.rs b/src/renderer/tree_walking.rs index 65f6cbd..4cb2c41 100644 --- a/src/renderer/tree_walking.rs +++ b/src/renderer/tree_walking.rs @@ -10,22 +10,44 @@ enum WalkResult<'a> { FullyWalked(&'a dyn IntoContextElement), } -fn walk_path_from_single_level<'a, P, C>(context: &'a C, path: &[P]) -> WalkResult<'a> +fn walk_path_from_single_level<'a, P>( + context: &'a dyn IntoContextElement, + path: &[P], +) -> WalkResult<'a> where P: Borrow, - C: Borrow, { - todo!() + if path.is_empty() { + return WalkResult::FullyWalked(context); + } + + let mut walk_failure = WalkResult::NoWalk; + let mut output = context; + for elem in path.iter() { + match output.walk(elem.borrow()) { + Err(WalkError::CantWalk { .. }) => { + return walk_failure; + } + Ok(new_val) => { + walk_failure = WalkResult::PartialWalk; + output = new_val; + } + } + } + + WalkResult::FullyWalked(output) } fn get_first_non_pseudo_element<'a, B>( breadcrumbs: &'a BreadcrumbTreeNode, -) -> Option<&'a dyn IntoContextElement> +) -> Option<&'a dyn BreadcrumbTree> where B: IntoContextElement, { - todo!() - // breadcrumbs.iter().filter(|b| b.is_pseudo_element()).next() + breadcrumbs + .breadcrumb_iter() + .filter(|b| b.get_ice().is_pseudo_element()) + .next() } pub fn walk_path<'a, B, P>( @@ -41,12 +63,33 @@ where (Some(breadcrumbs), None) => return Ok(breadcrumbs.get_ice()), (Some(breadcrumbs), Some(path_first)) if path_first.borrow() == "." => { let first_non_pseudo_element = get_first_non_pseudo_element(breadcrumbs); - // return match first_non_pseudo_element { - // None => Err(WalkError::CantWalk), - // Some(current_context) - // } + return match first_non_pseudo_element { + None => Err(WalkError::CantWalk), + Some(current_context) => { + match walk_path_from_single_level(current_context.get_ice(), &path[1..]) { + // If no walking was done at all or we partially walked + // then stop trying to find anything because '.' restricts + // us to the current scope + WalkResult::NoWalk | WalkResult::PartialWalk => Err(WalkError::CantWalk), + WalkResult::FullyWalked(new_context) => Ok(new_context), + } + } + }; + } + (Some(breadcrumbs), Some(path_first)) => { + for context in breadcrumbs.ice_iter() { + 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), + } + } } - (Some(breadcrumbs), Some(path_first)) => (), } Err(WalkError::CantWalk) From ff27c2c85d81a6f50c5f9c6e11956e56471c7171 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 16:21:13 -0400 Subject: [PATCH 12/67] Basic structure for the new tree renderer. --- src/renderer/mod.rs | 1 + src/renderer/tree_renderer.rs | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 src/renderer/tree_renderer.rs diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 7eecacb..7debf9a 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -7,6 +7,7 @@ mod inline_partial_tree; mod iteration_context; mod parameters_context; mod renderer; +mod tree_renderer; mod tree_walking; mod walking; diff --git a/src/renderer/tree_renderer.rs b/src/renderer/tree_renderer.rs new file mode 100644 index 0000000..5965ffe --- /dev/null +++ b/src/renderer/tree_renderer.rs @@ -0,0 +1,35 @@ +use crate::parser::template; +use crate::parser::Template; +use crate::renderer::errors::CompileError; +use std::collections::HashMap; + +#[derive(Clone, Debug)] +pub struct CompiledTemplate<'a> { + template: Template<'a>, + pub name: String, +} + +#[derive(Clone, Debug)] +pub struct DustRenderer<'a> { + templates: HashMap>, +} + +pub fn compile_template<'a>( + source: &'a str, + name: String, +) -> Result, CompileError> { + // TODO: This could use better error management + let (_remaining, parsed_template) = template(source).expect("Failed to compile template"); + Ok(CompiledTemplate { + template: parsed_template, + name: name, + }) +} + +impl<'a> DustRenderer<'a> { + pub fn new() -> DustRenderer<'a> { + DustRenderer { + templates: HashMap::new(), + } + } +} From 92dca74505b6b4e4b9bdd0abdd2139a42924105d Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 16:40:39 -0400 Subject: [PATCH 13/67] Running into issue where I need to know the type to create new tree node elements. --- src/renderer/breadcrumb_tree.rs | 2 +- src/renderer/tree_renderer.rs | 52 +++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/renderer/breadcrumb_tree.rs b/src/renderer/breadcrumb_tree.rs index b84eb10..0fe40e6 100644 --- a/src/renderer/breadcrumb_tree.rs +++ b/src/renderer/breadcrumb_tree.rs @@ -12,7 +12,7 @@ pub struct BreadcrumbTreeNode<'a, C: IntoContextElement> { element: BreadcrumbTreeNodeElement<'a, C>, } -enum BreadcrumbTreeNodeElement<'a, C: IntoContextElement> { +pub enum BreadcrumbTreeNodeElement<'a, C: IntoContextElement> { Owned(C), Borrowed(&'a C), } diff --git a/src/renderer/tree_renderer.rs b/src/renderer/tree_renderer.rs index 5965ffe..0f1c0a4 100644 --- a/src/renderer/tree_renderer.rs +++ b/src/renderer/tree_renderer.rs @@ -1,5 +1,11 @@ use crate::parser::template; +use crate::parser::Path; use crate::parser::Template; +use crate::renderer::breadcrumb_tree::BreadcrumbTree; +use crate::renderer::breadcrumb_tree::BreadcrumbTreeNode; +use crate::renderer::breadcrumb_tree::BreadcrumbTreeNodeElement; +use crate::renderer::context_element::ContextElement; +use crate::renderer::context_element::IntoContextElement; use crate::renderer::errors::CompileError; use std::collections::HashMap; @@ -32,4 +38,50 @@ impl<'a> DustRenderer<'a> { templates: HashMap::new(), } } + + pub fn load_source(&mut self, template: &'a CompiledTemplate) { + self.templates + .insert(template.name.clone(), &template.template); + } + + fn new_breadcrumbs_section<'b, B>( + &self, + maybe_breadcrumbs: Option<&'a BreadcrumbTreeNode>, + index_context: Option<&'b dyn IntoContextElement>, + injected_context: Option<&'b dyn IntoContextElement>, + explicit_context: &Option>, + new_context_element: Option<&'b dyn ContextElement>, + ) -> Option<&'a BreadcrumbTreeNode> + where + B: IntoContextElement, + { + // If there is no new content, return the original breadcrumbs + match ( + index_context, + injected_context, + explicit_context, + new_context_element, + ) { + (None, None, None, None) => return maybe_breadcrumbs, + _ => (), + } + + // If there is an explicit context, then drop all the current + // context + let mut new_stack = match explicit_context { + Some(_) => None, + None => maybe_breadcrumbs, + }; + + // TODO: Explicit context + injected_context.map(|ctx| { + new_stack = Some(BreadcrumbTreeNode { + parent: new_stack.map(|b| b as _), + element: BreadcrumbTreeNodeElement::Borrowed(ctx), + }); + // TODO + }); + + None + } } From 2b532e7eb474743feb34469d29d5f32a0d14c54b Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 17:25:41 -0400 Subject: [PATCH 14/67] Going to try switching to boxed owned values to remove the template. --- src/renderer/breadcrumb_tree.rs | 22 +++++----- src/renderer/tree_renderer.rs | 74 ++++++++++++++++----------------- src/renderer/tree_walking.rs | 14 +++---- 3 files changed, 52 insertions(+), 58 deletions(-) diff --git a/src/renderer/breadcrumb_tree.rs b/src/renderer/breadcrumb_tree.rs index 0fe40e6..7b502cc 100644 --- a/src/renderer/breadcrumb_tree.rs +++ b/src/renderer/breadcrumb_tree.rs @@ -7,17 +7,17 @@ pub trait BreadcrumbTree { fn get_parent(&self) -> Option<&dyn BreadcrumbTree>; } -pub struct BreadcrumbTreeNode<'a, C: IntoContextElement> { +pub struct BreadcrumbTreeNode<'a> { parent: Option<&'a dyn BreadcrumbTree>, - element: BreadcrumbTreeNodeElement<'a, C>, + element: BreadcrumbTreeNodeElement<'a>, } -pub enum BreadcrumbTreeNodeElement<'a, C: IntoContextElement> { - Owned(C), - Borrowed(&'a C), +pub enum BreadcrumbTreeNodeElement<'a> { + Owned(Box), + Borrowed(&'a dyn IntoContextElement), } -impl<'a, C: IntoContextElement> BreadcrumbTreeNode<'a, C> { +impl<'a> BreadcrumbTreeNode<'a> { pub fn ice_iter(&'a self) -> impl Iterator { self.breadcrumb_iter().map(|b| b.get_ice()) } @@ -27,18 +27,16 @@ impl<'a, C: IntoContextElement> BreadcrumbTreeNode<'a, C> { } } -impl<'a, C: IntoContextElement> Borrow - for BreadcrumbTreeNodeElement<'a, C> -{ +impl<'a> Borrow for BreadcrumbTreeNodeElement<'a> { fn borrow(&self) -> &(dyn IntoContextElement + 'a) { match self { - BreadcrumbTreeNodeElement::Owned(ice) => ice, + BreadcrumbTreeNodeElement::Owned(ice) => ice.as_ref(), BreadcrumbTreeNodeElement::Borrowed(ice) => *ice, } } } -impl<'a, C: IntoContextElement> BreadcrumbTree for BreadcrumbTreeNode<'a, C> { +impl<'a> BreadcrumbTree for BreadcrumbTreeNode<'a> { fn get_ice(&self) -> &dyn IntoContextElement { self.element.borrow() } @@ -48,7 +46,7 @@ impl<'a, C: IntoContextElement> BreadcrumbTree for BreadcrumbTreeNode<'a, C> { } } -impl<'a, C: IntoContextElement> IntoIterator for &'a BreadcrumbTreeNode<'a, C> { +impl<'a> IntoIterator for &'a BreadcrumbTreeNode<'a> { type Item = &'a dyn BreadcrumbTree; type IntoIter = BreadcrumbTreeIterator<'a>; diff --git a/src/renderer/tree_renderer.rs b/src/renderer/tree_renderer.rs index 0f1c0a4..de7fee2 100644 --- a/src/renderer/tree_renderer.rs +++ b/src/renderer/tree_renderer.rs @@ -44,44 +44,44 @@ impl<'a> DustRenderer<'a> { .insert(template.name.clone(), &template.template); } - fn new_breadcrumbs_section<'b, B>( - &self, - maybe_breadcrumbs: Option<&'a BreadcrumbTreeNode>, - index_context: Option<&'b dyn IntoContextElement>, - injected_context: Option<&'b dyn IntoContextElement>, - explicit_context: &Option>, - new_context_element: Option<&'b dyn ContextElement>, - ) -> Option<&'a BreadcrumbTreeNode> - where - B: IntoContextElement, - { - // If there is no new content, return the original breadcrumbs - match ( - index_context, - injected_context, - explicit_context, - new_context_element, - ) { - (None, None, None, None) => return maybe_breadcrumbs, - _ => (), - } + // fn new_breadcrumbs_section<'b, B>( + // &self, + // maybe_breadcrumbs: Option<&'a BreadcrumbTreeNode>, + // index_context: Option<&'b dyn IntoContextElement>, + // injected_context: Option<&'b dyn IntoContextElement>, + // explicit_context: &Option>, + // new_context_element: Option<&'b dyn ContextElement>, + // ) -> Option<&'a BreadcrumbTreeNode> + // where + // B: IntoContextElement, + // { + // // If there is no new content, return the original breadcrumbs + // match ( + // index_context, + // injected_context, + // explicit_context, + // new_context_element, + // ) { + // (None, None, None, None) => return maybe_breadcrumbs, + // _ => (), + // } - // If there is an explicit context, then drop all the current - // context - let mut new_stack = match explicit_context { - Some(_) => None, - None => maybe_breadcrumbs, - }; + // // If there is an explicit context, then drop all the current + // // context + // let mut new_stack = match explicit_context { + // Some(_) => None, + // None => maybe_breadcrumbs, + // }; - // TODO: Explicit context - injected_context.map(|ctx| { - new_stack = Some(BreadcrumbTreeNode { - parent: new_stack.map(|b| b as _), - element: BreadcrumbTreeNodeElement::Borrowed(ctx), - }); - // TODO - }); + // // TODO: Explicit context + // // injected_context.map(|ctx| { + // // new_stack = Some(BreadcrumbTreeNode { + // // parent: new_stack.map(|b| b as _), + // // element: BreadcrumbTreeNodeElement::Borrowed(ctx), + // // }); + // // // TODO + // // }); - None - } + // None + // } } diff --git a/src/renderer/tree_walking.rs b/src/renderer/tree_walking.rs index 4cb2c41..b68b775 100644 --- a/src/renderer/tree_walking.rs +++ b/src/renderer/tree_walking.rs @@ -38,24 +38,20 @@ where WalkResult::FullyWalked(output) } -fn get_first_non_pseudo_element<'a, B>( - breadcrumbs: &'a BreadcrumbTreeNode, -) -> Option<&'a dyn BreadcrumbTree> -where - B: IntoContextElement, -{ +fn get_first_non_pseudo_element<'a>( + breadcrumbs: &'a BreadcrumbTreeNode, +) -> Option<&'a dyn BreadcrumbTree> { breadcrumbs .breadcrumb_iter() .filter(|b| b.get_ice().is_pseudo_element()) .next() } -pub fn walk_path<'a, B, P>( - maybe_breadcrumbs: Option<&'a BreadcrumbTreeNode>, +pub fn walk_path<'a, P>( + maybe_breadcrumbs: Option<&'a BreadcrumbTreeNode>, path: &Vec

, ) -> Result<&'a dyn IntoContextElement, WalkError> where - B: IntoContextElement, P: Borrow, { match (maybe_breadcrumbs, path.first()) { From b381789422caa8714ea6a14cdf6e92376e3ebcf1 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 17:32:06 -0400 Subject: [PATCH 15/67] Slight progress on new_breadcrumbs_section. --- src/renderer/breadcrumb_tree.rs | 10 +++++ src/renderer/tree_renderer.rs | 72 ++++++++++++++++----------------- 2 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/renderer/breadcrumb_tree.rs b/src/renderer/breadcrumb_tree.rs index 7b502cc..46184b9 100644 --- a/src/renderer/breadcrumb_tree.rs +++ b/src/renderer/breadcrumb_tree.rs @@ -18,6 +18,16 @@ pub enum BreadcrumbTreeNodeElement<'a> { } impl<'a> BreadcrumbTreeNode<'a> { + pub fn new( + parent: Option<&'a dyn BreadcrumbTree>, + element: BreadcrumbTreeNodeElement<'a>, + ) -> Self { + BreadcrumbTreeNode { + parent: parent, + element: element, + } + } + pub fn ice_iter(&'a self) -> impl Iterator { self.breadcrumb_iter().map(|b| b.get_ice()) } diff --git a/src/renderer/tree_renderer.rs b/src/renderer/tree_renderer.rs index de7fee2..4016d09 100644 --- a/src/renderer/tree_renderer.rs +++ b/src/renderer/tree_renderer.rs @@ -44,44 +44,42 @@ impl<'a> DustRenderer<'a> { .insert(template.name.clone(), &template.template); } - // fn new_breadcrumbs_section<'b, B>( - // &self, - // maybe_breadcrumbs: Option<&'a BreadcrumbTreeNode>, - // index_context: Option<&'b dyn IntoContextElement>, - // injected_context: Option<&'b dyn IntoContextElement>, - // explicit_context: &Option>, - // new_context_element: Option<&'b dyn ContextElement>, - // ) -> Option<&'a BreadcrumbTreeNode> - // where - // B: IntoContextElement, - // { - // // If there is no new content, return the original breadcrumbs - // match ( - // index_context, - // injected_context, - // explicit_context, - // new_context_element, - // ) { - // (None, None, None, None) => return maybe_breadcrumbs, - // _ => (), - // } + fn new_breadcrumbs_section<'b>( + &self, + maybe_breadcrumbs: Option<&'a BreadcrumbTreeNode>, + index_context: Option<&'b dyn IntoContextElement>, + injected_context: Option<&'b dyn IntoContextElement>, + explicit_context: &Option>, + new_context_element: Option<&'b dyn ContextElement>, + ) -> Option<&'a BreadcrumbTreeNode> { + // If there is no new content, return the original breadcrumbs + match ( + index_context, + injected_context, + explicit_context, + new_context_element, + ) { + (None, None, None, None) => return maybe_breadcrumbs, + _ => (), + } - // // If there is an explicit context, then drop all the current - // // context - // let mut new_stack = match explicit_context { - // Some(_) => None, - // None => maybe_breadcrumbs, - // }; + // If there is an explicit context, then drop all the current + // context + let mut parent = match explicit_context { + Some(_) => None, + None => maybe_breadcrumbs, + }; + let mut new_stack = None; - // // TODO: Explicit context - // // injected_context.map(|ctx| { - // // new_stack = Some(BreadcrumbTreeNode { - // // parent: new_stack.map(|b| b as _), - // // element: BreadcrumbTreeNodeElement::Borrowed(ctx), - // // }); - // // // TODO - // // }); + // TODO: Explicit context - // None - // } + injected_context.map(|ctx| { + new_stack = Some(BreadcrumbTreeNode::new( + parent.map(|b| b as _), + BreadcrumbTreeNodeElement::Borrowed(ctx), + )) + }); + + None + } } From de5914417e097a629445c67f8d9045449898d432 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 17:56:07 -0400 Subject: [PATCH 16/67] All but explicit context for new_breadcrumbs_section. --- src/renderer/tree_renderer.rs | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/renderer/tree_renderer.rs b/src/renderer/tree_renderer.rs index 4016d09..569b104 100644 --- a/src/renderer/tree_renderer.rs +++ b/src/renderer/tree_renderer.rs @@ -44,6 +44,15 @@ impl<'a> DustRenderer<'a> { .insert(template.name.clone(), &template.template); } + /// Returns a option of a tuple of (parent, new_node_elements) + /// which can then be formed into new BreadcrumbTreeNodes + /// + /// If None is returned, then it is a signal to simply re-use the + /// existing breadcrumbs. + /// + /// Otherwise, the parent (which may be None, especially for + /// explicit contexts) and the additional node elements (which may + /// be empty) should be combined into a final BreadcrumbTreeNode fn new_breadcrumbs_section<'b>( &self, maybe_breadcrumbs: Option<&'a BreadcrumbTreeNode>, @@ -51,15 +60,20 @@ impl<'a> DustRenderer<'a> { injected_context: Option<&'b dyn IntoContextElement>, explicit_context: &Option>, new_context_element: Option<&'b dyn ContextElement>, - ) -> Option<&'a BreadcrumbTreeNode> { - // If there is no new content, return the original breadcrumbs + ) -> Option<( + Option<&'b BreadcrumbTreeNode>, + Vec>, + )> { + // If none of the additional contexts are present, return None + // to signal that the original breadcrumbs should be used + // rather than incurring a copy here. match ( index_context, injected_context, explicit_context, new_context_element, ) { - (None, None, None, None) => return maybe_breadcrumbs, + (None, None, None, None) => return None, _ => (), } @@ -69,17 +83,18 @@ impl<'a> DustRenderer<'a> { Some(_) => None, None => maybe_breadcrumbs, }; - let mut new_stack = None; + let mut new_nodes = Vec::new(); // TODO: Explicit context - injected_context.map(|ctx| { - new_stack = Some(BreadcrumbTreeNode::new( - parent.map(|b| b as _), - BreadcrumbTreeNodeElement::Borrowed(ctx), + injected_context.map(|ctx| new_nodes.push(BreadcrumbTreeNodeElement::Borrowed(ctx))); + new_context_element.map(|ctx| { + new_nodes.push(BreadcrumbTreeNodeElement::Borrowed( + ctx.from_context_element(), )) }); + index_context.map(|ctx| new_nodes.push(BreadcrumbTreeNodeElement::Borrowed(ctx))); - None + Some((parent, new_nodes)) } } From 1111b482704953de84ceeafbcd27bf899d7a0743 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 18:02:02 -0400 Subject: [PATCH 17/67] Due to traits I am going to have to start hard-replacing the old renderer file. --- src/renderer/tree_renderer.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/renderer/tree_renderer.rs b/src/renderer/tree_renderer.rs index 569b104..d3516f7 100644 --- a/src/renderer/tree_renderer.rs +++ b/src/renderer/tree_renderer.rs @@ -7,6 +7,7 @@ use crate::renderer::breadcrumb_tree::BreadcrumbTreeNodeElement; use crate::renderer::context_element::ContextElement; use crate::renderer::context_element::IntoContextElement; use crate::renderer::errors::CompileError; +use crate::renderer::tree_walking::walk_path; use std::collections::HashMap; #[derive(Clone, Debug)] @@ -85,8 +86,18 @@ impl<'a> DustRenderer<'a> { }; let mut new_nodes = Vec::new(); - // TODO: Explicit context - + explicit_context.as_ref().map(|path| { + let x = walk_path(maybe_breadcrumbs, &path.keys); + x.map(|ice| ice.into_context_element(self, breadcrumbs)) + .map(|val| if val.is_truthy() {}); + // walk_path(breadcrumbs, &path.keys) + // .map(|ice| ice.into_context_element(self, breadcrumbs)) + // .map(|val| { + // if val.is_truthy() { + // new_stack.push(val.from_context_element()) + // } + // }); + }); injected_context.map(|ctx| new_nodes.push(BreadcrumbTreeNodeElement::Borrowed(ctx))); new_context_element.map(|ctx| { new_nodes.push(BreadcrumbTreeNodeElement::Borrowed( From 439601bbb5b3c601eb444ce97eabea49045024ac Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 18:05:49 -0400 Subject: [PATCH 18/67] First I'll try to eliminate the duplication between the BreadcrumbTree and BreadcrumbTreeNode. --- src/renderer/tree_renderer.rs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/renderer/tree_renderer.rs b/src/renderer/tree_renderer.rs index d3516f7..11eba11 100644 --- a/src/renderer/tree_renderer.rs +++ b/src/renderer/tree_renderer.rs @@ -86,18 +86,15 @@ impl<'a> DustRenderer<'a> { }; let mut new_nodes = Vec::new(); - explicit_context.as_ref().map(|path| { - let x = walk_path(maybe_breadcrumbs, &path.keys); - x.map(|ice| ice.into_context_element(self, breadcrumbs)) - .map(|val| if val.is_truthy() {}); - // walk_path(breadcrumbs, &path.keys) - // .map(|ice| ice.into_context_element(self, breadcrumbs)) - // .map(|val| { - // if val.is_truthy() { - // new_stack.push(val.from_context_element()) - // } - // }); - }); + // explicit_context.as_ref().map(|path| { + // let x = walk_path(maybe_breadcrumbs, &path.keys); + // x.map(|ice| ice.into_context_element(self, breadcrumbs)) + // .map(|val| { + // if val.is_truthy() { + // new_nodes.push(val.from_context_element()) + // } + // }); + // }); injected_context.map(|ctx| new_nodes.push(BreadcrumbTreeNodeElement::Borrowed(ctx))); new_context_element.map(|ctx| { new_nodes.push(BreadcrumbTreeNodeElement::Borrowed( From 283430a45bfd0273e74d704b54bf766dd89500ee Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 18:14:26 -0400 Subject: [PATCH 19/67] Merge the breadcrumb tree trait and struct into just a struct. --- src/renderer/breadcrumb_tree.rs | 55 +++++++++++++-------------------- src/renderer/tree_renderer.rs | 21 +++++-------- src/renderer/tree_walking.rs | 7 ++--- 3 files changed, 32 insertions(+), 51 deletions(-) diff --git a/src/renderer/breadcrumb_tree.rs b/src/renderer/breadcrumb_tree.rs index 46184b9..1871e15 100644 --- a/src/renderer/breadcrumb_tree.rs +++ b/src/renderer/breadcrumb_tree.rs @@ -1,33 +1,32 @@ use crate::renderer::context_element::IntoContextElement; use std::borrow::Borrow; -pub trait BreadcrumbTree { - fn get_ice(&self) -> &dyn IntoContextElement; - - fn get_parent(&self) -> Option<&dyn BreadcrumbTree>; +pub struct BreadcrumbTree<'a> { + parent: Option<&'a BreadcrumbTree<'a>>, + element: BreadcrumbTreeElement<'a>, } -pub struct BreadcrumbTreeNode<'a> { - parent: Option<&'a dyn BreadcrumbTree>, - element: BreadcrumbTreeNodeElement<'a>, -} - -pub enum BreadcrumbTreeNodeElement<'a> { +pub enum BreadcrumbTreeElement<'a> { Owned(Box), Borrowed(&'a dyn IntoContextElement), } -impl<'a> BreadcrumbTreeNode<'a> { - pub fn new( - parent: Option<&'a dyn BreadcrumbTree>, - element: BreadcrumbTreeNodeElement<'a>, - ) -> Self { - BreadcrumbTreeNode { +impl<'a> BreadcrumbTree<'a> { + pub fn new(parent: Option<&'a BreadcrumbTree>, element: BreadcrumbTreeElement<'a>) -> Self { + BreadcrumbTree { parent: parent, element: element, } } + pub fn get_ice(&self) -> &dyn IntoContextElement { + self.element.borrow() + } + + pub fn get_parent(&self) -> Option<&BreadcrumbTree> { + self.parent + } + pub fn ice_iter(&'a self) -> impl Iterator { self.breadcrumb_iter().map(|b| b.get_ice()) } @@ -37,27 +36,17 @@ impl<'a> BreadcrumbTreeNode<'a> { } } -impl<'a> Borrow for BreadcrumbTreeNodeElement<'a> { +impl<'a> Borrow for BreadcrumbTreeElement<'a> { fn borrow(&self) -> &(dyn IntoContextElement + 'a) { match self { - BreadcrumbTreeNodeElement::Owned(ice) => ice.as_ref(), - BreadcrumbTreeNodeElement::Borrowed(ice) => *ice, + BreadcrumbTreeElement::Owned(ice) => ice.as_ref(), + BreadcrumbTreeElement::Borrowed(ice) => *ice, } } } -impl<'a> BreadcrumbTree for BreadcrumbTreeNode<'a> { - fn get_ice(&self) -> &dyn IntoContextElement { - self.element.borrow() - } - - fn get_parent(&self) -> Option<&dyn BreadcrumbTree> { - self.parent - } -} - -impl<'a> IntoIterator for &'a BreadcrumbTreeNode<'a> { - type Item = &'a dyn BreadcrumbTree; +impl<'a> IntoIterator for &'a BreadcrumbTree<'a> { + type Item = &'a BreadcrumbTree<'a>; type IntoIter = BreadcrumbTreeIterator<'a>; fn into_iter(self) -> BreadcrumbTreeIterator<'a> { @@ -65,10 +54,10 @@ impl<'a> IntoIterator for &'a BreadcrumbTreeNode<'a> { } } -pub struct BreadcrumbTreeIterator<'a>(Option<&'a dyn BreadcrumbTree>); +pub struct BreadcrumbTreeIterator<'a>(Option<&'a BreadcrumbTree<'a>>); impl<'a> Iterator for BreadcrumbTreeIterator<'a> { - type Item = &'a dyn BreadcrumbTree; + type Item = &'a BreadcrumbTree<'a>; fn next(&mut self) -> Option { let ret = self.0; diff --git a/src/renderer/tree_renderer.rs b/src/renderer/tree_renderer.rs index 11eba11..455dfd9 100644 --- a/src/renderer/tree_renderer.rs +++ b/src/renderer/tree_renderer.rs @@ -2,8 +2,7 @@ use crate::parser::template; use crate::parser::Path; use crate::parser::Template; use crate::renderer::breadcrumb_tree::BreadcrumbTree; -use crate::renderer::breadcrumb_tree::BreadcrumbTreeNode; -use crate::renderer::breadcrumb_tree::BreadcrumbTreeNodeElement; +use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement; use crate::renderer::context_element::ContextElement; use crate::renderer::context_element::IntoContextElement; use crate::renderer::errors::CompileError; @@ -56,15 +55,12 @@ impl<'a> DustRenderer<'a> { /// be empty) should be combined into a final BreadcrumbTreeNode fn new_breadcrumbs_section<'b>( &self, - maybe_breadcrumbs: Option<&'a BreadcrumbTreeNode>, + maybe_breadcrumbs: Option<&'a BreadcrumbTree>, index_context: Option<&'b dyn IntoContextElement>, injected_context: Option<&'b dyn IntoContextElement>, explicit_context: &Option>, new_context_element: Option<&'b dyn ContextElement>, - ) -> Option<( - Option<&'b BreadcrumbTreeNode>, - Vec>, - )> { + ) -> Option<(Option<&'b BreadcrumbTree>, Vec>)> { // If none of the additional contexts are present, return None // to signal that the original breadcrumbs should be used // rather than incurring a copy here. @@ -95,13 +91,10 @@ impl<'a> DustRenderer<'a> { // } // }); // }); - injected_context.map(|ctx| new_nodes.push(BreadcrumbTreeNodeElement::Borrowed(ctx))); - new_context_element.map(|ctx| { - new_nodes.push(BreadcrumbTreeNodeElement::Borrowed( - ctx.from_context_element(), - )) - }); - index_context.map(|ctx| new_nodes.push(BreadcrumbTreeNodeElement::Borrowed(ctx))); + injected_context.map(|ctx| new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx))); + new_context_element + .map(|ctx| new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx.from_context_element()))); + index_context.map(|ctx| new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx))); Some((parent, new_nodes)) } diff --git a/src/renderer/tree_walking.rs b/src/renderer/tree_walking.rs index b68b775..a37c208 100644 --- a/src/renderer/tree_walking.rs +++ b/src/renderer/tree_walking.rs @@ -1,5 +1,4 @@ use crate::renderer::breadcrumb_tree::BreadcrumbTree; -use crate::renderer::breadcrumb_tree::BreadcrumbTreeNode; use crate::renderer::context_element::IntoContextElement; use crate::renderer::WalkError; use std::borrow::Borrow; @@ -39,8 +38,8 @@ where } fn get_first_non_pseudo_element<'a>( - breadcrumbs: &'a BreadcrumbTreeNode, -) -> Option<&'a dyn BreadcrumbTree> { + breadcrumbs: &'a BreadcrumbTree, +) -> Option<&'a BreadcrumbTree<'a>> { breadcrumbs .breadcrumb_iter() .filter(|b| b.get_ice().is_pseudo_element()) @@ -48,7 +47,7 @@ fn get_first_non_pseudo_element<'a>( } pub fn walk_path<'a, P>( - maybe_breadcrumbs: Option<&'a BreadcrumbTreeNode>, + maybe_breadcrumbs: Option<&'a BreadcrumbTree>, path: &Vec

, ) -> Result<&'a dyn IntoContextElement, WalkError> where From acb8dfb58ebeabf54fd67c0eee6999f54886a100 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 18:18:21 -0400 Subject: [PATCH 20/67] Replaced the old renderer file with a new renderer file. --- src/bin.rs | 12 +- src/renderer/mod.rs | 1 - src/renderer/renderer.rs | 988 ++----------------------------- src/renderer/renderer_old.rs | 1021 +++++++++++++++++++++++++++++++++ src/renderer/tree_renderer.rs | 101 ---- 5 files changed, 1061 insertions(+), 1062 deletions(-) create mode 100644 src/renderer/renderer_old.rs delete mode 100644 src/renderer/tree_renderer.rs diff --git a/src/bin.rs b/src/bin.rs index cb0373f..ec05dd1 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -51,12 +51,12 @@ fn main() { .expect("There should be more than 1 template") .name; let breadcrumbs = vec![&context as &dyn IntoContextElement]; - println!( - "{}", - dust_renderer - .render(main_template_name, &breadcrumbs) - .expect("Failed to render") - ); + // println!( + // "{}", + // dust_renderer + // .render(main_template_name, &breadcrumbs) + // .expect("Failed to render") + // ); } fn template_from_file<'a>(file_path: &str, file_contents: &'a str) -> CompiledTemplate<'a> { diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 7debf9a..7eecacb 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -7,7 +7,6 @@ mod inline_partial_tree; mod iteration_context; mod parameters_context; mod renderer; -mod tree_renderer; mod tree_walking; mod walking; diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index adc8dec..455dfd9 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -1,24 +1,12 @@ use crate::parser::template; -use crate::parser::Body; -use crate::parser::DustTag; -use crate::parser::KVPair; -use crate::parser::PartialNameElement; use crate::parser::Path; -use crate::parser::RValue; -use crate::parser::Special; use crate::parser::Template; -use crate::parser::{Filter, TemplateElement}; +use crate::renderer::breadcrumb_tree::BreadcrumbTree; +use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement; use crate::renderer::context_element::ContextElement; use crate::renderer::context_element::IntoContextElement; use crate::renderer::errors::CompileError; -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::iteration_context::IterationContext; -use crate::renderer::parameters_context::ParametersContext; -use crate::renderer::walking::walk_path; -use std::borrow::Borrow; +use crate::renderer::tree_walking::walk_path; use std::collections::HashMap; #[derive(Clone, Debug)] @@ -56,670 +44,23 @@ impl<'a> DustRenderer<'a> { .insert(template.name.clone(), &template.template); } - pub fn render( - &'a self, - name: &str, - breadcrumbs: &Vec<&'a dyn IntoContextElement>, - ) -> Result { - self.render_template(name, breadcrumbs, None) - } - - fn render_template( - &'a self, - name: &str, - breadcrumbs: &Vec<&'a dyn IntoContextElement>, - blocks: Option<&'a InlinePartialTreeElement<'a>>, - ) -> Result { - let main_template = match self.templates.get(name) { - Some(tmpl) => tmpl, - None => { - return Err(RenderError::TemplateNotFound(name.to_owned())); - } - }; - let extracted_inline_partials = extract_inline_partials(main_template); - let new_blocks = InlinePartialTreeElement::new(blocks, extracted_inline_partials); - let new_block_context = BlockContext { - breadcrumbs: breadcrumbs, - blocks: &new_blocks, - }; - self.render_body(&main_template.contents, breadcrumbs, &new_block_context) - } - - fn render_maybe_body( - &'a self, - body: &'a Option, - breadcrumbs: &Vec<&'a dyn IntoContextElement>, - blocks: &'a BlockContext<'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, - breadcrumbs: &Vec<&'a dyn IntoContextElement>, - blocks: &'a BlockContext<'a>, - ) -> Result { - let mut output = String::new(); - for elem in &body.elements { - match elem { - TemplateElement::TEIgnoredWhitespace(_) => {} - TemplateElement::TESpan(span) => output.push_str(span.contents), - TemplateElement::TETag(dt) => { - output.push_str(&self.render_tag(dt, breadcrumbs, blocks)?); - } - } - } - Ok(output) - } - - /// For rendering a dynamic partial's name or an rvalue template - pub fn render_partial_name( - &'a self, - body: &'a Vec, - breadcrumbs: &Vec<&'a dyn IntoContextElement>, - ) -> Result { - let converted_to_template_elements: Vec> = - body.into_iter().map(|e| e.into()).collect(); - // Simple templates like partial names and reference rvalues - // cannot contain blocks or inline partials, so we use a blank - // BlockContext. - let empty_block_context = BlockContext { - breadcrumbs: &Vec::new(), - blocks: &InlinePartialTreeElement::new(None, HashMap::new()), - }; - self.render_body( - &Body { - elements: converted_to_template_elements, - }, - breadcrumbs, - &empty_block_context, - ) - } - - fn render_tag( - &'a self, - tag: &'a DustTag, - breadcrumbs: &Vec<&'a dyn IntoContextElement>, - blocks: &'a BlockContext<'a>, - ) -> Result { - match tag { - DustTag::DTComment(_comment) => (), - DustTag::DTSpecial(special) => { - return Ok(match special { - Special::Space => " ", - Special::NewLine => "\n", - Special::CarriageReturn => "\r", - Special::LeftCurlyBrace => "{", - Special::RightCurlyBrace => "}", - } - .to_owned()) - } - DustTag::DTLiteralStringBlock(literal) => return Ok((*literal).to_owned()), - DustTag::DTReference(reference) => { - let val = walk_path(breadcrumbs, &reference.path.keys) - .map(|ice| ice.into_context_element(self, breadcrumbs)); - match val { - Err(WalkError::CantWalk) => return Ok("".to_owned()), - Ok(final_val) => { - return if final_val.is_truthy() { - final_val.render(&Self::preprocess_filters(&reference.filters)) - } else { - Ok("".to_owned()) - }; - } - } - } - DustTag::DTSection(container) => { - let injected_context = ParametersContext::new(breadcrumbs, &container.params); - let val = walk_path(breadcrumbs, &container.path.keys) - .map(|ice| ice.into_context_element(self, breadcrumbs)); - match val { - Err(WalkError::CantWalk) => { - let new_breadcrumbs = self.new_breadcrumbs_section( - breadcrumbs, - None, - Some(&injected_context), - &container.explicit_context, - None, - ); - return self.render_maybe_body( - &container.else_contents, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - ); - } - Ok(final_val) => { - return if final_val.is_truthy() { - match &container.contents { - // If the body is empty, just shortcut - // to an empty string now rather than - // generating intermediate contexts - // and iterating for nothing. - None => Ok("".to_owned()), - Some(body) => { - let loop_elements: Vec<&dyn ContextElement> = - final_val.get_loop_elements(); - if loop_elements.is_empty() { - // Scalar value - let new_breadcrumbs = self.new_breadcrumbs_section( - breadcrumbs, - None, - Some(&injected_context), - &container.explicit_context, - Some(final_val), - ); - self.render_body( - body, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - ) - } else { - // Array-like value - let total_length = loop_elements.len(); - let rendered_results: Result, RenderError> = - loop_elements - .into_iter() - .enumerate() - .map(|(i, array_elem)| { - let index_context = - IterationContext::new(i, total_length); - let new_breadcrumbs = self - .new_breadcrumbs_section( - breadcrumbs, - Some(&index_context), - Some(&injected_context), - &container.explicit_context, - Some(array_elem), - ); - self.render_body( - &body, - new_breadcrumbs - .as_ref() - .unwrap_or(breadcrumbs), - blocks, - ) - }) - .collect(); - let rendered_slice: &[String] = &rendered_results?; - return Ok(rendered_slice.join("")); - } - } - } - } else { - // 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 - let new_breadcrumbs = self.new_breadcrumbs_section( - breadcrumbs, - None, - Some(&injected_context), - &container.explicit_context, - None, - ); - return self.render_maybe_body( - &container.else_contents, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - ); - }; - } - } - } - DustTag::DTExists(container) => { - let new_breadcrumbs = self.new_breadcrumbs_partial( - breadcrumbs, - breadcrumbs, - None, - &container.explicit_context, - ); - let val = walk_path(breadcrumbs, &container.path.keys) - .map(|ice| ice.into_context_element(self, breadcrumbs)); - return if val.map(|v| v.is_truthy()).unwrap_or(false) { - self.render_maybe_body( - &container.contents, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - ) - } else { - self.render_maybe_body( - &container.else_contents, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - ) - }; - } - DustTag::DTNotExists(container) => { - let new_breadcrumbs = self.new_breadcrumbs_partial( - breadcrumbs, - breadcrumbs, - None, - &container.explicit_context, - ); - let val = walk_path(breadcrumbs, &container.path.keys) - .map(|ice| ice.into_context_element(self, breadcrumbs)); - return if !val.map(|v| v.is_truthy()).unwrap_or(false) { - self.render_maybe_body( - &container.contents, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - ) - } else { - self.render_maybe_body( - &container.else_contents, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - ) - }; - } - DustTag::DTPartial(partial) => { - let partial_name = self.render_partial_name(&partial.name, breadcrumbs)?; - if partial.params.is_empty() { - let new_breadcrumbs = self.new_breadcrumbs_partial( - breadcrumbs, - breadcrumbs, - None, - &partial.explicit_context, - ); - let rendered_content = self.render_template( - &partial_name, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - Some(blocks.blocks), - )?; - return Ok(rendered_content); - } else { - let injected_context = ParametersContext::new(breadcrumbs, &partial.params); - let new_breadcrumbs = self.new_breadcrumbs_partial( - breadcrumbs, - breadcrumbs, - Some(&injected_context), - &partial.explicit_context, - ); - let rendered_content = self.render_template( - &partial_name, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - Some(blocks.blocks), - )?; - return Ok(rendered_content); - } - } - DustTag::DTInlinePartial(_named_block) => { - // Inline partials are blank during rendering (they get injected into blocks) - return Ok("".to_owned()); - } - DustTag::DTBlock(named_block) => { - let new_breadcrumbs = self.new_breadcrumbs_partial( - breadcrumbs, - blocks.breadcrumbs, - None, - &named_block.explicit_context, - ); - return match blocks.blocks.get_block(named_block.path.keys[0]) { - None => self.render_maybe_body( - &named_block.contents, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - ), - Some(inline_partial) => self.render_maybe_body( - inline_partial, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - ), - }; - } - DustTag::DTHelperEquals(parameterized_block) => { - let new_breadcrumbs = self.new_breadcrumbs_partial( - breadcrumbs, - breadcrumbs, - None, - ¶meterized_block.explicit_context, - ); - let param_map: HashMap<&str, &RValue<'a>> = - Self::get_rval_map(¶meterized_block.params); - // Special case: when comparing two RVPaths, if the - // path is equal then dust assumes the values are - // equal (otherwise, non-scalar values are - // automatically not equal) - if Self::are_paths_identical(¶m_map) { - return match ¶meterized_block.contents { - None => Ok("".to_owned()), - Some(body) => { - let rendered_content = self.render_body( - body, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - )?; - Ok(rendered_content) - } - }; - } - - let left_side: Result<&dyn ContextElement, WalkError> = - match Self::get_rval(breadcrumbs, ¶m_map, "key") { - None => return Ok("".to_owned()), - Some(res) => res.map(|ice| ice.into_context_element(self, breadcrumbs)), - }; - let right_side: Result<&dyn ContextElement, WalkError> = - Self::get_rval(breadcrumbs, ¶m_map, "value") - .unwrap_or(Err(WalkError::CantWalk)) - .map(|ice| ice.into_context_element(self, breadcrumbs)); - - if left_side == right_side { - return self.render_maybe_body( - ¶meterized_block.contents, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - ); - } else { - return self.render_maybe_body( - ¶meterized_block.else_contents, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - ); - } - } - DustTag::DTHelperNotEquals(parameterized_block) => { - let new_breadcrumbs = self.new_breadcrumbs_partial( - breadcrumbs, - breadcrumbs, - None, - ¶meterized_block.explicit_context, - ); - let param_map: HashMap<&str, &RValue<'a>> = - Self::get_rval_map(¶meterized_block.params); - // Special case: when comparing two RVPaths, if the - // path is equal then dust assumes the values are - // equal (otherwise, non-scalar values are - // automatically not equal) - if Self::are_paths_identical(¶m_map) { - return match ¶meterized_block.else_contents { - None => Ok("".to_owned()), - Some(body) => { - let rendered_content = self.render_body( - body, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - )?; - Ok(rendered_content) - } - }; - } - - let left_side: Result<&dyn ContextElement, WalkError> = - match Self::get_rval(breadcrumbs, ¶m_map, "key") { - None => return Ok("".to_owned()), - Some(res) => res.map(|ice| ice.into_context_element(self, breadcrumbs)), - }; - let right_side: Result<&dyn ContextElement, WalkError> = - Self::get_rval(breadcrumbs, ¶m_map, "value") - .unwrap_or(Err(WalkError::CantWalk)) - .map(|ice| ice.into_context_element(self, breadcrumbs)); - if left_side != right_side { - return self.render_maybe_body( - ¶meterized_block.contents, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - ); - } else { - return self.render_maybe_body( - ¶meterized_block.else_contents, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - ); - } - } - DustTag::DTHelperGreaterThan(parameterized_block) => { - let new_breadcrumbs = self.new_breadcrumbs_partial( - breadcrumbs, - breadcrumbs, - None, - ¶meterized_block.explicit_context, - ); - 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.map(|ice| ice.into_context_element(self, breadcrumbs)), - }; - let right_side: Result<&dyn ContextElement, WalkError> = - Self::get_rval(breadcrumbs, ¶m_map, "value") - .unwrap_or(Err(WalkError::CantWalk)) - .map(|ice| ice.into_context_element(self, breadcrumbs)); - match (left_side, right_side) { - (Err(_), _) | (_, Err(_)) => { - return self.render_maybe_body( - ¶meterized_block.else_contents, - new_breadcrumbs.as_ref().unwrap_or(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, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - ); - } else { - return self.render_maybe_body( - ¶meterized_block.else_contents, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - ); - } - } - } - } - DustTag::DTHelperGreaterThanOrEquals(parameterized_block) => { - let new_breadcrumbs = self.new_breadcrumbs_partial( - breadcrumbs, - breadcrumbs, - None, - ¶meterized_block.explicit_context, - ); - 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.map(|ice| ice.into_context_element(self, breadcrumbs)), - }; - let right_side: Result<&dyn ContextElement, WalkError> = - Self::get_rval(breadcrumbs, ¶m_map, "value") - .unwrap_or(Err(WalkError::CantWalk)) - .map(|ice| ice.into_context_element(self, breadcrumbs)); - match (left_side, right_side) { - (Err(_), _) | (_, Err(_)) => { - return self.render_maybe_body( - ¶meterized_block.else_contents, - new_breadcrumbs.as_ref().unwrap_or(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, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - ); - } else { - return self.render_maybe_body( - ¶meterized_block.else_contents, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - ); - } - } - } - } - DustTag::DTHelperLessThan(parameterized_block) => { - let new_breadcrumbs = self.new_breadcrumbs_partial( - breadcrumbs, - breadcrumbs, - None, - ¶meterized_block.explicit_context, - ); - 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.map(|ice| ice.into_context_element(self, breadcrumbs)), - }; - let right_side: Result<&dyn ContextElement, WalkError> = - Self::get_rval(breadcrumbs, ¶m_map, "value") - .unwrap_or(Err(WalkError::CantWalk)) - .map(|ice| ice.into_context_element(self, breadcrumbs)); - match (left_side, right_side) { - (Err(_), _) | (_, Err(_)) => { - return self.render_maybe_body( - ¶meterized_block.else_contents, - new_breadcrumbs.as_ref().unwrap_or(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, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - ); - } else { - return self.render_maybe_body( - ¶meterized_block.else_contents, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - ); - } - } - } - } - DustTag::DTHelperLessThanOrEquals(parameterized_block) => { - let new_breadcrumbs = self.new_breadcrumbs_partial( - breadcrumbs, - breadcrumbs, - None, - ¶meterized_block.explicit_context, - ); - 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.map(|ice| ice.into_context_element(self, breadcrumbs)), - }; - let right_side: Result<&dyn ContextElement, WalkError> = - Self::get_rval(breadcrumbs, ¶m_map, "value") - .unwrap_or(Err(WalkError::CantWalk)) - .map(|ice| ice.into_context_element(self, breadcrumbs)); - match (left_side, right_side) { - (Err(_), _) | (_, Err(_)) => { - return self.render_maybe_body( - ¶meterized_block.else_contents, - new_breadcrumbs.as_ref().unwrap_or(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, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - ); - } else { - return self.render_maybe_body( - ¶meterized_block.else_contents, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - ); - } - } - } - } - } - Ok("".to_owned()) - } - - /// Gets the elements to loop over for a section. + /// Returns a option of a tuple of (parent, new_node_elements) + /// which can then be formed into new BreadcrumbTreeNodes /// - /// If the value is falsey, and therefore should render the else - /// block, this will return an empty vector. - fn get_loop_elements<'b>( - 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(), - } - } - - fn are_paths_identical<'b>(param_map: &HashMap<&str, &RValue<'b>>) -> bool { - match (param_map.get("key"), param_map.get("value")) { - (None, _) => false, - (_, None) => false, - (Some(key_rval), Some(value_rval)) => match (key_rval, value_rval) { - (RValue::RVPath(key_path), RValue::RVPath(value_path)) => { - key_path.keys == value_path.keys - } - _ => false, - }, - } - } - - fn get_rval_map<'b>(params: &'b Vec>) -> HashMap<&'b str, &'b RValue<'b>> { - params - .iter() - .map(|pair: &KVPair<'b>| (pair.key, &pair.value)) - .collect() - } - - fn get_rval<'b>( - breadcrumbs: &'b Vec<&'b dyn IntoContextElement>, - param_map: &HashMap<&str, &'b RValue<'b>>, - key: &str, - ) -> Option> { - match param_map.get(key) { - None => None, - Some(rval) => match rval { - RValue::RVLiteral(literal) => Some(Ok(literal)), - RValue::RVTemplate(template) => None, - // TODO: Do I resolve the IntoContextElement here for RVPath? - RValue::RVPath(path) => Some(walk_path(breadcrumbs, &path.keys)), - }, - } - } - - fn preprocess_filters(filters: &Vec) -> Vec { - let mut final_filters: Vec = filters - .into_iter() - .filter(|f| f != &&Filter::DisableHtmlEncode) - .map(|f| f.clone()) - .collect(); - - // If the user has not specified any escaping filter (|s or - // |h), automatically add an html escape filter - if !filters.iter().any(|f| f == &Filter::DisableHtmlEncode) { - final_filters.push(Filter::HtmlEncode); - } - final_filters - } - + /// If None is returned, then it is a signal to simply re-use the + /// existing breadcrumbs. + /// + /// Otherwise, the parent (which may be None, especially for + /// explicit contexts) and the additional node elements (which may + /// be empty) should be combined into a final BreadcrumbTreeNode fn new_breadcrumbs_section<'b>( &self, - breadcrumbs: &'b Vec<&'b dyn IntoContextElement>, + maybe_breadcrumbs: Option<&'a BreadcrumbTree>, index_context: Option<&'b dyn IntoContextElement>, injected_context: Option<&'b dyn IntoContextElement>, explicit_context: &Option>, new_context_element: Option<&'b dyn ContextElement>, - ) -> Option> { + ) -> Option<(Option<&'b BreadcrumbTree>, Vec>)> { // If none of the additional contexts are present, return None // to signal that the original breadcrumbs should be used // rather than incurring a copy here. @@ -732,290 +73,29 @@ impl<'a> DustRenderer<'a> { (None, None, None, None) => return None, _ => (), } - let mut new_stack = match explicit_context { - Some(_) => Vec::with_capacity(4), - None => breadcrumbs.clone(), - }; - explicit_context.as_ref().map(|path| { - walk_path(breadcrumbs, &path.keys) - .map(|ice| ice.into_context_element(self, breadcrumbs)) - .map(|val| { - if val.is_truthy() { - new_stack.push(val.from_context_element()) - } - }); - }); - injected_context.map(|ctx| new_stack.push(ctx)); - new_context_element.map(|ctx| new_stack.push(ctx.from_context_element())); - index_context.map(|ctx| new_stack.push(ctx)); - Some(new_stack) - } - fn new_breadcrumbs_partial<'b>( - &self, - breadcrumbs: &'b Vec<&'b dyn IntoContextElement>, - explicit_context_breadcrumbs: &'b Vec<&'b dyn IntoContextElement>, - injected_context: Option<&'b dyn IntoContextElement>, - explicit_context: &Option>, - ) -> Option> { - // If none of the additional contexts are present, return None - // to signal that the original breadcrumbs should be used - // rather than incurring a copy here. - match (injected_context, explicit_context) { - (None, None) => return None, - _ => (), + // If there is an explicit context, then drop all the current + // context + let mut parent = match explicit_context { + Some(_) => None, + None => maybe_breadcrumbs, }; - let mut new_stack = match explicit_context { - Some(_) => Vec::with_capacity(3), - None => breadcrumbs.clone(), - }; - injected_context.map(|ctx| { - // Special case: when there is no explicit context, the - // injected context gets inserted 1 spot behind the - // current context. Otherwise, the injected context gets - // added after the current context but before the explicit - // context. - match explicit_context { - None => new_stack.insert( - Self::get_index_of_first_non_pseudo_element(&new_stack).unwrap_or(0), - ctx, - ), - _ => new_stack.push(ctx), - } - }); - explicit_context.as_ref().map(|path| { - walk_path(explicit_context_breadcrumbs, &path.keys) - // TODO should resolving the value here use explicit_context_breadcrumbs or breadcrumbs? - .map(|ice| ice.into_context_element(self, explicit_context_breadcrumbs)) - .map(|val| { - if val.is_truthy() { - new_stack.push(val.from_context_element()) - } - }); - }); - Some(new_stack) - } + let mut new_nodes = Vec::new(); - fn get_index_of_first_non_pseudo_element<'b, B>(breadcrumbs: &'b Vec) -> Option - where - B: Borrow, - { - breadcrumbs - .iter() - .rposition(|b| !(*b).borrow().is_pseudo_element()) - } -} - -struct BlockContext<'a> { - /// The breadcrumbs at the time of entering the current partial - breadcrumbs: &'a Vec<&'a dyn IntoContextElement>, - blocks: &'a InlinePartialTreeElement<'a>, -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::parser::Filter; - use crate::renderer::context_element::Loopable; - use crate::renderer::context_element::Renderable; - use crate::renderer::context_element::Truthiness; - use crate::renderer::context_element::Walkable; - use crate::renderer::CompareContextElement; - use std::cmp::Ordering; - - impl ContextElement for String {} - - impl Truthiness for String { - fn is_truthy(&self) -> bool { - !self.is_empty() - } - } - - 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> { - Vec::new() - } - } - - impl Walkable for String { - fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> { - Err(WalkError::CantWalk) - } - } - - impl CompareContextElement for String { - fn equals(&self, other: &dyn ContextElement) -> bool { - match other.to_any().downcast_ref::() { - None => false, - Some(other_string) => self == other_string, - } - } - - fn partial_compare(&self, other: &dyn ContextElement) -> Option { - match other.to_any().downcast_ref::() { - None => None, - Some(other_string) => self.partial_cmp(other_string), - } - } - } - impl ContextElement for u64 {} - - impl Truthiness for u64 { - fn is_truthy(&self) -> bool { - true - } - } - - 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::new() - } - } - - 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 { - match other.to_any().downcast_ref::() { - None => false, - Some(other_num) => self == other_num, - } - } - - fn partial_compare(&self, other: &dyn ContextElement) -> Option { - match other.to_any().downcast_ref::() { - None => None, - Some(other_num) => self.partial_cmp(other_num), - } - } - } - - impl ContextElement for HashMap {} - - impl Truthiness for HashMap { - fn is_truthy(&self) -> bool { - true - } - } - - impl Renderable for HashMap { - fn render(&self, _filters: &Vec) -> Result { - // TODO: handle the filters - Ok("[object Object]".to_owned()) - } - } - - 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 Loopable for HashMap { - fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { - Vec::new() - } - } - - impl CompareContextElement for HashMap { - fn equals(&self, other: &dyn ContextElement) -> bool { - false - } - - fn partial_compare(&self, other: &dyn ContextElement) -> Option { - // TODO: Implement - None - } - } - - #[test] - fn test_walk_path() { - 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() - .collect(); - - assert_eq!( - walk_path(&vec![&context as &dyn ContextElement], &vec!["cat"]) - .unwrap() - .render(&Vec::new()) - .unwrap(), - "kitty".to_owned() - ); - assert_eq!( - walk_path( - &vec![&number_context as &dyn ContextElement], - &vec!["tiger"] - ) - .unwrap() - .render(&Vec::new()) - .unwrap(), - "3".to_owned() - ); - assert_eq!( - walk_path( - &vec![&deep_context as &dyn ContextElement], - &vec!["tiger", "food"] - ) - .unwrap() - .render(&Vec::new()) - .unwrap(), - "people".to_owned() - ); + // explicit_context.as_ref().map(|path| { + // let x = walk_path(maybe_breadcrumbs, &path.keys); + // x.map(|ice| ice.into_context_element(self, breadcrumbs)) + // .map(|val| { + // if val.is_truthy() { + // new_nodes.push(val.from_context_element()) + // } + // }); + // }); + injected_context.map(|ctx| new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx))); + new_context_element + .map(|ctx| new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx.from_context_element()))); + index_context.map(|ctx| new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx))); + + Some((parent, new_nodes)) } } diff --git a/src/renderer/renderer_old.rs b/src/renderer/renderer_old.rs new file mode 100644 index 0000000..adc8dec --- /dev/null +++ b/src/renderer/renderer_old.rs @@ -0,0 +1,1021 @@ +use crate::parser::template; +use crate::parser::Body; +use crate::parser::DustTag; +use crate::parser::KVPair; +use crate::parser::PartialNameElement; +use crate::parser::Path; +use crate::parser::RValue; +use crate::parser::Special; +use crate::parser::Template; +use crate::parser::{Filter, TemplateElement}; +use crate::renderer::context_element::ContextElement; +use crate::renderer::context_element::IntoContextElement; +use crate::renderer::errors::CompileError; +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::iteration_context::IterationContext; +use crate::renderer::parameters_context::ParametersContext; +use crate::renderer::walking::walk_path; +use std::borrow::Borrow; +use std::collections::HashMap; + +#[derive(Clone, Debug)] +pub struct CompiledTemplate<'a> { + template: Template<'a>, + pub name: String, +} + +#[derive(Clone, Debug)] +pub struct DustRenderer<'a> { + templates: HashMap>, +} + +pub fn compile_template<'a>( + source: &'a str, + name: String, +) -> Result, CompileError> { + // TODO: This could use better error management + let (_remaining, parsed_template) = template(source).expect("Failed to compile template"); + Ok(CompiledTemplate { + template: parsed_template, + name: name, + }) +} + +impl<'a> DustRenderer<'a> { + pub fn new() -> DustRenderer<'a> { + DustRenderer { + templates: HashMap::new(), + } + } + + pub fn load_source(&mut self, template: &'a CompiledTemplate) { + self.templates + .insert(template.name.clone(), &template.template); + } + + pub fn render( + &'a self, + name: &str, + breadcrumbs: &Vec<&'a dyn IntoContextElement>, + ) -> Result { + self.render_template(name, breadcrumbs, None) + } + + fn render_template( + &'a self, + name: &str, + breadcrumbs: &Vec<&'a dyn IntoContextElement>, + blocks: Option<&'a InlinePartialTreeElement<'a>>, + ) -> Result { + let main_template = match self.templates.get(name) { + Some(tmpl) => tmpl, + None => { + return Err(RenderError::TemplateNotFound(name.to_owned())); + } + }; + let extracted_inline_partials = extract_inline_partials(main_template); + let new_blocks = InlinePartialTreeElement::new(blocks, extracted_inline_partials); + let new_block_context = BlockContext { + breadcrumbs: breadcrumbs, + blocks: &new_blocks, + }; + self.render_body(&main_template.contents, breadcrumbs, &new_block_context) + } + + fn render_maybe_body( + &'a self, + body: &'a Option, + breadcrumbs: &Vec<&'a dyn IntoContextElement>, + blocks: &'a BlockContext<'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, + breadcrumbs: &Vec<&'a dyn IntoContextElement>, + blocks: &'a BlockContext<'a>, + ) -> Result { + let mut output = String::new(); + for elem in &body.elements { + match elem { + TemplateElement::TEIgnoredWhitespace(_) => {} + TemplateElement::TESpan(span) => output.push_str(span.contents), + TemplateElement::TETag(dt) => { + output.push_str(&self.render_tag(dt, breadcrumbs, blocks)?); + } + } + } + Ok(output) + } + + /// For rendering a dynamic partial's name or an rvalue template + pub fn render_partial_name( + &'a self, + body: &'a Vec, + breadcrumbs: &Vec<&'a dyn IntoContextElement>, + ) -> Result { + let converted_to_template_elements: Vec> = + body.into_iter().map(|e| e.into()).collect(); + // Simple templates like partial names and reference rvalues + // cannot contain blocks or inline partials, so we use a blank + // BlockContext. + let empty_block_context = BlockContext { + breadcrumbs: &Vec::new(), + blocks: &InlinePartialTreeElement::new(None, HashMap::new()), + }; + self.render_body( + &Body { + elements: converted_to_template_elements, + }, + breadcrumbs, + &empty_block_context, + ) + } + + fn render_tag( + &'a self, + tag: &'a DustTag, + breadcrumbs: &Vec<&'a dyn IntoContextElement>, + blocks: &'a BlockContext<'a>, + ) -> Result { + match tag { + DustTag::DTComment(_comment) => (), + DustTag::DTSpecial(special) => { + return Ok(match special { + Special::Space => " ", + Special::NewLine => "\n", + Special::CarriageReturn => "\r", + Special::LeftCurlyBrace => "{", + Special::RightCurlyBrace => "}", + } + .to_owned()) + } + DustTag::DTLiteralStringBlock(literal) => return Ok((*literal).to_owned()), + DustTag::DTReference(reference) => { + let val = walk_path(breadcrumbs, &reference.path.keys) + .map(|ice| ice.into_context_element(self, breadcrumbs)); + match val { + Err(WalkError::CantWalk) => return Ok("".to_owned()), + Ok(final_val) => { + return if final_val.is_truthy() { + final_val.render(&Self::preprocess_filters(&reference.filters)) + } else { + Ok("".to_owned()) + }; + } + } + } + DustTag::DTSection(container) => { + let injected_context = ParametersContext::new(breadcrumbs, &container.params); + let val = walk_path(breadcrumbs, &container.path.keys) + .map(|ice| ice.into_context_element(self, breadcrumbs)); + match val { + Err(WalkError::CantWalk) => { + let new_breadcrumbs = self.new_breadcrumbs_section( + breadcrumbs, + None, + Some(&injected_context), + &container.explicit_context, + None, + ); + return self.render_maybe_body( + &container.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } + Ok(final_val) => { + return if final_val.is_truthy() { + match &container.contents { + // If the body is empty, just shortcut + // to an empty string now rather than + // generating intermediate contexts + // and iterating for nothing. + None => Ok("".to_owned()), + Some(body) => { + let loop_elements: Vec<&dyn ContextElement> = + final_val.get_loop_elements(); + if loop_elements.is_empty() { + // Scalar value + let new_breadcrumbs = self.new_breadcrumbs_section( + breadcrumbs, + None, + Some(&injected_context), + &container.explicit_context, + Some(final_val), + ); + self.render_body( + body, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ) + } else { + // Array-like value + let total_length = loop_elements.len(); + let rendered_results: Result, RenderError> = + loop_elements + .into_iter() + .enumerate() + .map(|(i, array_elem)| { + let index_context = + IterationContext::new(i, total_length); + let new_breadcrumbs = self + .new_breadcrumbs_section( + breadcrumbs, + Some(&index_context), + Some(&injected_context), + &container.explicit_context, + Some(array_elem), + ); + self.render_body( + &body, + new_breadcrumbs + .as_ref() + .unwrap_or(breadcrumbs), + blocks, + ) + }) + .collect(); + let rendered_slice: &[String] = &rendered_results?; + return Ok(rendered_slice.join("")); + } + } + } + } else { + // 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 + let new_breadcrumbs = self.new_breadcrumbs_section( + breadcrumbs, + None, + Some(&injected_context), + &container.explicit_context, + None, + ); + return self.render_maybe_body( + &container.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + }; + } + } + } + DustTag::DTExists(container) => { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + None, + &container.explicit_context, + ); + let val = walk_path(breadcrumbs, &container.path.keys) + .map(|ice| ice.into_context_element(self, breadcrumbs)); + return if val.map(|v| v.is_truthy()).unwrap_or(false) { + self.render_maybe_body( + &container.contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ) + } else { + self.render_maybe_body( + &container.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ) + }; + } + DustTag::DTNotExists(container) => { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + None, + &container.explicit_context, + ); + let val = walk_path(breadcrumbs, &container.path.keys) + .map(|ice| ice.into_context_element(self, breadcrumbs)); + return if !val.map(|v| v.is_truthy()).unwrap_or(false) { + self.render_maybe_body( + &container.contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ) + } else { + self.render_maybe_body( + &container.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ) + }; + } + DustTag::DTPartial(partial) => { + let partial_name = self.render_partial_name(&partial.name, breadcrumbs)?; + if partial.params.is_empty() { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + None, + &partial.explicit_context, + ); + let rendered_content = self.render_template( + &partial_name, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + Some(blocks.blocks), + )?; + return Ok(rendered_content); + } else { + let injected_context = ParametersContext::new(breadcrumbs, &partial.params); + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + Some(&injected_context), + &partial.explicit_context, + ); + let rendered_content = self.render_template( + &partial_name, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + Some(blocks.blocks), + )?; + return Ok(rendered_content); + } + } + DustTag::DTInlinePartial(_named_block) => { + // Inline partials are blank during rendering (they get injected into blocks) + return Ok("".to_owned()); + } + DustTag::DTBlock(named_block) => { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + blocks.breadcrumbs, + None, + &named_block.explicit_context, + ); + return match blocks.blocks.get_block(named_block.path.keys[0]) { + None => self.render_maybe_body( + &named_block.contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ), + Some(inline_partial) => self.render_maybe_body( + inline_partial, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ), + }; + } + DustTag::DTHelperEquals(parameterized_block) => { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + None, + ¶meterized_block.explicit_context, + ); + let param_map: HashMap<&str, &RValue<'a>> = + Self::get_rval_map(¶meterized_block.params); + // Special case: when comparing two RVPaths, if the + // path is equal then dust assumes the values are + // equal (otherwise, non-scalar values are + // automatically not equal) + if Self::are_paths_identical(¶m_map) { + return match ¶meterized_block.contents { + None => Ok("".to_owned()), + Some(body) => { + let rendered_content = self.render_body( + body, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + )?; + Ok(rendered_content) + } + }; + } + + let left_side: Result<&dyn ContextElement, WalkError> = + match Self::get_rval(breadcrumbs, ¶m_map, "key") { + None => return Ok("".to_owned()), + Some(res) => res.map(|ice| ice.into_context_element(self, breadcrumbs)), + }; + let right_side: Result<&dyn ContextElement, WalkError> = + Self::get_rval(breadcrumbs, ¶m_map, "value") + .unwrap_or(Err(WalkError::CantWalk)) + .map(|ice| ice.into_context_element(self, breadcrumbs)); + + if left_side == right_side { + return self.render_maybe_body( + ¶meterized_block.contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } else { + return self.render_maybe_body( + ¶meterized_block.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } + } + DustTag::DTHelperNotEquals(parameterized_block) => { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + None, + ¶meterized_block.explicit_context, + ); + let param_map: HashMap<&str, &RValue<'a>> = + Self::get_rval_map(¶meterized_block.params); + // Special case: when comparing two RVPaths, if the + // path is equal then dust assumes the values are + // equal (otherwise, non-scalar values are + // automatically not equal) + if Self::are_paths_identical(¶m_map) { + return match ¶meterized_block.else_contents { + None => Ok("".to_owned()), + Some(body) => { + let rendered_content = self.render_body( + body, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + )?; + Ok(rendered_content) + } + }; + } + + let left_side: Result<&dyn ContextElement, WalkError> = + match Self::get_rval(breadcrumbs, ¶m_map, "key") { + None => return Ok("".to_owned()), + Some(res) => res.map(|ice| ice.into_context_element(self, breadcrumbs)), + }; + let right_side: Result<&dyn ContextElement, WalkError> = + Self::get_rval(breadcrumbs, ¶m_map, "value") + .unwrap_or(Err(WalkError::CantWalk)) + .map(|ice| ice.into_context_element(self, breadcrumbs)); + if left_side != right_side { + return self.render_maybe_body( + ¶meterized_block.contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } else { + return self.render_maybe_body( + ¶meterized_block.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } + } + DustTag::DTHelperGreaterThan(parameterized_block) => { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + None, + ¶meterized_block.explicit_context, + ); + 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.map(|ice| ice.into_context_element(self, breadcrumbs)), + }; + let right_side: Result<&dyn ContextElement, WalkError> = + Self::get_rval(breadcrumbs, ¶m_map, "value") + .unwrap_or(Err(WalkError::CantWalk)) + .map(|ice| ice.into_context_element(self, breadcrumbs)); + match (left_side, right_side) { + (Err(_), _) | (_, Err(_)) => { + return self.render_maybe_body( + ¶meterized_block.else_contents, + new_breadcrumbs.as_ref().unwrap_or(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, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } else { + return self.render_maybe_body( + ¶meterized_block.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } + } + } + } + DustTag::DTHelperGreaterThanOrEquals(parameterized_block) => { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + None, + ¶meterized_block.explicit_context, + ); + 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.map(|ice| ice.into_context_element(self, breadcrumbs)), + }; + let right_side: Result<&dyn ContextElement, WalkError> = + Self::get_rval(breadcrumbs, ¶m_map, "value") + .unwrap_or(Err(WalkError::CantWalk)) + .map(|ice| ice.into_context_element(self, breadcrumbs)); + match (left_side, right_side) { + (Err(_), _) | (_, Err(_)) => { + return self.render_maybe_body( + ¶meterized_block.else_contents, + new_breadcrumbs.as_ref().unwrap_or(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, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } else { + return self.render_maybe_body( + ¶meterized_block.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } + } + } + } + DustTag::DTHelperLessThan(parameterized_block) => { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + None, + ¶meterized_block.explicit_context, + ); + 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.map(|ice| ice.into_context_element(self, breadcrumbs)), + }; + let right_side: Result<&dyn ContextElement, WalkError> = + Self::get_rval(breadcrumbs, ¶m_map, "value") + .unwrap_or(Err(WalkError::CantWalk)) + .map(|ice| ice.into_context_element(self, breadcrumbs)); + match (left_side, right_side) { + (Err(_), _) | (_, Err(_)) => { + return self.render_maybe_body( + ¶meterized_block.else_contents, + new_breadcrumbs.as_ref().unwrap_or(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, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } else { + return self.render_maybe_body( + ¶meterized_block.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } + } + } + } + DustTag::DTHelperLessThanOrEquals(parameterized_block) => { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + None, + ¶meterized_block.explicit_context, + ); + 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.map(|ice| ice.into_context_element(self, breadcrumbs)), + }; + let right_side: Result<&dyn ContextElement, WalkError> = + Self::get_rval(breadcrumbs, ¶m_map, "value") + .unwrap_or(Err(WalkError::CantWalk)) + .map(|ice| ice.into_context_element(self, breadcrumbs)); + match (left_side, right_side) { + (Err(_), _) | (_, Err(_)) => { + return self.render_maybe_body( + ¶meterized_block.else_contents, + new_breadcrumbs.as_ref().unwrap_or(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, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } else { + return self.render_maybe_body( + ¶meterized_block.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } + } + } + } + } + Ok("".to_owned()) + } + + /// Gets the elements to loop over for a section. + /// + /// If the value is falsey, and therefore should render the else + /// block, this will return an empty vector. + fn get_loop_elements<'b>( + 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(), + } + } + + fn are_paths_identical<'b>(param_map: &HashMap<&str, &RValue<'b>>) -> bool { + match (param_map.get("key"), param_map.get("value")) { + (None, _) => false, + (_, None) => false, + (Some(key_rval), Some(value_rval)) => match (key_rval, value_rval) { + (RValue::RVPath(key_path), RValue::RVPath(value_path)) => { + key_path.keys == value_path.keys + } + _ => false, + }, + } + } + + fn get_rval_map<'b>(params: &'b Vec>) -> HashMap<&'b str, &'b RValue<'b>> { + params + .iter() + .map(|pair: &KVPair<'b>| (pair.key, &pair.value)) + .collect() + } + + fn get_rval<'b>( + breadcrumbs: &'b Vec<&'b dyn IntoContextElement>, + param_map: &HashMap<&str, &'b RValue<'b>>, + key: &str, + ) -> Option> { + match param_map.get(key) { + None => None, + Some(rval) => match rval { + RValue::RVLiteral(literal) => Some(Ok(literal)), + RValue::RVTemplate(template) => None, + // TODO: Do I resolve the IntoContextElement here for RVPath? + RValue::RVPath(path) => Some(walk_path(breadcrumbs, &path.keys)), + }, + } + } + + fn preprocess_filters(filters: &Vec) -> Vec { + let mut final_filters: Vec = filters + .into_iter() + .filter(|f| f != &&Filter::DisableHtmlEncode) + .map(|f| f.clone()) + .collect(); + + // If the user has not specified any escaping filter (|s or + // |h), automatically add an html escape filter + if !filters.iter().any(|f| f == &Filter::DisableHtmlEncode) { + final_filters.push(Filter::HtmlEncode); + } + final_filters + } + + fn new_breadcrumbs_section<'b>( + &self, + breadcrumbs: &'b Vec<&'b dyn IntoContextElement>, + index_context: Option<&'b dyn IntoContextElement>, + injected_context: Option<&'b dyn IntoContextElement>, + explicit_context: &Option>, + new_context_element: Option<&'b dyn ContextElement>, + ) -> Option> { + // If none of the additional contexts are present, return None + // to signal that the original breadcrumbs should be used + // rather than incurring a copy here. + match ( + index_context, + injected_context, + explicit_context, + new_context_element, + ) { + (None, None, None, None) => return None, + _ => (), + } + let mut new_stack = match explicit_context { + Some(_) => Vec::with_capacity(4), + None => breadcrumbs.clone(), + }; + explicit_context.as_ref().map(|path| { + walk_path(breadcrumbs, &path.keys) + .map(|ice| ice.into_context_element(self, breadcrumbs)) + .map(|val| { + if val.is_truthy() { + new_stack.push(val.from_context_element()) + } + }); + }); + injected_context.map(|ctx| new_stack.push(ctx)); + new_context_element.map(|ctx| new_stack.push(ctx.from_context_element())); + index_context.map(|ctx| new_stack.push(ctx)); + Some(new_stack) + } + + fn new_breadcrumbs_partial<'b>( + &self, + breadcrumbs: &'b Vec<&'b dyn IntoContextElement>, + explicit_context_breadcrumbs: &'b Vec<&'b dyn IntoContextElement>, + injected_context: Option<&'b dyn IntoContextElement>, + explicit_context: &Option>, + ) -> Option> { + // If none of the additional contexts are present, return None + // to signal that the original breadcrumbs should be used + // rather than incurring a copy here. + match (injected_context, explicit_context) { + (None, None) => return None, + _ => (), + }; + let mut new_stack = match explicit_context { + Some(_) => Vec::with_capacity(3), + None => breadcrumbs.clone(), + }; + injected_context.map(|ctx| { + // Special case: when there is no explicit context, the + // injected context gets inserted 1 spot behind the + // current context. Otherwise, the injected context gets + // added after the current context but before the explicit + // context. + match explicit_context { + None => new_stack.insert( + Self::get_index_of_first_non_pseudo_element(&new_stack).unwrap_or(0), + ctx, + ), + _ => new_stack.push(ctx), + } + }); + explicit_context.as_ref().map(|path| { + walk_path(explicit_context_breadcrumbs, &path.keys) + // TODO should resolving the value here use explicit_context_breadcrumbs or breadcrumbs? + .map(|ice| ice.into_context_element(self, explicit_context_breadcrumbs)) + .map(|val| { + if val.is_truthy() { + new_stack.push(val.from_context_element()) + } + }); + }); + Some(new_stack) + } + + fn get_index_of_first_non_pseudo_element<'b, B>(breadcrumbs: &'b Vec) -> Option + where + B: Borrow, + { + breadcrumbs + .iter() + .rposition(|b| !(*b).borrow().is_pseudo_element()) + } +} + +struct BlockContext<'a> { + /// The breadcrumbs at the time of entering the current partial + breadcrumbs: &'a Vec<&'a dyn IntoContextElement>, + blocks: &'a InlinePartialTreeElement<'a>, +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::parser::Filter; + use crate::renderer::context_element::Loopable; + use crate::renderer::context_element::Renderable; + use crate::renderer::context_element::Truthiness; + use crate::renderer::context_element::Walkable; + use crate::renderer::CompareContextElement; + use std::cmp::Ordering; + + impl ContextElement for String {} + + impl Truthiness for String { + fn is_truthy(&self) -> bool { + !self.is_empty() + } + } + + 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> { + Vec::new() + } + } + + impl Walkable for String { + fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> { + Err(WalkError::CantWalk) + } + } + + impl CompareContextElement for String { + fn equals(&self, other: &dyn ContextElement) -> bool { + match other.to_any().downcast_ref::() { + None => false, + Some(other_string) => self == other_string, + } + } + + fn partial_compare(&self, other: &dyn ContextElement) -> Option { + match other.to_any().downcast_ref::() { + None => None, + Some(other_string) => self.partial_cmp(other_string), + } + } + } + impl ContextElement for u64 {} + + impl Truthiness for u64 { + fn is_truthy(&self) -> bool { + true + } + } + + 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::new() + } + } + + 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 { + match other.to_any().downcast_ref::() { + None => false, + Some(other_num) => self == other_num, + } + } + + fn partial_compare(&self, other: &dyn ContextElement) -> Option { + match other.to_any().downcast_ref::() { + None => None, + Some(other_num) => self.partial_cmp(other_num), + } + } + } + + impl ContextElement for HashMap {} + + impl Truthiness for HashMap { + fn is_truthy(&self) -> bool { + true + } + } + + impl Renderable for HashMap { + fn render(&self, _filters: &Vec) -> Result { + // TODO: handle the filters + Ok("[object Object]".to_owned()) + } + } + + 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 Loopable for HashMap { + fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { + Vec::new() + } + } + + impl CompareContextElement for HashMap { + fn equals(&self, other: &dyn ContextElement) -> bool { + false + } + + fn partial_compare(&self, other: &dyn ContextElement) -> Option { + // TODO: Implement + None + } + } + + #[test] + fn test_walk_path() { + 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() + .collect(); + + assert_eq!( + walk_path(&vec![&context as &dyn ContextElement], &vec!["cat"]) + .unwrap() + .render(&Vec::new()) + .unwrap(), + "kitty".to_owned() + ); + assert_eq!( + walk_path( + &vec![&number_context as &dyn ContextElement], + &vec!["tiger"] + ) + .unwrap() + .render(&Vec::new()) + .unwrap(), + "3".to_owned() + ); + assert_eq!( + walk_path( + &vec![&deep_context as &dyn ContextElement], + &vec!["tiger", "food"] + ) + .unwrap() + .render(&Vec::new()) + .unwrap(), + "people".to_owned() + ); + } +} diff --git a/src/renderer/tree_renderer.rs b/src/renderer/tree_renderer.rs deleted file mode 100644 index 455dfd9..0000000 --- a/src/renderer/tree_renderer.rs +++ /dev/null @@ -1,101 +0,0 @@ -use crate::parser::template; -use crate::parser::Path; -use crate::parser::Template; -use crate::renderer::breadcrumb_tree::BreadcrumbTree; -use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement; -use crate::renderer::context_element::ContextElement; -use crate::renderer::context_element::IntoContextElement; -use crate::renderer::errors::CompileError; -use crate::renderer::tree_walking::walk_path; -use std::collections::HashMap; - -#[derive(Clone, Debug)] -pub struct CompiledTemplate<'a> { - template: Template<'a>, - pub name: String, -} - -#[derive(Clone, Debug)] -pub struct DustRenderer<'a> { - templates: HashMap>, -} - -pub fn compile_template<'a>( - source: &'a str, - name: String, -) -> Result, CompileError> { - // TODO: This could use better error management - let (_remaining, parsed_template) = template(source).expect("Failed to compile template"); - Ok(CompiledTemplate { - template: parsed_template, - name: name, - }) -} - -impl<'a> DustRenderer<'a> { - pub fn new() -> DustRenderer<'a> { - DustRenderer { - templates: HashMap::new(), - } - } - - pub fn load_source(&mut self, template: &'a CompiledTemplate) { - self.templates - .insert(template.name.clone(), &template.template); - } - - /// Returns a option of a tuple of (parent, new_node_elements) - /// which can then be formed into new BreadcrumbTreeNodes - /// - /// If None is returned, then it is a signal to simply re-use the - /// existing breadcrumbs. - /// - /// Otherwise, the parent (which may be None, especially for - /// explicit contexts) and the additional node elements (which may - /// be empty) should be combined into a final BreadcrumbTreeNode - fn new_breadcrumbs_section<'b>( - &self, - maybe_breadcrumbs: Option<&'a BreadcrumbTree>, - index_context: Option<&'b dyn IntoContextElement>, - injected_context: Option<&'b dyn IntoContextElement>, - explicit_context: &Option>, - new_context_element: Option<&'b dyn ContextElement>, - ) -> Option<(Option<&'b BreadcrumbTree>, Vec>)> { - // If none of the additional contexts are present, return None - // to signal that the original breadcrumbs should be used - // rather than incurring a copy here. - match ( - index_context, - injected_context, - explicit_context, - new_context_element, - ) { - (None, None, None, None) => return None, - _ => (), - } - - // If there is an explicit context, then drop all the current - // context - let mut parent = match explicit_context { - Some(_) => None, - None => maybe_breadcrumbs, - }; - let mut new_nodes = Vec::new(); - - // explicit_context.as_ref().map(|path| { - // let x = walk_path(maybe_breadcrumbs, &path.keys); - // x.map(|ice| ice.into_context_element(self, breadcrumbs)) - // .map(|val| { - // if val.is_truthy() { - // new_nodes.push(val.from_context_element()) - // } - // }); - // }); - injected_context.map(|ctx| new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx))); - new_context_element - .map(|ctx| new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx.from_context_element()))); - index_context.map(|ctx| new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx))); - - Some((parent, new_nodes)) - } -} From b8b4759d451d3504171cb581edb5cf8165d17f96 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 18:27:55 -0400 Subject: [PATCH 21/67] Remove iteration_context, parameters_context, and most of bin.rs. Since I'm changing a pretty core concept of the renderer, I'm going to be rebuilding it piece by piece. In the interest of being able to rapidly change things and check if they are valid through compilation, I need to eliminate most of the old code so I do not have that weighing me down. --- src/bin.rs | 536 ++++++++++++++++---------------- src/renderer/context_element.rs | 5 +- src/renderer/mod.rs | 4 +- src/renderer/renderer.rs | 20 +- 4 files changed, 283 insertions(+), 282 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index ec05dd1..43690e1 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -50,7 +50,7 @@ fn main() { .first() .expect("There should be more than 1 template") .name; - let breadcrumbs = vec![&context as &dyn IntoContextElement]; + // let breadcrumbs = vec![&context as &dyn IntoContextElement]; // println!( // "{}", // dust_renderer @@ -161,288 +161,288 @@ fn encode_uri_component(inp: &str) -> String { output } -fn apply_filter( - json_value: &serde_json::Value, - filter: &Filter, -) -> Result { - match (json_value, filter) { - // Html escape filter - (serde_json::Value::String(string), Filter::HtmlEncode) => { - Ok(serde_json::Value::String(html_escape(string))) - } - (_, Filter::HtmlEncode) => Ok(serde_json::Value::String(html_escape( - &json_value.render(&Vec::new())?, - ))), - // Disable html escape filter - (_, Filter::DisableHtmlEncode) => panic!("The |s filter is automatically removed by the renderer since it is a no-op during rendering."), - // Parse JSON filter - (serde_json::Value::String(string), Filter::JsonParse) => { - serde_json::from_str(&string).or(Err(RenderError::InvalidJson(string.to_owned()))) - } - (_, Filter::JsonParse) => { - let rendered_value = json_value.render(&Vec::new())?; - serde_json::from_str(&rendered_value).or(Err(RenderError::InvalidJson(rendered_value))) - } - // Json Stringify filter - (_, Filter::JsonStringify) => { - Ok(serde_json::Value::String(json_value.to_string())) - } - // Javascript escape filter - (serde_json::Value::String(string), Filter::JavascriptStringEncode) => { - Ok(serde_json::Value::String(javascript_escape(string))) - } - (serde_json::Value::Bool(boolean), Filter::JavascriptStringEncode) => { - Ok(serde_json::Value::Bool(*boolean)) - } - (serde_json::Value::Number(number), Filter::JavascriptStringEncode) => { - Ok(serde_json::Value::Number(number.clone())) - } - (serde_json::Value::Array(arr), Filter::JavascriptStringEncode) => { - Ok(serde_json::Value::Array(arr.clone())) - } - (serde_json::Value::Object(obj), Filter::JavascriptStringEncode) => { - Ok(serde_json::Value::Object(obj.clone())) - } - (_, Filter::JavascriptStringEncode) => Ok(serde_json::Value::String(javascript_escape( - &json_value.render(&Vec::new())?, - ))), - // EncodeURI filter - (serde_json::Value::String(string), Filter::EncodeUri) => { - Ok(serde_json::Value::String(encode_uri(string))) - } - (_, Filter::EncodeUri) => Ok(serde_json::Value::String(encode_uri( - &json_value.render(&Vec::new())?, - ))), - // EncodeURIComponent filter - (serde_json::Value::String(string), Filter::EncodeUriComponent) => { - Ok(serde_json::Value::String(encode_uri_component(string))) - } - (_, Filter::EncodeUriComponent) => Ok(serde_json::Value::String(encode_uri_component( - &json_value.render(&Vec::new())?, - ))), - } -} +// fn apply_filter( +// json_value: &serde_json::Value, +// filter: &Filter, +// ) -> Result { +// match (json_value, filter) { +// // Html escape filter +// (serde_json::Value::String(string), Filter::HtmlEncode) => { +// Ok(serde_json::Value::String(html_escape(string))) +// } +// (_, Filter::HtmlEncode) => Ok(serde_json::Value::String(html_escape( +// &json_value.render(&Vec::new())?, +// ))), +// // Disable html escape filter +// (_, Filter::DisableHtmlEncode) => panic!("The |s filter is automatically removed by the renderer since it is a no-op during rendering."), +// // Parse JSON filter +// (serde_json::Value::String(string), Filter::JsonParse) => { +// serde_json::from_str(&string).or(Err(RenderError::InvalidJson(string.to_owned()))) +// } +// (_, Filter::JsonParse) => { +// let rendered_value = json_value.render(&Vec::new())?; +// serde_json::from_str(&rendered_value).or(Err(RenderError::InvalidJson(rendered_value))) +// } +// // Json Stringify filter +// (_, Filter::JsonStringify) => { +// Ok(serde_json::Value::String(json_value.to_string())) +// } +// // Javascript escape filter +// (serde_json::Value::String(string), Filter::JavascriptStringEncode) => { +// Ok(serde_json::Value::String(javascript_escape(string))) +// } +// (serde_json::Value::Bool(boolean), Filter::JavascriptStringEncode) => { +// Ok(serde_json::Value::Bool(*boolean)) +// } +// (serde_json::Value::Number(number), Filter::JavascriptStringEncode) => { +// Ok(serde_json::Value::Number(number.clone())) +// } +// (serde_json::Value::Array(arr), Filter::JavascriptStringEncode) => { +// Ok(serde_json::Value::Array(arr.clone())) +// } +// (serde_json::Value::Object(obj), Filter::JavascriptStringEncode) => { +// Ok(serde_json::Value::Object(obj.clone())) +// } +// (_, Filter::JavascriptStringEncode) => Ok(serde_json::Value::String(javascript_escape( +// &json_value.render(&Vec::new())?, +// ))), +// // EncodeURI filter +// (serde_json::Value::String(string), Filter::EncodeUri) => { +// Ok(serde_json::Value::String(encode_uri(string))) +// } +// (_, Filter::EncodeUri) => Ok(serde_json::Value::String(encode_uri( +// &json_value.render(&Vec::new())?, +// ))), +// // EncodeURIComponent filter +// (serde_json::Value::String(string), Filter::EncodeUriComponent) => { +// Ok(serde_json::Value::String(encode_uri_component(string))) +// } +// (_, Filter::EncodeUriComponent) => Ok(serde_json::Value::String(encode_uri_component( +// &json_value.render(&Vec::new())?, +// ))), +// } +// } -fn apply_filters( - json_value: &serde_json::Value, - filters: &[Filter], -) -> Result { - let mut final_value: serde_json::Value = apply_filter(json_value, &filters[0])?; +// fn apply_filters( +// json_value: &serde_json::Value, +// filters: &[Filter], +// ) -> Result { +// let mut final_value: serde_json::Value = apply_filter(json_value, &filters[0])?; - for filter in &filters[1..] { - final_value = apply_filter(&final_value, filter)?; - } +// for filter in &filters[1..] { +// final_value = apply_filter(&final_value, filter)?; +// } - Ok(final_value) -} +// Ok(final_value) +// } -impl ContextElement for serde_json::Value {} +// impl ContextElement for serde_json::Value {} -impl Truthiness for serde_json::Value { - fn is_truthy(&self) -> bool { - match self { - serde_json::Value::Null => false, - serde_json::Value::Bool(boolean) => *boolean, - serde_json::Value::Number(_num) => true, - serde_json::Value::String(string_value) => !string_value.is_empty(), - serde_json::Value::Array(array_value) => !array_value.is_empty(), - serde_json::Value::Object(_obj) => true, - } - } -} +// impl Truthiness for serde_json::Value { +// fn is_truthy(&self) -> bool { +// match self { +// serde_json::Value::Null => false, +// serde_json::Value::Bool(boolean) => *boolean, +// serde_json::Value::Number(_num) => true, +// serde_json::Value::String(string_value) => !string_value.is_empty(), +// serde_json::Value::Array(array_value) => !array_value.is_empty(), +// serde_json::Value::Object(_obj) => true, +// } +// } +// } -impl Renderable for serde_json::Value { - fn render(&self, _filters: &Vec) -> Result { - let after_apply = if _filters.is_empty() { - None - } else { - Some(apply_filters(self, _filters)?) - }; +// impl Renderable for serde_json::Value { +// fn render(&self, _filters: &Vec) -> Result { +// let after_apply = if _filters.is_empty() { +// None +// } else { +// Some(apply_filters(self, _filters)?) +// }; - match after_apply.as_ref().unwrap_or(self) { - serde_json::Value::Null => Ok("".to_owned()), - serde_json::Value::Bool(boolean) => Ok(boolean.to_string()), - serde_json::Value::Number(num) => Ok(num.to_string()), - serde_json::Value::String(string) => Ok(string.to_string()), - serde_json::Value::Array(arr) => { - // TODO: Handle the filters instead of passing a Vec::new() - let rendered: Result, RenderError> = - arr.iter().map(|val| val.render(&Vec::new())).collect(); - let rendered_slice: &[String] = &rendered?; - Ok(rendered_slice.join(",")) - } - serde_json::Value::Object(_obj) => Ok("[object Object]".to_owned()), - } - } -} +// match after_apply.as_ref().unwrap_or(self) { +// serde_json::Value::Null => Ok("".to_owned()), +// serde_json::Value::Bool(boolean) => Ok(boolean.to_string()), +// serde_json::Value::Number(num) => Ok(num.to_string()), +// serde_json::Value::String(string) => Ok(string.to_string()), +// serde_json::Value::Array(arr) => { +// // TODO: Handle the filters instead of passing a Vec::new() +// let rendered: Result, RenderError> = +// arr.iter().map(|val| val.render(&Vec::new())).collect(); +// let rendered_slice: &[String] = &rendered?; +// Ok(rendered_slice.join(",")) +// } +// serde_json::Value::Object(_obj) => Ok("[object Object]".to_owned()), +// } +// } +// } -impl Walkable for serde_json::Value { - fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { - match 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), - } - } -} +// impl Walkable for serde_json::Value { +// fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { +// match 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), +// } +// } +// } -impl Loopable for serde_json::Value { - fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { - match self { - serde_json::Value::Array(array_value) => array_value.iter().map(|x| x as _).collect(), - _ => Vec::new(), - } - } -} +// impl Loopable for serde_json::Value { +// fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { +// match self { +// serde_json::Value::Array(array_value) => array_value.iter().map(|x| x as _).collect(), +// _ => Vec::new(), +// } +// } +// } -impl CompareContextElement for serde_json::Value { - fn equals(&self, other: &dyn ContextElement) -> bool { - // println!("equals json {:?} | {:?}", self, other); - // Handle other serde_json::Value - match other.to_any().downcast_ref::() { - None => (), - Some(other_json_value) => match (self, other_json_value) { - // Non-scalar values not caught in the renderer by the - // identical-path shortcut are always not equal. - (serde_json::Value::Array(_), _) - | (_, serde_json::Value::Array(_)) - | (serde_json::Value::Object(_), _) - | (_, serde_json::Value::Object(_)) => return false, - _ => return self == other_json_value, - }, - } - // Handle literals - match other.to_any().downcast_ref::() { - None => (), - Some(OwnedLiteral::LString(other_string)) => { - return self.as_str().map_or(false, |s| s == other_string) - } - Some(OwnedLiteral::LPositiveInteger(other_num)) => { - return self.as_u64().map_or(false, |n| n == *other_num) - } - } - false - } +// impl CompareContextElement for serde_json::Value { +// fn equals(&self, other: &dyn ContextElement) -> bool { +// // println!("equals json {:?} | {:?}", self, other); +// // Handle other serde_json::Value +// match other.to_any().downcast_ref::() { +// None => (), +// Some(other_json_value) => match (self, other_json_value) { +// // Non-scalar values not caught in the renderer by the +// // identical-path shortcut are always not equal. +// (serde_json::Value::Array(_), _) +// | (_, serde_json::Value::Array(_)) +// | (serde_json::Value::Object(_), _) +// | (_, serde_json::Value::Object(_)) => return false, +// _ => return self == other_json_value, +// }, +// } +// // Handle literals +// match other.to_any().downcast_ref::() { +// None => (), +// Some(OwnedLiteral::LString(other_string)) => { +// return self.as_str().map_or(false, |s| s == other_string) +// } +// Some(OwnedLiteral::LPositiveInteger(other_num)) => { +// return self.as_u64().map_or(false, |n| n == *other_num) +// } +// } +// false +// } - fn partial_compare(&self, other: &dyn ContextElement) -> Option { - // println!("partial_compare json {:?} | {:?}", self, other); - // Handle type coerced objects +// fn partial_compare(&self, other: &dyn ContextElement) -> Option { +// // println!("partial_compare json {:?} | {:?}", self, other); +// // Handle type coerced objects - // When doing a greater than or less than comparison, - // javascript coerces objects into "[object Object]". - if let serde_json::Value::Object(_) = self { - return OwnedLiteral::LString(self.render(&Vec::new()).unwrap_or("".to_owned())) - .partial_compare(other); - } +// // When doing a greater than or less than comparison, +// // javascript coerces objects into "[object Object]". +// if let serde_json::Value::Object(_) = self { +// return OwnedLiteral::LString(self.render(&Vec::new()).unwrap_or("".to_owned())) +// .partial_compare(other); +// } - // When doing a greater than or less than comparison - // javascript turns arrays into strings. - if let serde_json::Value::Array(_) = self { - return OwnedLiteral::LString(self.render(&Vec::new()).unwrap_or("".to_owned())) - .partial_compare(other); - } +// // When doing a greater than or less than comparison +// // javascript turns arrays into strings. +// if let serde_json::Value::Array(_) = self { +// return OwnedLiteral::LString(self.render(&Vec::new()).unwrap_or("".to_owned())) +// .partial_compare(other); +// } - // Handle other serde_json::Value - match other.to_any().downcast_ref::() { - None => (), - Some(other_json_value) => { - return match (self, other_json_value) { - ( - serde_json::Value::Bool(self_boolean), - serde_json::Value::Bool(other_boolean), - ) => self_boolean.partial_cmp(other_boolean), - ( - serde_json::Value::Number(self_number), - serde_json::Value::Number(other_number), - ) => return compare_json_numbers(self_number, other_number), - ( - serde_json::Value::String(self_string), - serde_json::Value::Number(other_number), - ) => return compare_json_numbers(self_string, other_number), - ( - serde_json::Value::Number(self_number), - serde_json::Value::String(other_string), - ) => return compare_json_numbers(self_number, other_string), - ( - serde_json::Value::String(self_string), - serde_json::Value::String(other_string), - ) => self_string.partial_cmp(other_string), - ( - serde_json::Value::Array(self_array), - serde_json::Value::Array(other_array), - ) => { - return self - .render(&Vec::new()) - .unwrap_or("".to_owned()) - .partial_cmp( - &other_json_value - .render(&Vec::new()) - .unwrap_or("".to_owned()), - ) - } - _ => None, - }; - } - } - // Handle literals - match other.to_any().downcast_ref::() { - None => (), - Some(other_literal) => match (self, other_literal) { - (serde_json::Value::String(self_string), OwnedLiteral::LString(other_string)) => { - return self_string.partial_cmp(other_string) - } - ( - serde_json::Value::String(self_string), - OwnedLiteral::LPositiveInteger(other_num), - ) => return compare_json_numbers(self_string, other_literal), - (serde_json::Value::Number(self_num), OwnedLiteral::LString(other_string)) => { - return compare_json_numbers(self_num, other_string) - } - ( - serde_json::Value::Number(self_num), - OwnedLiteral::LPositiveInteger(other_num), - ) => return compare_json_numbers(self_num, other_literal), - (serde_json::Value::Array(_), _) => { - // TODO - todo!() - } - (serde_json::Value::Object(_), _) => { - // TODO - todo!() - } - (serde_json::Value::Bool(_), _) => { - // TODO - todo!() - } - (serde_json::Value::Null, _) => { - // TODO - todo!() - } - }, - } - None - } -} +// // Handle other serde_json::Value +// match other.to_any().downcast_ref::() { +// None => (), +// Some(other_json_value) => { +// return match (self, other_json_value) { +// ( +// serde_json::Value::Bool(self_boolean), +// serde_json::Value::Bool(other_boolean), +// ) => self_boolean.partial_cmp(other_boolean), +// ( +// serde_json::Value::Number(self_number), +// serde_json::Value::Number(other_number), +// ) => return compare_json_numbers(self_number, other_number), +// ( +// serde_json::Value::String(self_string), +// serde_json::Value::Number(other_number), +// ) => return compare_json_numbers(self_string, other_number), +// ( +// serde_json::Value::Number(self_number), +// serde_json::Value::String(other_string), +// ) => return compare_json_numbers(self_number, other_string), +// ( +// serde_json::Value::String(self_string), +// serde_json::Value::String(other_string), +// ) => self_string.partial_cmp(other_string), +// ( +// serde_json::Value::Array(self_array), +// serde_json::Value::Array(other_array), +// ) => { +// return self +// .render(&Vec::new()) +// .unwrap_or("".to_owned()) +// .partial_cmp( +// &other_json_value +// .render(&Vec::new()) +// .unwrap_or("".to_owned()), +// ) +// } +// _ => None, +// }; +// } +// } +// // Handle literals +// match other.to_any().downcast_ref::() { +// None => (), +// Some(other_literal) => match (self, other_literal) { +// (serde_json::Value::String(self_string), OwnedLiteral::LString(other_string)) => { +// return self_string.partial_cmp(other_string) +// } +// ( +// serde_json::Value::String(self_string), +// OwnedLiteral::LPositiveInteger(other_num), +// ) => return compare_json_numbers(self_string, other_literal), +// (serde_json::Value::Number(self_num), OwnedLiteral::LString(other_string)) => { +// return compare_json_numbers(self_num, other_string) +// } +// ( +// serde_json::Value::Number(self_num), +// OwnedLiteral::LPositiveInteger(other_num), +// ) => return compare_json_numbers(self_num, other_literal), +// (serde_json::Value::Array(_), _) => { +// // TODO +// todo!() +// } +// (serde_json::Value::Object(_), _) => { +// // TODO +// todo!() +// } +// (serde_json::Value::Bool(_), _) => { +// // TODO +// todo!() +// } +// (serde_json::Value::Null, _) => { +// // TODO +// todo!() +// } +// }, +// } +// 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() -} +// /// 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() +// } #[derive(Debug)] enum JsonNumber { diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index c31ecc9..68008e0 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -1,4 +1,5 @@ use crate::parser::Filter; +use crate::renderer::breadcrumb_tree::BreadcrumbTree; use crate::renderer::errors::RenderError; use crate::renderer::errors::WalkError; use crate::renderer::DustRenderer; @@ -103,7 +104,7 @@ pub trait IntoContextElement: Debug + Walkable + CloneIntoBoxedContextElement { fn into_context_element( &self, renderer: &DustRenderer, - breadcrumbs: &Vec<&dyn IntoContextElement>, + breadcrumbs: Option<&BreadcrumbTree>, ) -> &dyn ContextElement; } @@ -111,7 +112,7 @@ impl IntoContextElement for C { fn into_context_element( &self, renderer: &DustRenderer, - breadcrumbs: &Vec<&dyn IntoContextElement>, + breadcrumbs: Option<&BreadcrumbTree>, ) -> &dyn ContextElement { self } diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 7eecacb..a168830 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -4,8 +4,8 @@ mod breadcrumb_tree; mod context_element; mod errors; mod inline_partial_tree; -mod iteration_context; -mod parameters_context; +// mod iteration_context; +// mod parameters_context; mod renderer; mod tree_walking; mod walking; diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 455dfd9..75d45b2 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -80,17 +80,17 @@ impl<'a> DustRenderer<'a> { Some(_) => None, None => maybe_breadcrumbs, }; - let mut new_nodes = Vec::new(); + let mut new_nodes: Vec = Vec::new(); - // explicit_context.as_ref().map(|path| { - // let x = walk_path(maybe_breadcrumbs, &path.keys); - // x.map(|ice| ice.into_context_element(self, breadcrumbs)) - // .map(|val| { - // if val.is_truthy() { - // new_nodes.push(val.from_context_element()) - // } - // }); - // }); + explicit_context.as_ref().map(|path| { + let x = walk_path(maybe_breadcrumbs, &path.keys); + x.map(|ice| ice.into_context_element(self, maybe_breadcrumbs)) + .map(|val| { + if val.is_truthy() { + new_nodes.push(BreadcrumbTreeElement::Borrowed(val.from_context_element())) + } + }); + }); injected_context.map(|ctx| new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx))); new_context_element .map(|ctx| new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx.from_context_element()))); From 15c8e3bf288be7234768d528228aef3ebcb74cec Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 18:31:49 -0400 Subject: [PATCH 22/67] Get rid of the CloneIntoBoxedContextElement trait for now because I don't know if its still going to be necessary with this overhaul. --- src/renderer/context_element.rs | 20 ++++++++++---------- src/renderer/mod.rs | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index 68008e0..f69fcd3 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -12,7 +12,7 @@ pub trait ContextElement: + Walkable + Renderable + Loopable - + CloneIntoBoxedContextElement + // + CloneIntoBoxedContextElement + CompareContextElement + FromContextElement { @@ -62,15 +62,15 @@ pub trait CompareContextElement: CastToAny { fn partial_compare(&self, other: &dyn ContextElement) -> Option; } -pub trait CloneIntoBoxedContextElement { - fn clone_to_box(&self) -> Box; -} +// pub trait CloneIntoBoxedContextElement { +// fn clone_to_box(&self) -> Box; +// } -impl CloneIntoBoxedContextElement for C { - fn clone_to_box(&self) -> Box { - Box::new(self.clone()) - } -} +// impl CloneIntoBoxedContextElement for C { +// fn clone_to_box(&self) -> Box { +// Box::new(self.clone()) +// } +// } impl CastToAny for C { fn to_any(&self) -> &dyn Any { @@ -100,7 +100,7 @@ impl FromContextElement for C { } } -pub trait IntoContextElement: Debug + Walkable + CloneIntoBoxedContextElement { +pub trait IntoContextElement: Debug + Walkable /* + CloneIntoBoxedContextElement*/ { fn into_context_element( &self, renderer: &DustRenderer, diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index a168830..799cba4 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -10,7 +10,7 @@ mod renderer; mod tree_walking; mod walking; -pub use context_element::CloneIntoBoxedContextElement; +// pub use context_element::CloneIntoBoxedContextElement; pub use context_element::CompareContextElement; pub use context_element::ContextElement; pub use context_element::IntoContextElement; From f1ec0ffb9eea19df68e276f895e77c753c26f7bb Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 19:01:51 -0400 Subject: [PATCH 23/67] Got rid of most of the Clone traits on the parser types since some of the parser results now contain owned values rather than just references. --- src/bin.rs | 42 ++++++++++++++++++++++++---------------- src/parser/parser.rs | 30 ++++++++++++++-------------- src/renderer/mod.rs | 2 +- src/renderer/renderer.rs | 26 +++++++------------------ 4 files changed, 48 insertions(+), 52 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index 43690e1..845a3ae 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -3,8 +3,9 @@ extern crate nom; use crate::renderer::CompareContextElement; use parser::Filter; use parser::OwnedLiteral; +use parser::Template; use renderer::compile_template; -use renderer::CompiledTemplate; +use renderer::CompileError; use renderer::ContextElement; use renderer::DustRenderer; use renderer::IntoContextElement; @@ -38,18 +39,18 @@ fn main() { (p.to_string(), template_content) }) .collect(); - let compiled_templates: Vec = template_contents - .iter() - .map(|(p, contents)| template_from_file(p, contents)) - .collect(); - let mut dust_renderer = DustRenderer::new(); - compiled_templates.iter().for_each(|template| { - dust_renderer.load_source(template); - }); - let main_template_name = &compiled_templates - .first() - .expect("There should be more than 1 template") - .name; + // let compiled_templates: Vec = template_contents + // .iter() + // .map(|(p, contents)| template_from_file(p, contents)) + // .collect(); + // let mut dust_renderer = DustRenderer::new(); + // compiled_templates.iter().for_each(|template| { + // dust_renderer.load_source(template); + // }); + // let main_template_name = &compiled_templates + // .first() + // .expect("There should be more than 1 template") + // .name; // let breadcrumbs = vec![&context as &dyn IntoContextElement]; // println!( // "{}", @@ -59,11 +60,18 @@ fn main() { // ); } -fn template_from_file<'a>(file_path: &str, file_contents: &'a str) -> CompiledTemplate<'a> { +fn template_from_file<'a>( + file_path: &str, + file_contents: &'a str, +) -> Result<(String, Template<'a>), CompileError> { let path: &Path = Path::new(file_path); - let name = path.file_stem().unwrap(); - compile_template(file_contents, name.to_string_lossy().to_string()) - .expect("Failed to compile template") + let name = path.file_stem().ok_or(CompileError { + message: format!("Failed to get file stem on {}", file_path), + })?; + Ok(( + name.to_string_lossy().to_string(), + compile_template(file_contents)?, + )) } fn read_context_from_stdin() -> serde_json::Value { diff --git a/src/parser/parser.rs b/src/parser/parser.rs index fa42cb7..8598f9f 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -23,7 +23,7 @@ use nom::sequence::terminated; use nom::sequence::tuple; use nom::IResult; -#[derive(Clone, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub enum DustTag<'a> { DTSpecial(Special), DTComment(Comment<'a>), @@ -52,12 +52,12 @@ pub enum Special { RightCurlyBrace, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub enum IgnoredWhitespace<'a> { StartOfLine(&'a str), } -#[derive(Clone, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub struct Comment<'a> { value: &'a str, } @@ -65,12 +65,12 @@ pub struct Comment<'a> { /// A series of keys separated by '.' to reference a variable in the context /// /// Special case: If the path is just "." then keys will be an empty vec -#[derive(Clone, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub struct Path<'a> { pub keys: Vec<&'a str>, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub struct Reference<'a> { pub path: Path<'a>, pub filters: Vec, @@ -87,12 +87,12 @@ pub enum Filter { JsonParse, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub struct Span<'a> { pub contents: &'a str, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub struct ParameterizedBlock<'a> { pub path: Path<'a>, pub explicit_context: Option>, @@ -101,33 +101,33 @@ pub struct ParameterizedBlock<'a> { pub else_contents: Option>, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub struct Partial<'a> { pub name: Vec, pub explicit_context: Option>, pub params: Vec>, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub enum OwnedLiteral { LString(String), LPositiveInteger(u64), } -#[derive(Clone, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub enum RValue<'a> { RVPath(Path<'a>), RVTemplate(Vec), RVLiteral(OwnedLiteral), } -#[derive(Clone, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub struct KVPair<'a> { pub key: &'a str, pub value: RValue<'a>, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub enum PartialNameElement { PNSpan { contents: String, @@ -138,17 +138,17 @@ pub enum PartialNameElement { }, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub struct Body<'a> { pub elements: Vec>, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub struct Template<'a> { pub contents: Body<'a>, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub enum TemplateElement<'a> { TESpan(Span<'a>), TETag(DustTag<'a>), diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 799cba4..2ebbc51 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -22,5 +22,5 @@ pub use errors::CompileError; pub use errors::RenderError; pub use errors::WalkError; pub use renderer::compile_template; -pub use renderer::CompiledTemplate; +// pub use renderer::CompiledTemplate; pub use renderer::DustRenderer; diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 75d45b2..c8e1d67 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -9,27 +9,16 @@ use crate::renderer::errors::CompileError; use crate::renderer::tree_walking::walk_path; use std::collections::HashMap; -#[derive(Clone, Debug)] -pub struct CompiledTemplate<'a> { - template: Template<'a>, - pub name: String, -} - #[derive(Clone, Debug)] pub struct DustRenderer<'a> { templates: HashMap>, } -pub fn compile_template<'a>( - source: &'a str, - name: String, -) -> Result, CompileError> { - // TODO: This could use better error management - let (_remaining, parsed_template) = template(source).expect("Failed to compile template"); - Ok(CompiledTemplate { - template: parsed_template, - name: name, - }) +pub fn compile_template<'a>(source: &'a str) -> Result, CompileError> { + let (_remaining, parsed_template) = template(source).map_err(|err| CompileError { + message: "Failed to compile template".to_owned(), + })?; + Ok(parsed_template) } impl<'a> DustRenderer<'a> { @@ -39,9 +28,8 @@ impl<'a> DustRenderer<'a> { } } - pub fn load_source(&mut self, template: &'a CompiledTemplate) { - self.templates - .insert(template.name.clone(), &template.template); + pub fn load_source(&mut self, template: &'a Template, name: String) { + self.templates.insert(name, template); } /// Returns a option of a tuple of (parent, new_node_elements) From 6bde22b667a1e2149ad89312048db6ccd56d0ce0 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 19:08:10 -0400 Subject: [PATCH 24/67] Re-enabled compiling templates from bin.rs. --- src/bin.rs | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index 845a3ae..84fe456 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -39,18 +39,20 @@ fn main() { (p.to_string(), template_content) }) .collect(); - // let compiled_templates: Vec = template_contents - // .iter() - // .map(|(p, contents)| template_from_file(p, contents)) - // .collect(); - // let mut dust_renderer = DustRenderer::new(); - // compiled_templates.iter().for_each(|template| { - // dust_renderer.load_source(template); - // }); - // let main_template_name = &compiled_templates - // .first() - // .expect("There should be more than 1 template") - // .name; + let compiled_templates_result: Result, CompileError> = + template_contents + .iter() + .map(|(p, contents)| template_from_file(p, contents)) + .collect(); + let compiled_templates = compiled_templates_result.unwrap(); + let main_template_name = &compiled_templates + .first() + .expect("There should be more than 1 template") + .0; + let mut dust_renderer = DustRenderer::new(); + compiled_templates.iter().for_each(|(name, template)| { + dust_renderer.load_source(template, name.to_owned()); + }); // let breadcrumbs = vec![&context as &dyn IntoContextElement]; // println!( // "{}", From 2b8894f9c27bb863e7435f0152c35d25ad0b9c00 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 20:41:27 -0400 Subject: [PATCH 25/67] Most of new_breadcrumbs_partial, just need to get the return working. --- src/renderer/breadcrumb_tree.rs | 20 +++++++- src/renderer/renderer.rs | 83 ++++++++++++++++++++++++++++++++- 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/src/renderer/breadcrumb_tree.rs b/src/renderer/breadcrumb_tree.rs index 1871e15..c22079f 100644 --- a/src/renderer/breadcrumb_tree.rs +++ b/src/renderer/breadcrumb_tree.rs @@ -1,13 +1,19 @@ use crate::renderer::context_element::IntoContextElement; use std::borrow::Borrow; +use std::rc::Rc; pub struct BreadcrumbTree<'a> { parent: Option<&'a BreadcrumbTree<'a>>, element: BreadcrumbTreeElement<'a>, } +#[derive(Clone, Debug)] pub enum BreadcrumbTreeElement<'a> { - Owned(Box), + // Using Rc so that when we need to create BreadcrumbTrees with + // the same BreadcrumbTreeElement but a different parent (for + // example, when inserting behind the tail), we don't need to the + // copy the already owned/malloc'd data. + Owned(Rc), Borrowed(&'a dyn IntoContextElement), } @@ -27,6 +33,10 @@ impl<'a> BreadcrumbTree<'a> { self.parent } + pub fn get_element(&self) -> &BreadcrumbTreeElement<'a> { + &self.element + } + pub fn ice_iter(&'a self) -> impl Iterator { self.breadcrumb_iter().map(|b| b.get_ice()) } @@ -34,6 +44,14 @@ impl<'a> BreadcrumbTree<'a> { pub fn breadcrumb_iter(&'a self) -> BreadcrumbTreeIterator<'a> { BreadcrumbTreeIterator(Some(self)) } + + pub fn clone_to_new_parent(&self, parent: Option<&'a BreadcrumbTree>) -> Self { + // TODO: Maybe not needed anymore? + BreadcrumbTree { + parent: parent, + element: self.element.clone(), + } + } } impl<'a> Borrow for BreadcrumbTreeElement<'a> { diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index c8e1d67..3245568 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -64,7 +64,7 @@ impl<'a> DustRenderer<'a> { // If there is an explicit context, then drop all the current // context - let mut parent = match explicit_context { + let parent = match explicit_context { Some(_) => None, None => maybe_breadcrumbs, }; @@ -86,4 +86,85 @@ impl<'a> DustRenderer<'a> { Some((parent, new_nodes)) } + + fn new_breadcrumbs_partial<'b>( + &self, + maybe_breadcrumbs: Option<&'b BreadcrumbTree>, + explicit_context_maybe_breadcrumbs: Option<&'b BreadcrumbTree>, + injected_context: Option<&'b dyn IntoContextElement>, + explicit_context: &Option>, + ) -> Option<(Option<&'b BreadcrumbTree>, Vec>)> { + // If none of the additional contexts are present, return None + // to signal that the original breadcrumbs should be used + // rather than incurring a copy here. + match (injected_context, explicit_context) { + (None, None) => return None, + _ => (), + }; + + // If there is an explicit context, then drop all the current + // context + let mut parent = match explicit_context { + Some(_) => None, + None => maybe_breadcrumbs, + }; + let mut new_nodes: Vec = Vec::new(); + + injected_context.map(|ctx| { + // Special case: when there is no explicit context, the + // injected context gets inserted 1 spot behind the + // current context. Otherwise, the injected context gets + // added after the current context but before the explicit + // context. + match explicit_context { + None => { + let (new_parent, passed_nodes) = + Self::split_tree_at_predicate(parent, |b| b.get_ice().is_pseudo_element()); + parent = new_parent; + new_nodes.extend(passed_nodes.iter().map(|b| b.get_element().clone())); + } + _ => new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx)), + } + }); + explicit_context.as_ref().map(|path| { + let x = walk_path(maybe_breadcrumbs, &path.keys); + // TODO: should resolving the value here use + // explicit_context_maybe_breadcrumbs or + // maybe_breadcrumbs? + x.map(|ice| ice.into_context_element(self, maybe_breadcrumbs)) + .map(|val| { + if val.is_truthy() { + new_nodes.push(BreadcrumbTreeElement::Borrowed(val.from_context_element())) + } + }); + }); + + None + } + + /// Returns a Breadcrumb tree where all the bottom nodes that do + /// not match the predicate and the first node that match the + /// predicate are shaved off, and a list of those nodes that are + /// shaved off. + fn split_tree_at_predicate<'b, F>( + maybe_breadcrumbs: Option<&'b BreadcrumbTree>, + f: F, + ) -> (Option<&'b BreadcrumbTree<'b>>, Vec<&'b BreadcrumbTree<'b>>) + where + F: Fn(&'b BreadcrumbTree) -> bool, + { + match maybe_breadcrumbs { + None => return (None, Vec::new()), + Some(breadcrumbs) => { + let mut passed_nodes: Vec<&'b BreadcrumbTree<'b>> = Vec::new(); + for tree_node in breadcrumbs { + passed_nodes.push(tree_node); + if f(tree_node) { + return (tree_node.get_parent(), passed_nodes); + } + } + return (None, passed_nodes); + } + } + } } From 680fc167ea40f9015309f1f42c22277edd4dbd3b Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 20:43:07 -0400 Subject: [PATCH 26/67] Turns out it was just a lifetime issue. --- src/renderer/renderer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 3245568..94d3fcc 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -88,7 +88,7 @@ impl<'a> DustRenderer<'a> { } fn new_breadcrumbs_partial<'b>( - &self, + &'b self, maybe_breadcrumbs: Option<&'b BreadcrumbTree>, explicit_context_maybe_breadcrumbs: Option<&'b BreadcrumbTree>, injected_context: Option<&'b dyn IntoContextElement>, @@ -139,7 +139,7 @@ impl<'a> DustRenderer<'a> { }); }); - None + Some((parent, new_nodes)) } /// Returns a Breadcrumb tree where all the bottom nodes that do From da6655d4b68d4a57f3fbff7632d98f0db84ea054 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 21:18:54 -0400 Subject: [PATCH 27/67] General structure to the rendering code. --- src/renderer/renderer.rs | 124 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 2 deletions(-) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 94d3fcc..34f04fa 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -1,12 +1,20 @@ use crate::parser::template; +use crate::parser::Body; +use crate::parser::DustTag; +use crate::parser::PartialNameElement; use crate::parser::Path; use crate::parser::Template; +use crate::parser::TemplateElement; use crate::renderer::breadcrumb_tree::BreadcrumbTree; use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement; use crate::renderer::context_element::ContextElement; use crate::renderer::context_element::IntoContextElement; use crate::renderer::errors::CompileError; +use crate::renderer::errors::RenderError; +use crate::renderer::inline_partial_tree::extract_inline_partials; +use crate::renderer::inline_partial_tree::InlinePartialTreeElement; use crate::renderer::tree_walking::walk_path; +use std::borrow::Borrow; use std::collections::HashMap; #[derive(Clone, Debug)] @@ -32,6 +40,103 @@ impl<'a> DustRenderer<'a> { self.templates.insert(name, template); } + pub fn render(&'a self, name: &str, context: Option<&C>) -> Result + where + C: IntoContextElement, + { + let breadcrumbs = + context.map(|ctx| BreadcrumbTree::new(None, BreadcrumbTreeElement::Borrowed(ctx))); + self.render_template(name, breadcrumbs.as_ref(), None) + } + + pub fn render_template( + &'a self, + name: &str, + breadcrumbs: Option<&'a BreadcrumbTree>, + blocks: Option<&'a InlinePartialTreeElement<'a>>, + ) -> Result { + let main_template = match self.templates.get(name) { + Some(tmpl) => tmpl, + None => { + return Err(RenderError::TemplateNotFound(name.to_owned())); + } + }; + let extracted_inline_partials = extract_inline_partials(main_template); + let new_blocks = InlinePartialTreeElement::new(blocks, extracted_inline_partials); + let new_block_context = BlockContext { + breadcrumbs: breadcrumbs, + blocks: &new_blocks, + }; + self.render_body(&main_template.contents, breadcrumbs, &new_block_context) + } + + fn render_maybe_body( + &'a self, + body: &'a Option, + breadcrumbs: Option<&'a BreadcrumbTree>, + blocks: &'a BlockContext<'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, + breadcrumbs: Option<&'a BreadcrumbTree>, + blocks: &'a BlockContext<'a>, + ) -> Result { + let mut output = String::new(); + for elem in &body.elements { + match elem { + TemplateElement::TEIgnoredWhitespace(_) => {} + TemplateElement::TESpan(span) => output.push_str(span.contents), + TemplateElement::TETag(dt) => { + output.push_str(&self.render_tag(dt, breadcrumbs, blocks)?); + } + } + } + Ok(output) + } + + /// For rendering a dynamic partial's name or an rvalue template + pub fn render_partial_name( + &'a self, + body: &'a Vec, + breadcrumbs: Option<&'a BreadcrumbTree>, + ) -> Result { + let converted_to_template_elements: Vec> = + body.into_iter().map(|e| e.into()).collect(); + // Simple templates like partial names and reference rvalues + // cannot contain blocks or inline partials, so we use a blank + // BlockContext. + let empty_block_context = BlockContext { + breadcrumbs: None, + blocks: &InlinePartialTreeElement::new(None, HashMap::new()), + }; + self.render_body( + &Body { + elements: converted_to_template_elements, + }, + breadcrumbs, + &empty_block_context, + ) + } + + fn render_tag( + &'a self, + tag: &'a DustTag, + breadcrumbs: Option<&'a BreadcrumbTree>, + blocks: &'a BlockContext<'a>, + ) -> Result { + match tag { + _ => panic!("Unsupported tag"), + } + Ok("".to_owned()) + } + /// Returns a option of a tuple of (parent, new_node_elements) /// which can then be formed into new BreadcrumbTreeNodes /// @@ -42,8 +147,8 @@ impl<'a> DustRenderer<'a> { /// explicit contexts) and the additional node elements (which may /// be empty) should be combined into a final BreadcrumbTreeNode fn new_breadcrumbs_section<'b>( - &self, - maybe_breadcrumbs: Option<&'a BreadcrumbTree>, + &'b self, + maybe_breadcrumbs: Option<&'b BreadcrumbTree>, index_context: Option<&'b dyn IntoContextElement>, injected_context: Option<&'b dyn IntoContextElement>, explicit_context: &Option>, @@ -87,6 +192,15 @@ impl<'a> DustRenderer<'a> { Some((parent, new_nodes)) } + /// Returns a option of a tuple of (parent, new_node_elements) + /// which can then be formed into new BreadcrumbTreeNodes + /// + /// If None is returned, then it is a signal to simply re-use the + /// existing breadcrumbs. + /// + /// Otherwise, the parent (which may be None, especially for + /// explicit contexts) and the additional node elements (which may + /// be empty) should be combined into a final BreadcrumbTreeNode fn new_breadcrumbs_partial<'b>( &'b self, maybe_breadcrumbs: Option<&'b BreadcrumbTree>, @@ -168,3 +282,9 @@ impl<'a> DustRenderer<'a> { } } } + +struct BlockContext<'a> { + /// The breadcrumbs at the time of entering the current partial + breadcrumbs: Option<&'a BreadcrumbTree<'a>>, + blocks: &'a InlinePartialTreeElement<'a>, +} From bbc1a24631ad0a4f72e881e64da7d03aaaa4ef36 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 21:26:31 -0400 Subject: [PATCH 28/67] Added back in reference. --- src/renderer/renderer.rs | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 34f04fa..3aa1d2d 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -1,8 +1,10 @@ use crate::parser::template; use crate::parser::Body; use crate::parser::DustTag; +use crate::parser::Filter; use crate::parser::PartialNameElement; use crate::parser::Path; +use crate::parser::Special; use crate::parser::Template; use crate::parser::TemplateElement; use crate::renderer::breadcrumb_tree::BreadcrumbTree; @@ -11,6 +13,7 @@ use crate::renderer::context_element::ContextElement; use crate::renderer::context_element::IntoContextElement; use crate::renderer::errors::CompileError; 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::tree_walking::walk_path; @@ -132,6 +135,32 @@ impl<'a> DustRenderer<'a> { blocks: &'a BlockContext<'a>, ) -> Result { match tag { + DustTag::DTComment(_comment) => (), + DustTag::DTSpecial(special) => { + return Ok(match special { + Special::Space => " ", + Special::NewLine => "\n", + Special::CarriageReturn => "\r", + Special::LeftCurlyBrace => "{", + Special::RightCurlyBrace => "}", + } + .to_owned()) + } + DustTag::DTLiteralStringBlock(literal) => return Ok((*literal).to_owned()), + DustTag::DTReference(reference) => { + let val = walk_path(breadcrumbs, &reference.path.keys) + .map(|ice| ice.into_context_element(self, breadcrumbs)); + match val { + Err(WalkError::CantWalk) => return Ok("".to_owned()), + Ok(final_val) => { + return if final_val.is_truthy() { + final_val.render(&Self::preprocess_filters(&reference.filters)) + } else { + Ok("".to_owned()) + }; + } + } + } _ => panic!("Unsupported tag"), } Ok("".to_owned()) @@ -281,6 +310,21 @@ impl<'a> DustRenderer<'a> { } } } + + fn preprocess_filters(filters: &Vec) -> Vec { + let mut final_filters: Vec = filters + .into_iter() + .filter(|f| f != &&Filter::DisableHtmlEncode) + .map(|f| f.clone()) + .collect(); + + // If the user has not specified any escaping filter (|s or + // |h), automatically add an html escape filter + if !filters.iter().any(|f| f == &Filter::DisableHtmlEncode) { + final_filters.push(Filter::HtmlEncode); + } + final_filters + } } struct BlockContext<'a> { From 7bf8012d6ef069fe75537e2deaa7a16a6a812d2b Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 21:30:36 -0400 Subject: [PATCH 29/67] Need to mix back in parameterscontext. --- src/renderer/renderer.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 3aa1d2d..0cd3a38 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -161,6 +161,19 @@ impl<'a> DustRenderer<'a> { } } } + DustTag::DTSection(container) => { + //let injected_context = ParametersContext::new(breadcrumbs, &container.params); + let val = walk_path(breadcrumbs, &container.path.keys) + .map(|ice| ice.into_context_element(self, breadcrumbs)); + match val { + Err(WalkError::CantWalk) => { + // TODO + } + Ok(final_val) => { + // TODO + } + } + } _ => panic!("Unsupported tag"), } Ok("".to_owned()) From ff74d78fdb099911ffa465b8f334702b3c4f4bb3 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 21:46:00 -0400 Subject: [PATCH 30/67] Starting the new parameters context object. --- src/renderer/mod.rs | 2 +- src/renderer/parameters_context.rs | 262 +----------------------- src/renderer/parameters_context_old.rs | 264 +++++++++++++++++++++++++ 3 files changed, 274 insertions(+), 254 deletions(-) create mode 100644 src/renderer/parameters_context_old.rs diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 2ebbc51..6baa196 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -5,7 +5,7 @@ mod context_element; mod errors; mod inline_partial_tree; // mod iteration_context; -// mod parameters_context; +mod parameters_context; mod renderer; mod tree_walking; mod walking; diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 0fb4abf..3261b6e 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -1,264 +1,20 @@ use crate::parser::KVPair; -use crate::parser::{Filter, OwnedLiteral, PartialNameElement, RValue}; -use crate::renderer::context_element::CompareContextElement; -use crate::renderer::context_element::ContextElement; +use crate::renderer::breadcrumb_tree::BreadcrumbTree; use crate::renderer::context_element::IntoContextElement; -use crate::renderer::walking::walk_path; use crate::renderer::DustRenderer; -use crate::renderer::Loopable; -use crate::renderer::RenderError; -use crate::renderer::Renderable; -use crate::renderer::Truthiness; -use crate::renderer::WalkError; -use crate::renderer::Walkable; -use std::{cmp::Ordering, 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. -#[derive(Clone, Debug)] -pub enum OwnedRValue { - RVPath(OwnedPath), - RVTemplate(Vec), - RVLiteral(OwnedLiteral), -} - -#[derive(Clone, Debug, PartialEq)] -pub struct OwnedPath { - pub keys: Vec, -} - -impl From<&RValue<'_>> for OwnedRValue { - fn from(original: &RValue<'_>) -> Self { - match original { - RValue::RVLiteral(literal) => OwnedRValue::RVLiteral(literal.clone()), - RValue::RVTemplate(template) => OwnedRValue::RVTemplate(template.clone()), - RValue::RVPath(path) => OwnedRValue::RVPath(OwnedPath { - keys: path.keys.iter().map(|k| k.to_string()).collect(), - }), - } - } -} +use std::collections::HashMap; #[derive(Debug)] -pub struct ParametersContext { - params: HashMap, - breadcrumbs: Vec>, +pub struct ParametersContext<'a> { + params: HashMap<&'a str, &'a dyn IntoContextElement>, } -impl ParametersContext { +impl<'a> ParametersContext<'a> { pub fn new( - breadcrumbs: &Vec<&dyn IntoContextElement>, - params: &Vec, - ) -> ParametersContext { - let owned_params: HashMap = params - .iter() - .map(|kvpair| (kvpair.key.to_string(), OwnedRValue::from(&kvpair.value))) - .collect(); - let owned_breadcrumbs: Vec> = - breadcrumbs.iter().map(|ce| ce.clone_to_box()).collect(); - - ParametersContext { - params: owned_params, - breadcrumbs: owned_breadcrumbs, - } - } -} - -impl ContextElement for ParametersContext {} - -impl Truthiness for ParametersContext { - fn is_truthy(&self) -> bool { - // 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 `{.}`. - true - } -} - -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 - // context, we wouldn't be able to access it with `{.}`. - Ok("[object Object]".to_owned()) - } -} - -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 - // context, we wouldn't be able to access it with `{.}`. - Vec::new() - } -} - -impl Walkable for ParametersContext { - fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { - let rval = self.params.get(segment).ok_or(WalkError::CantWalk)?; - match rval { - OwnedRValue::RVPath(path) => walk_path(&self.breadcrumbs, &path.keys), - OwnedRValue::RVTemplate(template) => Ok(template), - OwnedRValue::RVLiteral(literal) => Ok(literal), - } - } - - fn is_pseudo_element(&self) -> bool { - true - } -} - -impl Clone for ParametersContext { - fn clone(&self) -> Self { - 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: new_params, - breadcrumbs: new_breadcrumbs, - } - } -} - -impl CompareContextElement for ParametersContext { - fn equals(&self, other: &dyn ContextElement) -> bool { - // TODO: Does this ever happen? perhaps I should have a panic here. - false - } - - fn partial_compare(&self, other: &dyn ContextElement) -> Option { - // TODO: Does this ever happen? perhaps I should have a panic here. - None - } -} - -impl ContextElement for OwnedLiteral {} - -impl Truthiness for OwnedLiteral { - fn is_truthy(&self) -> bool { - match self { - OwnedLiteral::LString(text) => !text.is_empty(), - OwnedLiteral::LPositiveInteger(num) => true, - } - } -} - -impl Renderable for OwnedLiteral { - fn render(&self, _filters: &Vec) -> Result { - match self { - OwnedLiteral::LString(text) => Ok(text.clone()), - OwnedLiteral::LPositiveInteger(num) => Ok(num.to_string()), - } - } -} - -impl Loopable for OwnedLiteral { - fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { - Vec::new() - } -} - -impl Walkable for OwnedLiteral { - fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { - Err(WalkError::CantWalk) - } -} - -impl CompareContextElement for OwnedLiteral { - fn equals(&self, other: &dyn ContextElement) -> bool { - // println!("equals literal {:?} | {:?}", self, other); - // If its an OwnedLiteral 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_literal) => match (self, other_literal) { - (OwnedLiteral::LString(self_text), OwnedLiteral::LString(other_text)) => { - self_text == other_text - } - (OwnedLiteral::LPositiveInteger(self_num), OwnedLiteral::LString(other_text)) => { - &self_num.to_string() == other_text - } - (OwnedLiteral::LString(self_text), OwnedLiteral::LPositiveInteger(other_num)) => { - self_text == &other_num.to_string() - } - ( - OwnedLiteral::LPositiveInteger(self_num), - OwnedLiteral::LPositiveInteger(other_num), - ) => self_num == other_num, - }, - } - } - - fn partial_compare(&self, other: &dyn ContextElement) -> Option { - // println!("partial_compare literal {:?} | {:?}", self, other); - // If its an OwnedLiteral 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_literal) => match (self, other_literal) { - (OwnedLiteral::LString(self_text), OwnedLiteral::LString(other_text)) => { - self_text.partial_cmp(other_text) - } - (OwnedLiteral::LPositiveInteger(self_num), OwnedLiteral::LString(other_text)) => { - self_num.to_string().partial_cmp(other_text) - } - (OwnedLiteral::LString(self_text), OwnedLiteral::LPositiveInteger(other_num)) => { - self_text.partial_cmp(&other_num.to_string()) - } - ( - OwnedLiteral::LPositiveInteger(self_num), - OwnedLiteral::LPositiveInteger(other_num), - ) => self_num.partial_cmp(other_num), - }, - } - } -} - -impl IntoContextElement for Vec { - fn into_context_element( - &self, renderer: &DustRenderer, - breadcrumbs: &Vec<&dyn IntoContextElement>, - ) -> &dyn ContextElement { - // OwnedLiteral::LString( - // renderer - // .render_partial_name(self, breadcrumbs) - // .expect("TODO: Make into_context_element return a RenderError"), - // ) - // TODO - &OwnedLiteral::LPositiveInteger(1) - } -} - -impl Walkable for Vec { - fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { - Err(WalkError::CantWalk) + breadcrumbs: Option<&BreadcrumbTree>, + params: &Vec, + ) -> Self { + todo!() } } diff --git a/src/renderer/parameters_context_old.rs b/src/renderer/parameters_context_old.rs new file mode 100644 index 0000000..0fb4abf --- /dev/null +++ b/src/renderer/parameters_context_old.rs @@ -0,0 +1,264 @@ +use crate::parser::KVPair; +use crate::parser::{Filter, OwnedLiteral, PartialNameElement, RValue}; +use crate::renderer::context_element::CompareContextElement; +use crate::renderer::context_element::ContextElement; +use crate::renderer::context_element::IntoContextElement; +use crate::renderer::walking::walk_path; +use crate::renderer::DustRenderer; +use crate::renderer::Loopable; +use crate::renderer::RenderError; +use crate::renderer::Renderable; +use crate::renderer::Truthiness; +use crate::renderer::WalkError; +use crate::renderer::Walkable; +use std::{cmp::Ordering, 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. +#[derive(Clone, Debug)] +pub enum OwnedRValue { + RVPath(OwnedPath), + RVTemplate(Vec), + RVLiteral(OwnedLiteral), +} + +#[derive(Clone, Debug, PartialEq)] +pub struct OwnedPath { + pub keys: Vec, +} + +impl From<&RValue<'_>> for OwnedRValue { + fn from(original: &RValue<'_>) -> Self { + match original { + RValue::RVLiteral(literal) => OwnedRValue::RVLiteral(literal.clone()), + RValue::RVTemplate(template) => OwnedRValue::RVTemplate(template.clone()), + RValue::RVPath(path) => OwnedRValue::RVPath(OwnedPath { + keys: path.keys.iter().map(|k| k.to_string()).collect(), + }), + } + } +} + +#[derive(Debug)] +pub struct ParametersContext { + params: HashMap, + breadcrumbs: Vec>, +} + +impl ParametersContext { + pub fn new( + breadcrumbs: &Vec<&dyn IntoContextElement>, + params: &Vec, + ) -> ParametersContext { + let owned_params: HashMap = params + .iter() + .map(|kvpair| (kvpair.key.to_string(), OwnedRValue::from(&kvpair.value))) + .collect(); + let owned_breadcrumbs: Vec> = + breadcrumbs.iter().map(|ce| ce.clone_to_box()).collect(); + + ParametersContext { + params: owned_params, + breadcrumbs: owned_breadcrumbs, + } + } +} + +impl ContextElement for ParametersContext {} + +impl Truthiness for ParametersContext { + fn is_truthy(&self) -> bool { + // 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 `{.}`. + true + } +} + +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 + // context, we wouldn't be able to access it with `{.}`. + Ok("[object Object]".to_owned()) + } +} + +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 + // context, we wouldn't be able to access it with `{.}`. + Vec::new() + } +} + +impl Walkable for ParametersContext { + fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { + let rval = self.params.get(segment).ok_or(WalkError::CantWalk)?; + match rval { + OwnedRValue::RVPath(path) => walk_path(&self.breadcrumbs, &path.keys), + OwnedRValue::RVTemplate(template) => Ok(template), + OwnedRValue::RVLiteral(literal) => Ok(literal), + } + } + + fn is_pseudo_element(&self) -> bool { + true + } +} + +impl Clone for ParametersContext { + fn clone(&self) -> Self { + 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: new_params, + breadcrumbs: new_breadcrumbs, + } + } +} + +impl CompareContextElement for ParametersContext { + fn equals(&self, other: &dyn ContextElement) -> bool { + // TODO: Does this ever happen? perhaps I should have a panic here. + false + } + + fn partial_compare(&self, other: &dyn ContextElement) -> Option { + // TODO: Does this ever happen? perhaps I should have a panic here. + None + } +} + +impl ContextElement for OwnedLiteral {} + +impl Truthiness for OwnedLiteral { + fn is_truthy(&self) -> bool { + match self { + OwnedLiteral::LString(text) => !text.is_empty(), + OwnedLiteral::LPositiveInteger(num) => true, + } + } +} + +impl Renderable for OwnedLiteral { + fn render(&self, _filters: &Vec) -> Result { + match self { + OwnedLiteral::LString(text) => Ok(text.clone()), + OwnedLiteral::LPositiveInteger(num) => Ok(num.to_string()), + } + } +} + +impl Loopable for OwnedLiteral { + fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { + Vec::new() + } +} + +impl Walkable for OwnedLiteral { + fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { + Err(WalkError::CantWalk) + } +} + +impl CompareContextElement for OwnedLiteral { + fn equals(&self, other: &dyn ContextElement) -> bool { + // println!("equals literal {:?} | {:?}", self, other); + // If its an OwnedLiteral 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_literal) => match (self, other_literal) { + (OwnedLiteral::LString(self_text), OwnedLiteral::LString(other_text)) => { + self_text == other_text + } + (OwnedLiteral::LPositiveInteger(self_num), OwnedLiteral::LString(other_text)) => { + &self_num.to_string() == other_text + } + (OwnedLiteral::LString(self_text), OwnedLiteral::LPositiveInteger(other_num)) => { + self_text == &other_num.to_string() + } + ( + OwnedLiteral::LPositiveInteger(self_num), + OwnedLiteral::LPositiveInteger(other_num), + ) => self_num == other_num, + }, + } + } + + fn partial_compare(&self, other: &dyn ContextElement) -> Option { + // println!("partial_compare literal {:?} | {:?}", self, other); + // If its an OwnedLiteral 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_literal) => match (self, other_literal) { + (OwnedLiteral::LString(self_text), OwnedLiteral::LString(other_text)) => { + self_text.partial_cmp(other_text) + } + (OwnedLiteral::LPositiveInteger(self_num), OwnedLiteral::LString(other_text)) => { + self_num.to_string().partial_cmp(other_text) + } + (OwnedLiteral::LString(self_text), OwnedLiteral::LPositiveInteger(other_num)) => { + self_text.partial_cmp(&other_num.to_string()) + } + ( + OwnedLiteral::LPositiveInteger(self_num), + OwnedLiteral::LPositiveInteger(other_num), + ) => self_num.partial_cmp(other_num), + }, + } + } +} + +impl IntoContextElement for Vec { + fn into_context_element( + &self, + renderer: &DustRenderer, + breadcrumbs: &Vec<&dyn IntoContextElement>, + ) -> &dyn ContextElement { + // OwnedLiteral::LString( + // renderer + // .render_partial_name(self, breadcrumbs) + // .expect("TODO: Make into_context_element return a RenderError"), + // ) + // TODO + &OwnedLiteral::LPositiveInteger(1) + } +} + +impl Walkable for Vec { + fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { + Err(WalkError::CantWalk) + } +} From 09e0b046a0eebcf4ad41c6d373be29d84f65853d Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 21:59:24 -0400 Subject: [PATCH 31/67] Copy over the implementation of ContextElement for OwnedLiteral which will be the result of calling IntoContextElement on RValues. --- src/renderer/parameters_context.rs | 121 +++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 3261b6e..6c45db6 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -1,7 +1,19 @@ +use crate::parser::Filter; use crate::parser::KVPair; +use crate::parser::OwnedLiteral; +use crate::parser::RValue; use crate::renderer::breadcrumb_tree::BreadcrumbTree; +use crate::renderer::context_element::CompareContextElement; +use crate::renderer::context_element::ContextElement; use crate::renderer::context_element::IntoContextElement; use crate::renderer::DustRenderer; +use crate::renderer::Loopable; +use crate::renderer::RenderError; +use crate::renderer::Renderable; +use crate::renderer::Truthiness; +use crate::renderer::WalkError; +use crate::renderer::Walkable; +use std::cmp::Ordering; use std::collections::HashMap; #[derive(Debug)] @@ -18,3 +30,112 @@ impl<'a> ParametersContext<'a> { todo!() } } + +impl<'a> IntoContextElement for RValue<'a> { + fn into_context_element( + &self, + renderer: &DustRenderer, + breadcrumbs: Option<&BreadcrumbTree>, + ) -> &dyn ContextElement { + todo!() + } +} + +impl<'a> Walkable for RValue<'a> { + fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { + Err(WalkError::CantWalk) + } +} + +impl ContextElement for OwnedLiteral {} + +impl Truthiness for OwnedLiteral { + fn is_truthy(&self) -> bool { + match self { + OwnedLiteral::LString(text) => !text.is_empty(), + OwnedLiteral::LPositiveInteger(num) => true, + } + } +} + +impl Renderable for OwnedLiteral { + fn render(&self, _filters: &Vec) -> Result { + match self { + OwnedLiteral::LString(text) => Ok(text.clone()), + OwnedLiteral::LPositiveInteger(num) => Ok(num.to_string()), + } + } +} + +impl Loopable for OwnedLiteral { + fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { + Vec::new() + } +} + +impl Walkable for OwnedLiteral { + fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { + Err(WalkError::CantWalk) + } +} + +impl CompareContextElement for OwnedLiteral { + fn equals(&self, other: &dyn ContextElement) -> bool { + // println!("equals literal {:?} | {:?}", self, other); + // If its an OwnedLiteral 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_literal) => match (self, other_literal) { + (OwnedLiteral::LString(self_text), OwnedLiteral::LString(other_text)) => { + self_text == other_text + } + (OwnedLiteral::LPositiveInteger(self_num), OwnedLiteral::LString(other_text)) => { + &self_num.to_string() == other_text + } + (OwnedLiteral::LString(self_text), OwnedLiteral::LPositiveInteger(other_num)) => { + self_text == &other_num.to_string() + } + ( + OwnedLiteral::LPositiveInteger(self_num), + OwnedLiteral::LPositiveInteger(other_num), + ) => self_num == other_num, + }, + } + } + + fn partial_compare(&self, other: &dyn ContextElement) -> Option { + // println!("partial_compare literal {:?} | {:?}", self, other); + // If its an OwnedLiteral 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_literal) => match (self, other_literal) { + (OwnedLiteral::LString(self_text), OwnedLiteral::LString(other_text)) => { + self_text.partial_cmp(other_text) + } + (OwnedLiteral::LPositiveInteger(self_num), OwnedLiteral::LString(other_text)) => { + self_num.to_string().partial_cmp(other_text) + } + (OwnedLiteral::LString(self_text), OwnedLiteral::LPositiveInteger(other_num)) => { + self_text.partial_cmp(&other_num.to_string()) + } + ( + OwnedLiteral::LPositiveInteger(self_num), + OwnedLiteral::LPositiveInteger(other_num), + ) => self_num.partial_cmp(other_num), + }, + } + } +} From 3aaf7f9987497a41857ed026a1f34cbe3eb8b34d Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 22:06:12 -0400 Subject: [PATCH 32/67] I think I need to make into_context_element return an Option. The reason is, missing values for paths are not equal to null so I can't use an OwnedLiteral unless I create a special "walking failed" OwnedLiteral. --- 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 6c45db6..4a81476 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -37,7 +37,11 @@ impl<'a> IntoContextElement for RValue<'a> { renderer: &DustRenderer, breadcrumbs: Option<&BreadcrumbTree>, ) -> &dyn ContextElement { - todo!() + match self { + RValue::RVLiteral(owned_literal) => owned_literal, + RValue::RVPath(path) => todo!(), + RValue::RVTemplate(template) => todo!(), + } } } From b74dc394a80fc3f19fe897f49262246f0408f54b Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 22:17:58 -0400 Subject: [PATCH 33/67] Switched to returning an Option for into_context_element(). --- src/renderer/context_element.rs | 6 +++--- src/renderer/parameters_context.rs | 4 ++-- src/renderer/renderer.rs | 16 ++++++++++------ 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index f69fcd3..27249d1 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -105,7 +105,7 @@ pub trait IntoContextElement: Debug + Walkable /* + CloneIntoBoxedContextElement &self, renderer: &DustRenderer, breadcrumbs: Option<&BreadcrumbTree>, - ) -> &dyn ContextElement; + ) -> Option<&dyn ContextElement>; } impl IntoContextElement for C { @@ -113,7 +113,7 @@ impl IntoContextElement for C { &self, renderer: &DustRenderer, breadcrumbs: Option<&BreadcrumbTree>, - ) -> &dyn ContextElement { - self + ) -> Option<&dyn ContextElement> { + Some(self) } } diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 4a81476..f07f2c2 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -36,9 +36,9 @@ impl<'a> IntoContextElement for RValue<'a> { &self, renderer: &DustRenderer, breadcrumbs: Option<&BreadcrumbTree>, - ) -> &dyn ContextElement { + ) -> Option<&dyn ContextElement> { match self { - RValue::RVLiteral(owned_literal) => owned_literal, + RValue::RVLiteral(owned_literal) => Some(owned_literal), RValue::RVPath(path) => todo!(), RValue::RVTemplate(template) => todo!(), } diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 0cd3a38..d6319ce 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -151,8 +151,8 @@ impl<'a> DustRenderer<'a> { let val = walk_path(breadcrumbs, &reference.path.keys) .map(|ice| ice.into_context_element(self, breadcrumbs)); match val { - Err(WalkError::CantWalk) => return Ok("".to_owned()), - Ok(final_val) => { + Err(WalkError::CantWalk) | Ok(None) => return Ok("".to_owned()), + Ok(Some(final_val)) => { return if final_val.is_truthy() { final_val.render(&Self::preprocess_filters(&reference.filters)) } else { @@ -218,8 +218,10 @@ impl<'a> DustRenderer<'a> { let mut new_nodes: Vec = Vec::new(); explicit_context.as_ref().map(|path| { - let x = walk_path(maybe_breadcrumbs, &path.keys); - x.map(|ice| ice.into_context_element(self, maybe_breadcrumbs)) + walk_path(maybe_breadcrumbs, &path.keys) + .map(|ice| ice.into_context_element(self, maybe_breadcrumbs)) + .ok() + .flatten() .map(|val| { if val.is_truthy() { new_nodes.push(BreadcrumbTreeElement::Borrowed(val.from_context_element())) @@ -283,11 +285,13 @@ impl<'a> DustRenderer<'a> { } }); explicit_context.as_ref().map(|path| { - let x = walk_path(maybe_breadcrumbs, &path.keys); // TODO: should resolving the value here use // explicit_context_maybe_breadcrumbs or // maybe_breadcrumbs? - x.map(|ice| ice.into_context_element(self, maybe_breadcrumbs)) + walk_path(maybe_breadcrumbs, &path.keys) + .map(|ice| ice.into_context_element(self, maybe_breadcrumbs)) + .ok() + .flatten() .map(|val| { if val.is_truthy() { new_nodes.push(BreadcrumbTreeElement::Borrowed(val.from_context_element())) From 064027e77b278e1dc18131c66d18659b8657af6c Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 22:31:05 -0400 Subject: [PATCH 34/67] Worked around a lifetime issue with into_context_element. --- src/renderer/context_element.rs | 16 ++++++++-------- src/renderer/parameters_context.rs | 15 ++++++++++----- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index 27249d1..49cc72d 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -101,19 +101,19 @@ impl FromContextElement for C { } pub trait IntoContextElement: Debug + Walkable /* + CloneIntoBoxedContextElement*/ { - fn into_context_element( - &self, + fn into_context_element<'a>( + &'a self, renderer: &DustRenderer, - breadcrumbs: Option<&BreadcrumbTree>, - ) -> Option<&dyn ContextElement>; + breadcrumbs: Option<&'a BreadcrumbTree<'a>>, + ) -> Option<&'a dyn ContextElement>; } impl IntoContextElement for C { - fn into_context_element( - &self, + fn into_context_element<'a>( + &'a self, renderer: &DustRenderer, - breadcrumbs: Option<&BreadcrumbTree>, - ) -> Option<&dyn ContextElement> { + breadcrumbs: Option<&'a BreadcrumbTree<'a>>, + ) -> Option<&'a dyn ContextElement> { Some(self) } } diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index f07f2c2..3b7b7ba 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -6,6 +6,7 @@ use crate::renderer::breadcrumb_tree::BreadcrumbTree; use crate::renderer::context_element::CompareContextElement; use crate::renderer::context_element::ContextElement; use crate::renderer::context_element::IntoContextElement; +use crate::renderer::tree_walking::walk_path; use crate::renderer::DustRenderer; use crate::renderer::Loopable; use crate::renderer::RenderError; @@ -32,16 +33,20 @@ impl<'a> ParametersContext<'a> { } impl<'a> IntoContextElement for RValue<'a> { - fn into_context_element( - &self, + fn into_context_element<'b>( + &'b self, renderer: &DustRenderer, - breadcrumbs: Option<&BreadcrumbTree>, - ) -> Option<&dyn ContextElement> { + breadcrumbs: Option<&'b BreadcrumbTree<'b>>, + ) -> Option<&'b dyn ContextElement> { match self { RValue::RVLiteral(owned_literal) => Some(owned_literal), - RValue::RVPath(path) => todo!(), + RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys) + .map(|ice| ice.into_context_element(renderer, breadcrumbs)) + .ok() + .flatten(), RValue::RVTemplate(template) => todo!(), } + // todo!() } } From 7789e6245d09cb1fa02e6e0b566e33079fdf874b Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 22:35:46 -0400 Subject: [PATCH 35/67] I need to be able to returned owned data from into_context_element. --- src/renderer/parameters_context.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 3b7b7ba..7e86c3e 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -44,9 +44,16 @@ impl<'a> IntoContextElement for RValue<'a> { .map(|ice| ice.into_context_element(renderer, breadcrumbs)) .ok() .flatten(), - RValue::RVTemplate(template) => todo!(), + RValue::RVTemplate(template) => { + // TODO + renderer + .render_partial_name(template, breadcrumbs) + .map(|rendered| OwnedLiteral::LString(rendered)) + .ok() + .as_ref() + .map(|l| l as _) + } } - // todo!() } } From da154399460a75a71118a20b3826cc1fc8ad825d Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 23:29:12 -0400 Subject: [PATCH 36/67] Start of IceResult to return owned values from into_context_element. --- src/renderer/context_element.rs | 39 ++++++++++++++++++++++++++++++ src/renderer/parameters_context.rs | 13 +++++----- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index 49cc72d..7a2d7f4 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -4,6 +4,7 @@ use crate::renderer::errors::RenderError; use crate::renderer::errors::WalkError; use crate::renderer::DustRenderer; use std::any::Any; +use std::rc::Rc; use std::{cmp::Ordering, fmt::Debug}; pub trait ContextElement: @@ -117,3 +118,41 @@ impl IntoContextElement for C { Some(self) } } + +#[derive(Clone, Debug)] +pub enum IceResult<'a> { + // Using Rc so that when we need to create BreadcrumbTrees with + // the same BreadcrumbTreeElement but a different parent (for + // example, when inserting behind the tail), we don't need to the + // copy the already owned/malloc'd data. + Owned(Rc), + Borrowed(&'a dyn ContextElement), +} + +impl<'a> IceResult<'a> { + pub fn get_context_element_reference(&self) -> &dyn ContextElement { + match self { + IceResult::Owned(rc_ce) => rc_ce.as_ref(), + IceResult::Borrowed(ce) => *ce, + } + } +} + +impl<'a> IntoContextElement for IceResult<'a> { + fn into_context_element<'b>( + &'b self, + renderer: &DustRenderer, + breadcrumbs: Option<&'b BreadcrumbTree<'b>>, + ) -> Option<&'b dyn ContextElement> { + match self { + IceResult::Owned(rc_ce) => Some(rc_ce.as_ref()), + IceResult::Borrowed(ce) => Some(*ce), + } + } +} + +impl<'a> Walkable for IceResult<'a> { + fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { + self.get_context_element_reference().walk(segment) + } +} diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 7e86c3e..20ec150 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -46,12 +46,13 @@ impl<'a> IntoContextElement for RValue<'a> { .flatten(), RValue::RVTemplate(template) => { // TODO - renderer - .render_partial_name(template, breadcrumbs) - .map(|rendered| OwnedLiteral::LString(rendered)) - .ok() - .as_ref() - .map(|l| l as _) + // renderer + // .render_partial_name(template, breadcrumbs) + // .map(|rendered| OwnedLiteral::LString(rendered)) + // .ok() + // .as_ref() + // .map(|l| l as _) + todo!() } } } From 303f092c3096fa21ddf03df667ede78c814ef6be Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 31 May 2020 23:47:20 -0400 Subject: [PATCH 37/67] Running into lifetime issues again. --- src/renderer/context_element.rs | 13 +++++----- src/renderer/parameters_context.rs | 38 ++++++++++++++++-------------- src/renderer/renderer.rs | 11 +++++---- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index 7a2d7f4..76def94 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -106,7 +106,7 @@ pub trait IntoContextElement: Debug + Walkable /* + CloneIntoBoxedContextElement &'a self, renderer: &DustRenderer, breadcrumbs: Option<&'a BreadcrumbTree<'a>>, - ) -> Option<&'a dyn ContextElement>; + ) -> Option>; } impl IntoContextElement for C { @@ -114,8 +114,8 @@ impl IntoContextElement for C { &'a self, renderer: &DustRenderer, breadcrumbs: Option<&'a BreadcrumbTree<'a>>, - ) -> Option<&'a dyn ContextElement> { - Some(self) + ) -> Option> { + Some(IceResult::Borrowed(self)) } } @@ -143,10 +143,11 @@ impl<'a> IntoContextElement for IceResult<'a> { &'b self, renderer: &DustRenderer, breadcrumbs: Option<&'b BreadcrumbTree<'b>>, - ) -> Option<&'b dyn ContextElement> { + ) -> Option> { + // Some(*self) match self { - IceResult::Owned(rc_ce) => Some(rc_ce.as_ref()), - IceResult::Borrowed(ce) => Some(*ce), + IceResult::Owned(rc_ce) => Some(IceResult::Borrowed(rc_ce.as_ref())), + IceResult::Borrowed(ce) => Some(IceResult::Borrowed(*ce)), } } } diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 20ec150..93d66ad 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -5,6 +5,7 @@ use crate::parser::RValue; use crate::renderer::breadcrumb_tree::BreadcrumbTree; use crate::renderer::context_element::CompareContextElement; use crate::renderer::context_element::ContextElement; +use crate::renderer::context_element::IceResult; use crate::renderer::context_element::IntoContextElement; use crate::renderer::tree_walking::walk_path; use crate::renderer::DustRenderer; @@ -37,24 +38,25 @@ impl<'a> IntoContextElement for RValue<'a> { &'b self, renderer: &DustRenderer, breadcrumbs: Option<&'b BreadcrumbTree<'b>>, - ) -> Option<&'b dyn ContextElement> { - match self { - RValue::RVLiteral(owned_literal) => Some(owned_literal), - RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys) - .map(|ice| ice.into_context_element(renderer, breadcrumbs)) - .ok() - .flatten(), - RValue::RVTemplate(template) => { - // TODO - // renderer - // .render_partial_name(template, breadcrumbs) - // .map(|rendered| OwnedLiteral::LString(rendered)) - // .ok() - // .as_ref() - // .map(|l| l as _) - todo!() - } - } + ) -> Option> { + todo!() + // match self { + // RValue::RVLiteral(owned_literal) => Some(owned_literal), + // RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys) + // .map(|ice| ice.into_context_element(renderer, breadcrumbs)) + // .ok() + // .flatten(), + // RValue::RVTemplate(template) => { + // // TODO + // // renderer + // // .render_partial_name(template, breadcrumbs) + // // .map(|rendered| OwnedLiteral::LString(rendered)) + // // .ok() + // // .as_ref() + // // .map(|l| l as _) + // todo!() + // } + // } } } diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index d6319ce..a779fd9 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -19,6 +19,7 @@ use crate::renderer::inline_partial_tree::InlinePartialTreeElement; use crate::renderer::tree_walking::walk_path; use std::borrow::Borrow; use std::collections::HashMap; +use std::rc::Rc; #[derive(Clone, Debug)] pub struct DustRenderer<'a> { @@ -153,8 +154,10 @@ impl<'a> DustRenderer<'a> { match val { Err(WalkError::CantWalk) | Ok(None) => return Ok("".to_owned()), Ok(Some(final_val)) => { - return if final_val.is_truthy() { - final_val.render(&Self::preprocess_filters(&reference.filters)) + return if final_val.get_context_element_reference().is_truthy() { + final_val + .get_context_element_reference() + .render(&Self::preprocess_filters(&reference.filters)) } else { Ok("".to_owned()) }; @@ -223,8 +226,8 @@ impl<'a> DustRenderer<'a> { .ok() .flatten() .map(|val| { - if val.is_truthy() { - new_nodes.push(BreadcrumbTreeElement::Borrowed(val.from_context_element())) + if val.get_context_element_reference().is_truthy() { + new_nodes.push(BreadcrumbTreeElement::Owned(Rc::new(val))) } }); }); From 842f39e178cc8a7efb516624dfc779cfa7e6ff39 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jun 2020 15:20:43 -0400 Subject: [PATCH 38/67] Minor syntax fix. --- src/renderer/renderer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index a779fd9..f61ec48 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -296,8 +296,8 @@ impl<'a> DustRenderer<'a> { .ok() .flatten() .map(|val| { - if val.is_truthy() { - new_nodes.push(BreadcrumbTreeElement::Borrowed(val.from_context_element())) + if val.get_context_element_reference().is_truthy() { + new_nodes.push(BreadcrumbTreeElement::Owned(Rc::new(val))); } }); }); From adbc2368364ab05f49e5bb866f973e4b09e2e57f Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jun 2020 16:03:02 -0400 Subject: [PATCH 39/67] Broke the problem area down into parts to make it easier to debug. --- src/renderer/renderer.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index f61ec48..0fc98f8 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -220,7 +220,14 @@ impl<'a> DustRenderer<'a> { }; let mut new_nodes: Vec = Vec::new(); - explicit_context.as_ref().map(|path| { + let p = explicit_context.as_ref().unwrap(); + let x = walk_path(maybe_breadcrumbs, &p.keys); + let ice = x.unwrap(); + let ce = ice.into_context_element(self, maybe_breadcrumbs); + let val = ce.unwrap(); + let to_insert = BreadcrumbTreeElement::Owned(Rc::new(val)); + //new_nodes.push(BreadcrumbTreeElement::Owned(Rc::new(val))); + /*explicit_context.as_ref().map(|path| { walk_path(maybe_breadcrumbs, &path.keys) .map(|ice| ice.into_context_element(self, maybe_breadcrumbs)) .ok() @@ -230,7 +237,7 @@ impl<'a> DustRenderer<'a> { new_nodes.push(BreadcrumbTreeElement::Owned(Rc::new(val))) } }); - }); + });*/ injected_context.map(|ctx| new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx))); new_context_element .map(|ctx| new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx.from_context_element()))); @@ -287,7 +294,7 @@ impl<'a> DustRenderer<'a> { _ => new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx)), } }); - explicit_context.as_ref().map(|path| { + /*explicit_context.as_ref().map(|path| { // TODO: should resolving the value here use // explicit_context_maybe_breadcrumbs or // maybe_breadcrumbs? @@ -300,7 +307,7 @@ impl<'a> DustRenderer<'a> { new_nodes.push(BreadcrumbTreeElement::Owned(Rc::new(val))); } }); - }); + });*/ Some((parent, new_nodes)) } From e725728a6596abc2c52ba04b77490e53c06bec50 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jun 2020 17:52:46 -0400 Subject: [PATCH 40/67] I think I have solved the lifetime issue. OwnedBreadcrumbTreeElement's needed a lifetime associated with them because IntoContextElement had lifetimes associated with it. --- src/renderer/breadcrumb_tree.rs | 2 +- src/renderer/renderer.rs | 15 ++++----------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/renderer/breadcrumb_tree.rs b/src/renderer/breadcrumb_tree.rs index c22079f..7083141 100644 --- a/src/renderer/breadcrumb_tree.rs +++ b/src/renderer/breadcrumb_tree.rs @@ -13,7 +13,7 @@ pub enum BreadcrumbTreeElement<'a> { // the same BreadcrumbTreeElement but a different parent (for // example, when inserting behind the tail), we don't need to the // copy the already owned/malloc'd data. - Owned(Rc), + Owned(Rc), Borrowed(&'a dyn IntoContextElement), } diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 0fc98f8..f61ec48 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -220,14 +220,7 @@ impl<'a> DustRenderer<'a> { }; let mut new_nodes: Vec = Vec::new(); - let p = explicit_context.as_ref().unwrap(); - let x = walk_path(maybe_breadcrumbs, &p.keys); - let ice = x.unwrap(); - let ce = ice.into_context_element(self, maybe_breadcrumbs); - let val = ce.unwrap(); - let to_insert = BreadcrumbTreeElement::Owned(Rc::new(val)); - //new_nodes.push(BreadcrumbTreeElement::Owned(Rc::new(val))); - /*explicit_context.as_ref().map(|path| { + explicit_context.as_ref().map(|path| { walk_path(maybe_breadcrumbs, &path.keys) .map(|ice| ice.into_context_element(self, maybe_breadcrumbs)) .ok() @@ -237,7 +230,7 @@ impl<'a> DustRenderer<'a> { new_nodes.push(BreadcrumbTreeElement::Owned(Rc::new(val))) } }); - });*/ + }); injected_context.map(|ctx| new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx))); new_context_element .map(|ctx| new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx.from_context_element()))); @@ -294,7 +287,7 @@ impl<'a> DustRenderer<'a> { _ => new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx)), } }); - /*explicit_context.as_ref().map(|path| { + explicit_context.as_ref().map(|path| { // TODO: should resolving the value here use // explicit_context_maybe_breadcrumbs or // maybe_breadcrumbs? @@ -307,7 +300,7 @@ impl<'a> DustRenderer<'a> { new_nodes.push(BreadcrumbTreeElement::Owned(Rc::new(val))); } }); - });*/ + }); Some((parent, new_nodes)) } From c5b927ca1133bf9bb0d4c0d3994aa62850ba5985 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jun 2020 17:56:54 -0400 Subject: [PATCH 41/67] Fix bug in use of breadcrumbs in new_breadcrumbs_partial. --- src/renderer/renderer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index f61ec48..7f9aec9 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -291,7 +291,7 @@ impl<'a> DustRenderer<'a> { // TODO: should resolving the value here use // explicit_context_maybe_breadcrumbs or // maybe_breadcrumbs? - walk_path(maybe_breadcrumbs, &path.keys) + walk_path(explicit_context_maybe_breadcrumbs, &path.keys) .map(|ice| ice.into_context_element(self, maybe_breadcrumbs)) .ok() .flatten() From f4f309caad7c418c77a6cd8ca8c029232f0c12c4 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jun 2020 18:10:30 -0400 Subject: [PATCH 42/67] Working implementation of into_context_element for RValue. --- src/renderer/parameters_context.rs | 31 +++++++++++++----------------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 93d66ad..c92c7a7 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -17,6 +17,7 @@ use crate::renderer::WalkError; use crate::renderer::Walkable; use std::cmp::Ordering; use std::collections::HashMap; +use std::rc::Rc; #[derive(Debug)] pub struct ParametersContext<'a> { @@ -39,24 +40,18 @@ impl<'a> IntoContextElement for RValue<'a> { renderer: &DustRenderer, breadcrumbs: Option<&'b BreadcrumbTree<'b>>, ) -> Option> { - todo!() - // match self { - // RValue::RVLiteral(owned_literal) => Some(owned_literal), - // RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys) - // .map(|ice| ice.into_context_element(renderer, breadcrumbs)) - // .ok() - // .flatten(), - // RValue::RVTemplate(template) => { - // // TODO - // // renderer - // // .render_partial_name(template, breadcrumbs) - // // .map(|rendered| OwnedLiteral::LString(rendered)) - // // .ok() - // // .as_ref() - // // .map(|l| l as _) - // todo!() - // } - // } + match self { + RValue::RVLiteral(owned_literal) => Some(IceResult::Borrowed(owned_literal)), + RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys) + .map(|ice| ice.into_context_element(renderer, breadcrumbs)) + .ok() + .flatten(), + RValue::RVTemplate(template) => renderer + .render_partial_name(template, breadcrumbs) + .map(|rendered| OwnedLiteral::LString(rendered)) + .ok() + .map(|owned_literal| IceResult::Owned(Rc::new(owned_literal))), + } } } From 18c168706441a9973d0934e0906cd980bd77ab4c Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jun 2020 18:15:11 -0400 Subject: [PATCH 43/67] Starting the constructor for ParametersContext. --- src/renderer/parameters_context.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index c92c7a7..d703408 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -30,7 +30,17 @@ impl<'a> ParametersContext<'a> { breadcrumbs: Option<&BreadcrumbTree>, params: &Vec, ) -> Self { - todo!() + // If the parameter is a Path, then we resolve it immediately + // to a context element because those are resolved using the + // breadcrumbs at the time of assignment. + // + // If the parameter is a template (for example `foo="{bar}"`) + // then those are resolved at the time of access rather than + // the time of assignment, so we leave them into their + // original IntoContextElement state. + ParametersContext { + params: HashMap::new(), + } } } From 7253c7d99e37cfeb486c1beda42ee3e652915fc8 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jun 2020 18:37:35 -0400 Subject: [PATCH 44/67] Implement function to convert IceResult into a BreadcrumbTreeElement. --- src/renderer/breadcrumb_tree.rs | 12 ++++++++++++ src/renderer/parameters_context.rs | 25 ++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/renderer/breadcrumb_tree.rs b/src/renderer/breadcrumb_tree.rs index 7083141..b0a6993 100644 --- a/src/renderer/breadcrumb_tree.rs +++ b/src/renderer/breadcrumb_tree.rs @@ -1,3 +1,4 @@ +use crate::renderer::context_element::IceResult; use crate::renderer::context_element::IntoContextElement; use std::borrow::Borrow; use std::rc::Rc; @@ -17,6 +18,17 @@ pub enum BreadcrumbTreeElement<'a> { Borrowed(&'a dyn IntoContextElement), } +impl<'a> From<&'a IceResult<'a>> for BreadcrumbTreeElement<'a> { + fn from(inp: &'a IceResult<'a>) -> Self { + match inp { + IceResult::Owned(rc_ce) => { + BreadcrumbTreeElement::Borrowed(rc_ce.from_context_element()) + } + IceResult::Borrowed(ce) => BreadcrumbTreeElement::Borrowed(ce.from_context_element()), + } + } +} + impl<'a> BreadcrumbTree<'a> { pub fn new(parent: Option<&'a BreadcrumbTree>, element: BreadcrumbTreeElement<'a>) -> Self { BreadcrumbTree { diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index d703408..653443a 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -27,8 +27,8 @@ pub struct ParametersContext<'a> { impl<'a> ParametersContext<'a> { pub fn new( renderer: &DustRenderer, - breadcrumbs: Option<&BreadcrumbTree>, - params: &Vec, + breadcrumbs: Option<&'a BreadcrumbTree>, + params: &'a Vec, ) -> Self { // If the parameter is a Path, then we resolve it immediately // to a context element because those are resolved using the @@ -38,8 +38,27 @@ impl<'a> ParametersContext<'a> { // then those are resolved at the time of access rather than // the time of assignment, so we leave them into their // original IntoContextElement state. + let rendered_params = params + .iter() + .map(|kvpair| { + let k = kvpair.key; + let v: &dyn IntoContextElement = match &kvpair.value { + RValue::RVLiteral(owned_literal) => &kvpair.value, + /*RValue::RVPath(path) => kvpair + .value + .into_context_element(renderer, breadcrumbs) + .unwrap() + .get_context_element_reference() + .from_context_element(),*/ + RValue::RVPath(path) => todo!(), + RValue::RVTemplate(template) => todo!(), + }; + (k, v) + }) + .collect(); + ParametersContext { - params: HashMap::new(), + params: rendered_params, } } } From f9dea70d231f84f35a55c1eeb6d3df8c5fc11791 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jun 2020 19:39:44 -0400 Subject: [PATCH 45/67] Implement conversion from IceResult into BreadcrumbTreeNode. I believe this change will remove an extra heap allocation I was doing in the new_breadcrumbs_* functions for the explicit context by adding support for converting from Rc to Rc without copying the underlying data. This should allow conversion of the IceResult::Owned variant to the BreadcrumbTreeElement::Owned variant without extra copying. --- src/renderer/breadcrumb_tree.rs | 11 +++++++++++ src/renderer/context_element.rs | 11 +++++++++++ src/renderer/parameters_context.rs | 11 +++++++---- src/renderer/renderer.rs | 4 ++-- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/renderer/breadcrumb_tree.rs b/src/renderer/breadcrumb_tree.rs index b0a6993..ec83f4f 100644 --- a/src/renderer/breadcrumb_tree.rs +++ b/src/renderer/breadcrumb_tree.rs @@ -1,5 +1,7 @@ +use crate::renderer::context_element::ContextElement; use crate::renderer::context_element::IceResult; use crate::renderer::context_element::IntoContextElement; +use crate::renderer::context_element::IntoRcIce; use std::borrow::Borrow; use std::rc::Rc; @@ -29,6 +31,15 @@ impl<'a> From<&'a IceResult<'a>> for BreadcrumbTreeElement<'a> { } } +impl<'a> From> for BreadcrumbTreeElement<'a> { + fn from(inp: IceResult<'a>) -> Self { + match inp { + IceResult::Owned(rc_ce) => BreadcrumbTreeElement::Owned(rc_ce.into_rc_ice()), + IceResult::Borrowed(ce) => BreadcrumbTreeElement::Borrowed(ce.from_context_element()), + } + } +} + impl<'a> BreadcrumbTree<'a> { pub fn new(parent: Option<&'a BreadcrumbTree>, element: BreadcrumbTreeElement<'a>) -> Self { BreadcrumbTree { diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index 76def94..15b5a58 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -16,6 +16,7 @@ pub trait ContextElement: // + CloneIntoBoxedContextElement + CompareContextElement + FromContextElement + + IntoRcIce { } @@ -119,6 +120,16 @@ impl IntoContextElement for C { } } +pub trait IntoRcIce { + fn into_rc_ice(self: Rc) -> Rc; +} + +impl IntoRcIce for C { + fn into_rc_ice(self: Rc) -> Rc { + Rc::clone(&self) as Rc + } +} + #[derive(Clone, Debug)] pub enum IceResult<'a> { // Using Rc so that when we need to create BreadcrumbTrees with diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 653443a..e614adc 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -3,6 +3,7 @@ use crate::parser::KVPair; use crate::parser::OwnedLiteral; use crate::parser::RValue; use crate::renderer::breadcrumb_tree::BreadcrumbTree; +use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement; use crate::renderer::context_element::CompareContextElement; use crate::renderer::context_element::ContextElement; use crate::renderer::context_element::IceResult; @@ -21,7 +22,7 @@ use std::rc::Rc; #[derive(Debug)] pub struct ParametersContext<'a> { - params: HashMap<&'a str, &'a dyn IntoContextElement>, + params: HashMap<&'a str, BreadcrumbTreeElement<'a>>, } impl<'a> ParametersContext<'a> { @@ -38,12 +39,14 @@ impl<'a> ParametersContext<'a> { // then those are resolved at the time of access rather than // the time of assignment, so we leave them into their // original IntoContextElement state. - let rendered_params = params + let rendered_params: HashMap<&'a str, BreadcrumbTreeElement<'a>> = params .iter() .map(|kvpair| { let k = kvpair.key; - let v: &dyn IntoContextElement = match &kvpair.value { - RValue::RVLiteral(owned_literal) => &kvpair.value, + let v: BreadcrumbTreeElement<'a> = match &kvpair.value { + RValue::RVLiteral(owned_literal) => { + BreadcrumbTreeElement::Borrowed(&kvpair.value) + } /*RValue::RVPath(path) => kvpair .value .into_context_element(renderer, breadcrumbs) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 7f9aec9..d0e0355 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -227,7 +227,7 @@ impl<'a> DustRenderer<'a> { .flatten() .map(|val| { if val.get_context_element_reference().is_truthy() { - new_nodes.push(BreadcrumbTreeElement::Owned(Rc::new(val))) + new_nodes.push(std::convert::From::from(val)) } }); }); @@ -297,7 +297,7 @@ impl<'a> DustRenderer<'a> { .flatten() .map(|val| { if val.get_context_element_reference().is_truthy() { - new_nodes.push(BreadcrumbTreeElement::Owned(Rc::new(val))); + new_nodes.push(std::convert::From::from(val)); } }); }); From ed7d80de6c95d7e6b9a5966bc2b9271ed780ec70 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jun 2020 19:57:33 -0400 Subject: [PATCH 46/67] Handling RVPath for ParametersContext constructor. --- src/renderer/parameters_context.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index e614adc..ade5aa2 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -43,21 +43,19 @@ impl<'a> ParametersContext<'a> { .iter() .map(|kvpair| { let k = kvpair.key; - let v: BreadcrumbTreeElement<'a> = match &kvpair.value { + let v: Option> = match &kvpair.value { RValue::RVLiteral(owned_literal) => { - BreadcrumbTreeElement::Borrowed(&kvpair.value) + Some(BreadcrumbTreeElement::Borrowed(&kvpair.value)) } - /*RValue::RVPath(path) => kvpair - .value - .into_context_element(renderer, breadcrumbs) - .unwrap() - .get_context_element_reference() - .from_context_element(),*/ - RValue::RVPath(path) => todo!(), + RValue::RVPath(path) => kvpair + .value + .into_context_element(renderer, breadcrumbs) + .map(std::convert::From::from), RValue::RVTemplate(template) => todo!(), }; - (k, v) + v.map(|some_v| (k, some_v)) }) + .filter_map(|pair| pair) .collect(); ParametersContext { From 3c15e35b67882be826c7fbfb204a8d7644be8b2b Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jun 2020 20:02:50 -0400 Subject: [PATCH 47/67] Finished constructor for ParametersContext. --- src/renderer/parameters_context.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index ade5aa2..0d27efe 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -51,7 +51,9 @@ impl<'a> ParametersContext<'a> { .value .into_context_element(renderer, breadcrumbs) .map(std::convert::From::from), - RValue::RVTemplate(template) => todo!(), + RValue::RVTemplate(template) => { + Some(BreadcrumbTreeElement::Borrowed(&kvpair.value)) + } }; v.map(|some_v| (k, some_v)) }) From 02259b9bd673c1fdfef882e9124c8d6d11bbd262 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jun 2020 20:24:01 -0400 Subject: [PATCH 48/67] Create from_borrowed and from_owned constructors for IceResult and BreadcrumbTreeElement. In an effort to keep track of how often memory is getting heap allocated, I am implementing constructors for IceResult and BreadcrumbTreeElement. This should limit the places that I call Rc::new and allow me to place tracing code into it later to ensure all code paths doing heap allocation make sense. --- src/renderer/breadcrumb_tree.rs | 20 +++++++++++++++++--- src/renderer/context_element.rs | 15 +++++++++++---- src/renderer/parameters_context.rs | 8 ++++---- src/renderer/renderer.rs | 15 +++++++++------ 4 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/renderer/breadcrumb_tree.rs b/src/renderer/breadcrumb_tree.rs index ec83f4f..6aeba51 100644 --- a/src/renderer/breadcrumb_tree.rs +++ b/src/renderer/breadcrumb_tree.rs @@ -20,13 +20,25 @@ pub enum BreadcrumbTreeElement<'a> { Borrowed(&'a dyn IntoContextElement), } +impl<'a> BreadcrumbTreeElement<'a> { + pub fn from_owned(val: I) -> BreadcrumbTreeElement<'a> { + BreadcrumbTreeElement::Owned(Rc::new(val)) + } + + pub fn from_borrowed(val: &'a dyn IntoContextElement) -> BreadcrumbTreeElement<'a> { + BreadcrumbTreeElement::Borrowed(val) + } +} + impl<'a> From<&'a IceResult<'a>> for BreadcrumbTreeElement<'a> { fn from(inp: &'a IceResult<'a>) -> Self { match inp { IceResult::Owned(rc_ce) => { - BreadcrumbTreeElement::Borrowed(rc_ce.from_context_element()) + BreadcrumbTreeElement::from_borrowed(rc_ce.from_context_element()) + } + IceResult::Borrowed(ce) => { + BreadcrumbTreeElement::from_borrowed(ce.from_context_element()) } - IceResult::Borrowed(ce) => BreadcrumbTreeElement::Borrowed(ce.from_context_element()), } } } @@ -35,7 +47,9 @@ impl<'a> From> for BreadcrumbTreeElement<'a> { fn from(inp: IceResult<'a>) -> Self { match inp { IceResult::Owned(rc_ce) => BreadcrumbTreeElement::Owned(rc_ce.into_rc_ice()), - IceResult::Borrowed(ce) => BreadcrumbTreeElement::Borrowed(ce.from_context_element()), + IceResult::Borrowed(ce) => { + BreadcrumbTreeElement::from_borrowed(ce.from_context_element()) + } } } } diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index 15b5a58..32f005d 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -116,7 +116,7 @@ impl IntoContextElement for C { renderer: &DustRenderer, breadcrumbs: Option<&'a BreadcrumbTree<'a>>, ) -> Option> { - Some(IceResult::Borrowed(self)) + Some(IceResult::from_borrowed(self)) } } @@ -141,6 +141,14 @@ pub enum IceResult<'a> { } impl<'a> IceResult<'a> { + pub fn from_owned(val: C) -> IceResult<'a> { + IceResult::Owned(Rc::new(val)) + } + + pub fn from_borrowed(val: &'a dyn ContextElement) -> IceResult<'a> { + IceResult::Borrowed(val) + } + pub fn get_context_element_reference(&self) -> &dyn ContextElement { match self { IceResult::Owned(rc_ce) => rc_ce.as_ref(), @@ -155,10 +163,9 @@ impl<'a> IntoContextElement for IceResult<'a> { renderer: &DustRenderer, breadcrumbs: Option<&'b BreadcrumbTree<'b>>, ) -> Option> { - // Some(*self) match self { - IceResult::Owned(rc_ce) => Some(IceResult::Borrowed(rc_ce.as_ref())), - IceResult::Borrowed(ce) => Some(IceResult::Borrowed(*ce)), + IceResult::Owned(rc_ce) => Some(IceResult::from_borrowed(rc_ce.as_ref())), + IceResult::Borrowed(ce) => Some(IceResult::from_borrowed(*ce)), } } } diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 0d27efe..d421fd6 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -45,14 +45,14 @@ impl<'a> ParametersContext<'a> { let k = kvpair.key; let v: Option> = match &kvpair.value { RValue::RVLiteral(owned_literal) => { - Some(BreadcrumbTreeElement::Borrowed(&kvpair.value)) + Some(BreadcrumbTreeElement::from_borrowed(&kvpair.value)) } RValue::RVPath(path) => kvpair .value .into_context_element(renderer, breadcrumbs) .map(std::convert::From::from), RValue::RVTemplate(template) => { - Some(BreadcrumbTreeElement::Borrowed(&kvpair.value)) + Some(BreadcrumbTreeElement::from_borrowed(&kvpair.value)) } }; v.map(|some_v| (k, some_v)) @@ -73,7 +73,7 @@ impl<'a> IntoContextElement for RValue<'a> { breadcrumbs: Option<&'b BreadcrumbTree<'b>>, ) -> Option> { match self { - RValue::RVLiteral(owned_literal) => Some(IceResult::Borrowed(owned_literal)), + RValue::RVLiteral(owned_literal) => Some(IceResult::from_borrowed(owned_literal)), RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys) .map(|ice| ice.into_context_element(renderer, breadcrumbs)) .ok() @@ -82,7 +82,7 @@ impl<'a> IntoContextElement for RValue<'a> { .render_partial_name(template, breadcrumbs) .map(|rendered| OwnedLiteral::LString(rendered)) .ok() - .map(|owned_literal| IceResult::Owned(Rc::new(owned_literal))), + .map(|owned_literal| IceResult::from_owned(owned_literal)), } } } diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index d0e0355..83864e6 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -49,7 +49,7 @@ impl<'a> DustRenderer<'a> { C: IntoContextElement, { let breadcrumbs = - context.map(|ctx| BreadcrumbTree::new(None, BreadcrumbTreeElement::Borrowed(ctx))); + context.map(|ctx| BreadcrumbTree::new(None, BreadcrumbTreeElement::from_borrowed(ctx))); self.render_template(name, breadcrumbs.as_ref(), None) } @@ -231,10 +231,13 @@ impl<'a> DustRenderer<'a> { } }); }); - injected_context.map(|ctx| new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx))); - new_context_element - .map(|ctx| new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx.from_context_element()))); - index_context.map(|ctx| new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx))); + injected_context.map(|ctx| new_nodes.push(BreadcrumbTreeElement::from_borrowed(ctx))); + new_context_element.map(|ctx| { + new_nodes.push(BreadcrumbTreeElement::from_borrowed( + ctx.from_context_element(), + )) + }); + index_context.map(|ctx| new_nodes.push(BreadcrumbTreeElement::from_borrowed(ctx))); Some((parent, new_nodes)) } @@ -284,7 +287,7 @@ impl<'a> DustRenderer<'a> { parent = new_parent; new_nodes.extend(passed_nodes.iter().map(|b| b.get_element().clone())); } - _ => new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx)), + _ => new_nodes.push(BreadcrumbTreeElement::from_borrowed(ctx)), } }); explicit_context.as_ref().map(|path| { From 77b842f8dee6e9cdf89f0e517a4c6b0379879e9e Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jun 2020 20:48:29 -0400 Subject: [PATCH 49/67] Implement IntoContextElement for ParametersContext. --- src/renderer/parameters_context.rs | 25 +++++++++++++++++++++++++ src/renderer/renderer.rs | 17 ++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index d421fd6..f286e68 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -16,6 +16,7 @@ use crate::renderer::Renderable; use crate::renderer::Truthiness; use crate::renderer::WalkError; use crate::renderer::Walkable; +use std::borrow::Borrow; use std::cmp::Ordering; use std::collections::HashMap; use std::rc::Rc; @@ -57,6 +58,7 @@ impl<'a> ParametersContext<'a> { }; v.map(|some_v| (k, some_v)) }) + // TODO: Should a None value here be the same as a key not existing, or should we store the Nones? .filter_map(|pair| pair) .collect(); @@ -66,6 +68,29 @@ impl<'a> ParametersContext<'a> { } } +impl<'a> IntoContextElement for ParametersContext<'a> { + fn into_context_element<'b>( + &'b self, + renderer: &DustRenderer, + breadcrumbs: Option<&'b BreadcrumbTree<'b>>, + ) -> Option> { + panic!("into_context_element cannot be called on pseudo elements"); + } +} + +impl<'a> Walkable for ParametersContext<'a> { + fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { + self.params + .get(segment) + .map(|bte| bte.borrow()) + .ok_or(WalkError::CantWalk) + } + + fn is_pseudo_element(&self) -> bool { + true + } +} + impl<'a> IntoContextElement for RValue<'a> { fn into_context_element<'b>( &'b self, diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 83864e6..4ee04fb 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -16,6 +16,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::tree_walking::walk_path; use std::borrow::Borrow; use std::collections::HashMap; @@ -165,12 +166,19 @@ impl<'a> DustRenderer<'a> { } } DustTag::DTSection(container) => { - //let injected_context = ParametersContext::new(breadcrumbs, &container.params); + let injected_context = ParametersContext::new(self, breadcrumbs, &container.params); let val = walk_path(breadcrumbs, &container.path.keys) .map(|ice| ice.into_context_element(self, breadcrumbs)); match val { Err(WalkError::CantWalk) => { // TODO + let new_breadcrumbs = self.new_breadcrumbs_section( + breadcrumbs, + None, + Some(&injected_context), + &container.explicit_context, + None, + ); } Ok(final_val) => { // TODO @@ -348,6 +356,13 @@ impl<'a> DustRenderer<'a> { } final_filters } + + fn append_new_elements_onto_tree<'b>( + original_parent: Option<&'b BreadcrumbTree>, + new_elements: Vec>, + ) -> Option> { + todo!() + } } struct BlockContext<'a> { From 78bffb5f04a9b0d590828002609c9e38859ca1ee Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jun 2020 21:46:02 -0400 Subject: [PATCH 50/67] I think I need to switch back to a vec. But with all I've built with IntoContextElement and BreadcrumbTreeElement I think the Vec will end up working in the end. --- src/renderer/renderer.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 4ee04fb..428d1c3 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -356,13 +356,6 @@ impl<'a> DustRenderer<'a> { } final_filters } - - fn append_new_elements_onto_tree<'b>( - original_parent: Option<&'b BreadcrumbTree>, - new_elements: Vec>, - ) -> Option> { - todo!() - } } struct BlockContext<'a> { From 71592a9a324ae1eae2033b138933e3590bd57c23 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jun 2020 22:24:27 -0400 Subject: [PATCH 51/67] Switching back to a Vec because inserting multiple elements into the linked list structure while maintaining ownership of each node proved to be difficult. --- src/renderer/breadcrumb_tree.rs | 63 ----------------------- src/renderer/context_element.rs | 8 +-- src/renderer/parameters_context.rs | 7 ++- src/renderer/renderer.rs | 81 ++++++++---------------------- src/renderer/tree_walking.rs | 33 ++++++++---- 5 files changed, 50 insertions(+), 142 deletions(-) diff --git a/src/renderer/breadcrumb_tree.rs b/src/renderer/breadcrumb_tree.rs index 6aeba51..fd70bd0 100644 --- a/src/renderer/breadcrumb_tree.rs +++ b/src/renderer/breadcrumb_tree.rs @@ -5,11 +5,6 @@ use crate::renderer::context_element::IntoRcIce; use std::borrow::Borrow; use std::rc::Rc; -pub struct BreadcrumbTree<'a> { - parent: Option<&'a BreadcrumbTree<'a>>, - element: BreadcrumbTreeElement<'a>, -} - #[derive(Clone, Debug)] pub enum BreadcrumbTreeElement<'a> { // Using Rc so that when we need to create BreadcrumbTrees with @@ -54,43 +49,6 @@ impl<'a> From> for BreadcrumbTreeElement<'a> { } } -impl<'a> BreadcrumbTree<'a> { - pub fn new(parent: Option<&'a BreadcrumbTree>, element: BreadcrumbTreeElement<'a>) -> Self { - BreadcrumbTree { - parent: parent, - element: element, - } - } - - pub fn get_ice(&self) -> &dyn IntoContextElement { - self.element.borrow() - } - - pub fn get_parent(&self) -> Option<&BreadcrumbTree> { - self.parent - } - - pub fn get_element(&self) -> &BreadcrumbTreeElement<'a> { - &self.element - } - - pub fn ice_iter(&'a self) -> impl Iterator { - self.breadcrumb_iter().map(|b| b.get_ice()) - } - - pub fn breadcrumb_iter(&'a self) -> BreadcrumbTreeIterator<'a> { - BreadcrumbTreeIterator(Some(self)) - } - - pub fn clone_to_new_parent(&self, parent: Option<&'a BreadcrumbTree>) -> Self { - // TODO: Maybe not needed anymore? - BreadcrumbTree { - parent: parent, - element: self.element.clone(), - } - } -} - impl<'a> Borrow for BreadcrumbTreeElement<'a> { fn borrow(&self) -> &(dyn IntoContextElement + 'a) { match self { @@ -99,24 +57,3 @@ impl<'a> Borrow for BreadcrumbTreeElement<'a> { } } } - -impl<'a> IntoIterator for &'a BreadcrumbTree<'a> { - type Item = &'a BreadcrumbTree<'a>; - type IntoIter = BreadcrumbTreeIterator<'a>; - - fn into_iter(self) -> BreadcrumbTreeIterator<'a> { - self.breadcrumb_iter() - } -} - -pub struct BreadcrumbTreeIterator<'a>(Option<&'a BreadcrumbTree<'a>>); - -impl<'a> Iterator for BreadcrumbTreeIterator<'a> { - type Item = &'a BreadcrumbTree<'a>; - - fn next(&mut self) -> Option { - let ret = self.0; - self.0 = self.0.map(|node| node.get_parent()).flatten(); - ret - } -} diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index 32f005d..8be76b9 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -1,5 +1,5 @@ use crate::parser::Filter; -use crate::renderer::breadcrumb_tree::BreadcrumbTree; +use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement; use crate::renderer::errors::RenderError; use crate::renderer::errors::WalkError; use crate::renderer::DustRenderer; @@ -106,7 +106,7 @@ pub trait IntoContextElement: Debug + Walkable /* + CloneIntoBoxedContextElement fn into_context_element<'a>( &'a self, renderer: &DustRenderer, - breadcrumbs: Option<&'a BreadcrumbTree<'a>>, + breadcrumbs: &'a Vec>, ) -> Option>; } @@ -114,7 +114,7 @@ impl IntoContextElement for C { fn into_context_element<'a>( &'a self, renderer: &DustRenderer, - breadcrumbs: Option<&'a BreadcrumbTree<'a>>, + breadcrumbs: &'a Vec>, ) -> Option> { Some(IceResult::from_borrowed(self)) } @@ -161,7 +161,7 @@ impl<'a> IntoContextElement for IceResult<'a> { fn into_context_element<'b>( &'b self, renderer: &DustRenderer, - breadcrumbs: Option<&'b BreadcrumbTree<'b>>, + breadcrumbs: &'b Vec>, ) -> Option> { match self { IceResult::Owned(rc_ce) => Some(IceResult::from_borrowed(rc_ce.as_ref())), diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index f286e68..c4b65e5 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -2,7 +2,6 @@ use crate::parser::Filter; use crate::parser::KVPair; use crate::parser::OwnedLiteral; use crate::parser::RValue; -use crate::renderer::breadcrumb_tree::BreadcrumbTree; use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement; use crate::renderer::context_element::CompareContextElement; use crate::renderer::context_element::ContextElement; @@ -29,7 +28,7 @@ pub struct ParametersContext<'a> { impl<'a> ParametersContext<'a> { pub fn new( renderer: &DustRenderer, - breadcrumbs: Option<&'a BreadcrumbTree>, + breadcrumbs: &'a Vec>, params: &'a Vec, ) -> Self { // If the parameter is a Path, then we resolve it immediately @@ -72,7 +71,7 @@ impl<'a> IntoContextElement for ParametersContext<'a> { fn into_context_element<'b>( &'b self, renderer: &DustRenderer, - breadcrumbs: Option<&'b BreadcrumbTree<'b>>, + breadcrumbs: &'b Vec>, ) -> Option> { panic!("into_context_element cannot be called on pseudo elements"); } @@ -95,7 +94,7 @@ impl<'a> IntoContextElement for RValue<'a> { fn into_context_element<'b>( &'b self, renderer: &DustRenderer, - breadcrumbs: Option<&'b BreadcrumbTree<'b>>, + breadcrumbs: &'b Vec>, ) -> Option> { match self { RValue::RVLiteral(owned_literal) => Some(IceResult::from_borrowed(owned_literal)), diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 428d1c3..0a15100 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -7,7 +7,6 @@ use crate::parser::Path; use crate::parser::Special; use crate::parser::Template; use crate::parser::TemplateElement; -use crate::renderer::breadcrumb_tree::BreadcrumbTree; use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement; use crate::renderer::context_element::ContextElement; use crate::renderer::context_element::IntoContextElement; @@ -49,15 +48,16 @@ impl<'a> DustRenderer<'a> { where C: IntoContextElement, { - let breadcrumbs = - context.map(|ctx| BreadcrumbTree::new(None, BreadcrumbTreeElement::from_borrowed(ctx))); + let breadcrumbs = context + .map(|ctx| vec![BreadcrumbTreeElement::from_borrowed(ctx)]) + .unwrap_or(Vec::new()); self.render_template(name, breadcrumbs.as_ref(), None) } pub fn render_template( &'a self, name: &str, - breadcrumbs: Option<&'a BreadcrumbTree>, + breadcrumbs: &'a Vec>, blocks: Option<&'a InlinePartialTreeElement<'a>>, ) -> Result { let main_template = match self.templates.get(name) { @@ -78,7 +78,7 @@ impl<'a> DustRenderer<'a> { fn render_maybe_body( &'a self, body: &'a Option, - breadcrumbs: Option<&'a BreadcrumbTree>, + breadcrumbs: &'a Vec>, blocks: &'a BlockContext<'a>, ) -> Result { match body { @@ -90,7 +90,7 @@ impl<'a> DustRenderer<'a> { fn render_body( &'a self, body: &'a Body, - breadcrumbs: Option<&'a BreadcrumbTree>, + breadcrumbs: &'a Vec>, blocks: &'a BlockContext<'a>, ) -> Result { let mut output = String::new(); @@ -110,7 +110,7 @@ impl<'a> DustRenderer<'a> { pub fn render_partial_name( &'a self, body: &'a Vec, - breadcrumbs: Option<&'a BreadcrumbTree>, + breadcrumbs: &'a Vec>, ) -> Result { let converted_to_template_elements: Vec> = body.into_iter().map(|e| e.into()).collect(); @@ -118,7 +118,7 @@ impl<'a> DustRenderer<'a> { // cannot contain blocks or inline partials, so we use a blank // BlockContext. let empty_block_context = BlockContext { - breadcrumbs: None, + breadcrumbs: &Vec::new(), blocks: &InlinePartialTreeElement::new(None, HashMap::new()), }; self.render_body( @@ -133,7 +133,7 @@ impl<'a> DustRenderer<'a> { fn render_tag( &'a self, tag: &'a DustTag, - breadcrumbs: Option<&'a BreadcrumbTree>, + breadcrumbs: &'a Vec>, blocks: &'a BlockContext<'a>, ) -> Result { match tag { @@ -190,23 +190,15 @@ impl<'a> DustRenderer<'a> { Ok("".to_owned()) } - /// Returns a option of a tuple of (parent, new_node_elements) - /// which can then be formed into new BreadcrumbTreeNodes - /// - /// If None is returned, then it is a signal to simply re-use the - /// existing breadcrumbs. - /// - /// Otherwise, the parent (which may be None, especially for - /// explicit contexts) and the additional node elements (which may - /// be empty) should be combined into a final BreadcrumbTreeNode fn new_breadcrumbs_section<'b>( &'b self, - maybe_breadcrumbs: Option<&'b BreadcrumbTree>, + maybe_breadcrumbs: &'b Vec>, index_context: Option<&'b dyn IntoContextElement>, injected_context: Option<&'b dyn IntoContextElement>, explicit_context: &Option>, new_context_element: Option<&'b dyn ContextElement>, - ) -> Option<(Option<&'b BreadcrumbTree>, Vec>)> { + ) { + /* // If none of the additional contexts are present, return None // to signal that the original breadcrumbs should be used // rather than incurring a copy here. @@ -248,24 +240,18 @@ impl<'a> DustRenderer<'a> { index_context.map(|ctx| new_nodes.push(BreadcrumbTreeElement::from_borrowed(ctx))); Some((parent, new_nodes)) + */ + todo!() } - /// Returns a option of a tuple of (parent, new_node_elements) - /// which can then be formed into new BreadcrumbTreeNodes - /// - /// If None is returned, then it is a signal to simply re-use the - /// existing breadcrumbs. - /// - /// Otherwise, the parent (which may be None, especially for - /// explicit contexts) and the additional node elements (which may - /// be empty) should be combined into a final BreadcrumbTreeNode fn new_breadcrumbs_partial<'b>( &'b self, - maybe_breadcrumbs: Option<&'b BreadcrumbTree>, - explicit_context_maybe_breadcrumbs: Option<&'b BreadcrumbTree>, + maybe_breadcrumbs: &'b Vec>, + explicit_context_maybe_breadcrumbs: &'a Vec>, injected_context: Option<&'b dyn IntoContextElement>, explicit_context: &Option>, - ) -> Option<(Option<&'b BreadcrumbTree>, Vec>)> { + ) { + /* // If none of the additional contexts are present, return None // to signal that the original breadcrumbs should be used // rather than incurring a copy here. @@ -313,33 +299,8 @@ impl<'a> DustRenderer<'a> { }); }); - Some((parent, new_nodes)) - } - - /// Returns a Breadcrumb tree where all the bottom nodes that do - /// not match the predicate and the first node that match the - /// predicate are shaved off, and a list of those nodes that are - /// shaved off. - fn split_tree_at_predicate<'b, F>( - maybe_breadcrumbs: Option<&'b BreadcrumbTree>, - f: F, - ) -> (Option<&'b BreadcrumbTree<'b>>, Vec<&'b BreadcrumbTree<'b>>) - where - F: Fn(&'b BreadcrumbTree) -> bool, - { - match maybe_breadcrumbs { - None => return (None, Vec::new()), - Some(breadcrumbs) => { - let mut passed_nodes: Vec<&'b BreadcrumbTree<'b>> = Vec::new(); - for tree_node in breadcrumbs { - passed_nodes.push(tree_node); - if f(tree_node) { - return (tree_node.get_parent(), passed_nodes); - } - } - return (None, passed_nodes); - } - } + Some((parent, new_nodes))*/ + todo!() } fn preprocess_filters(filters: &Vec) -> Vec { @@ -360,6 +321,6 @@ impl<'a> DustRenderer<'a> { struct BlockContext<'a> { /// The breadcrumbs at the time of entering the current partial - breadcrumbs: Option<&'a BreadcrumbTree<'a>>, + breadcrumbs: &'a Vec>, blocks: &'a InlinePartialTreeElement<'a>, } diff --git a/src/renderer/tree_walking.rs b/src/renderer/tree_walking.rs index a37c208..98d1793 100644 --- a/src/renderer/tree_walking.rs +++ b/src/renderer/tree_walking.rs @@ -1,4 +1,4 @@ -use crate::renderer::breadcrumb_tree::BreadcrumbTree; +use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement; use crate::renderer::context_element::IntoContextElement; use crate::renderer::WalkError; use std::borrow::Borrow; @@ -38,25 +38,34 @@ where } fn get_first_non_pseudo_element<'a>( - breadcrumbs: &'a BreadcrumbTree, -) -> Option<&'a BreadcrumbTree<'a>> { + breadcrumbs: &'a Vec>, +) -> Option<&'a BreadcrumbTreeElement<'a>> { breadcrumbs - .breadcrumb_iter() - .filter(|b| b.get_ice().is_pseudo_element()) + .iter() + .rev() + .filter(|b| { + std::borrow::Borrow::::borrow(*b).is_pseudo_element() + }) .next() } pub fn walk_path<'a, P>( - maybe_breadcrumbs: Option<&'a BreadcrumbTree>, + breadcrumbs: &'a Vec>, path: &Vec

, ) -> Result<&'a dyn IntoContextElement, WalkError> where P: Borrow, { - match (maybe_breadcrumbs, path.first()) { - (None, _) => return Err(WalkError::CantWalk), - (Some(breadcrumbs), None) => return Ok(breadcrumbs.get_ice()), - (Some(breadcrumbs), Some(path_first)) if path_first.borrow() == "." => { + /* + match (breadcrumbs.is_empty(), path.first()) { + (true, _) => return Err(WalkError::CantWalk), + (false, None) => { + return breadcrumbs + .last() + .map(|bte| bte.borrow()) + .ok_or(WalkError::CantWalk) + } + (false, Some(path_first)) if path_first.borrow() == "." => { let first_non_pseudo_element = get_first_non_pseudo_element(breadcrumbs); return match first_non_pseudo_element { None => Err(WalkError::CantWalk), @@ -71,7 +80,7 @@ where } }; } - (Some(breadcrumbs), Some(path_first)) => { + (false, Some(path_first)) => { for context in breadcrumbs.ice_iter() { match walk_path_from_single_level(context, path) { // If no walking was done at all, keep looping @@ -88,4 +97,6 @@ where } Err(WalkError::CantWalk) + */ + todo!() } From 256dcd03c5269a46b07ce35862202685c1245f4c Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jun 2020 22:29:39 -0400 Subject: [PATCH 52/67] Fixed tree_walking for the new breadcrumbs. --- src/renderer/tree_walking.rs | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/renderer/tree_walking.rs b/src/renderer/tree_walking.rs index 98d1793..dc2892b 100644 --- a/src/renderer/tree_walking.rs +++ b/src/renderer/tree_walking.rs @@ -56,21 +56,15 @@ pub fn walk_path<'a, P>( where P: Borrow, { - /* - match (breadcrumbs.is_empty(), path.first()) { - (true, _) => return Err(WalkError::CantWalk), - (false, None) => { - return breadcrumbs - .last() - .map(|bte| bte.borrow()) - .ok_or(WalkError::CantWalk) - } - (false, Some(path_first)) if path_first.borrow() == "." => { + match (breadcrumbs.last(), path.first()) { + (None, _) => return Err(WalkError::CantWalk), + (Some(last_elem), None) => return Ok(last_elem.borrow()), + (Some(_), Some(path_first)) if path_first.borrow() == "." => { let first_non_pseudo_element = get_first_non_pseudo_element(breadcrumbs); return match first_non_pseudo_element { None => Err(WalkError::CantWalk), Some(current_context) => { - match walk_path_from_single_level(current_context.get_ice(), &path[1..]) { + match walk_path_from_single_level(current_context.borrow(), &path[1..]) { // If no walking was done at all or we partially walked // then stop trying to find anything because '.' restricts // us to the current scope @@ -80,9 +74,9 @@ where } }; } - (false, Some(path_first)) => { - for context in breadcrumbs.ice_iter() { - match walk_path_from_single_level(context, path) { + (Some(_), Some(path_first)) => { + for context in breadcrumbs.iter().rev() { + match walk_path_from_single_level(context.borrow(), path) { // If no walking was done at all, keep looping WalkResult::NoWalk => {} // If we partially walked then stop trying to find @@ -97,6 +91,4 @@ where } Err(WalkError::CantWalk) - */ - todo!() } From b0efe504109d1acef30d9c07214f7023b9d890b9 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jun 2020 22:37:29 -0400 Subject: [PATCH 53/67] Fixed new_breadcrumbs_section for the Vec based breadcrumbs. --- src/renderer/renderer.rs | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 0a15100..93192c9 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -192,13 +192,12 @@ impl<'a> DustRenderer<'a> { fn new_breadcrumbs_section<'b>( &'b self, - maybe_breadcrumbs: &'b Vec>, + breadcrumbs: &'b Vec>, index_context: Option<&'b dyn IntoContextElement>, injected_context: Option<&'b dyn IntoContextElement>, explicit_context: &Option>, new_context_element: Option<&'b dyn ContextElement>, - ) { - /* + ) -> Option>> { // If none of the additional contexts are present, return None // to signal that the original breadcrumbs should be used // rather than incurring a copy here. @@ -214,34 +213,31 @@ impl<'a> DustRenderer<'a> { // If there is an explicit context, then drop all the current // context - let parent = match explicit_context { - Some(_) => None, - None => maybe_breadcrumbs, + let mut new_stack = match explicit_context { + Some(_) => Vec::with_capacity(4), + None => breadcrumbs.clone(), }; - let mut new_nodes: Vec = Vec::new(); explicit_context.as_ref().map(|path| { - walk_path(maybe_breadcrumbs, &path.keys) - .map(|ice| ice.into_context_element(self, maybe_breadcrumbs)) + walk_path(breadcrumbs, &path.keys) + .map(|ice| ice.into_context_element(self, breadcrumbs)) .ok() .flatten() .map(|val| { if val.get_context_element_reference().is_truthy() { - new_nodes.push(std::convert::From::from(val)) + new_stack.push(std::convert::From::from(val)) } }); }); - injected_context.map(|ctx| new_nodes.push(BreadcrumbTreeElement::from_borrowed(ctx))); + injected_context.map(|ctx| new_stack.push(BreadcrumbTreeElement::from_borrowed(ctx))); new_context_element.map(|ctx| { - new_nodes.push(BreadcrumbTreeElement::from_borrowed( + new_stack.push(BreadcrumbTreeElement::from_borrowed( ctx.from_context_element(), )) }); - index_context.map(|ctx| new_nodes.push(BreadcrumbTreeElement::from_borrowed(ctx))); + index_context.map(|ctx| new_stack.push(BreadcrumbTreeElement::from_borrowed(ctx))); - Some((parent, new_nodes)) - */ - todo!() + Some(new_stack) } fn new_breadcrumbs_partial<'b>( From 00699b84bae8ade41b7c0b27bade0e30ffb3dd2e Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jun 2020 22:45:58 -0400 Subject: [PATCH 54/67] Finished converting back to Vec. --- src/renderer/renderer.rs | 41 ++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 93192c9..adb17c5 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -242,12 +242,12 @@ impl<'a> DustRenderer<'a> { fn new_breadcrumbs_partial<'b>( &'b self, - maybe_breadcrumbs: &'b Vec>, - explicit_context_maybe_breadcrumbs: &'a Vec>, + breadcrumbs: &'b Vec>, + explicit_context_breadcrumbs: &'a Vec>, injected_context: Option<&'b dyn IntoContextElement>, explicit_context: &Option>, - ) { - /* + ) -> Option>> { + // If none of the additional contexts are present, return None // to signal that the original breadcrumbs should be used // rather than incurring a copy here. @@ -258,11 +258,10 @@ impl<'a> DustRenderer<'a> { // If there is an explicit context, then drop all the current // context - let mut parent = match explicit_context { - Some(_) => None, - None => maybe_breadcrumbs, + let mut new_stack = match explicit_context { + Some(_) => Vec::with_capacity(3), + None => breadcrumbs.clone(), }; - let mut new_nodes: Vec = Vec::new(); injected_context.map(|ctx| { // Special case: when there is no explicit context, the @@ -271,32 +270,27 @@ impl<'a> DustRenderer<'a> { // added after the current context but before the explicit // context. match explicit_context { - None => { - let (new_parent, passed_nodes) = - Self::split_tree_at_predicate(parent, |b| b.get_ice().is_pseudo_element()); - parent = new_parent; - new_nodes.extend(passed_nodes.iter().map(|b| b.get_element().clone())); - } - _ => new_nodes.push(BreadcrumbTreeElement::from_borrowed(ctx)), + None => new_stack.insert(Self::get_index_of_first_non_pseudo_element(&new_stack).unwrap_or(0), BreadcrumbTreeElement::from_borrowed(ctx)), + _ => new_stack.push(BreadcrumbTreeElement::from_borrowed(ctx)) } }); + explicit_context.as_ref().map(|path| { // TODO: should resolving the value here use // explicit_context_maybe_breadcrumbs or // maybe_breadcrumbs? - walk_path(explicit_context_maybe_breadcrumbs, &path.keys) - .map(|ice| ice.into_context_element(self, maybe_breadcrumbs)) + walk_path(explicit_context_breadcrumbs, &path.keys) + .map(|ice| ice.into_context_element(self, breadcrumbs)) .ok() .flatten() .map(|val| { if val.get_context_element_reference().is_truthy() { - new_nodes.push(std::convert::From::from(val)); + new_stack.push(std::convert::From::from(val)); } }); }); - Some((parent, new_nodes))*/ - todo!() + Some(new_stack) } fn preprocess_filters(filters: &Vec) -> Vec { @@ -313,6 +307,13 @@ impl<'a> DustRenderer<'a> { } final_filters } + + fn get_index_of_first_non_pseudo_element<'b>(breadcrumbs: &'b Vec>) -> Option + { + breadcrumbs + .iter() + .rposition(|b| std::borrow::Borrow::::borrow(b).is_pseudo_element()) + } } struct BlockContext<'a> { From e28ebaf26a0f324aabcf44885a9af0b2621887c2 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jun 2020 23:08:21 -0400 Subject: [PATCH 55/67] Update IterationContext to be an IntoContextElement and finish implementing section. --- src/renderer/iteration_context.rs | 50 ++++----------- src/renderer/mod.rs | 2 +- src/renderer/renderer.rs | 102 ++++++++++++++++++++++++++---- 3 files changed, 102 insertions(+), 52 deletions(-) diff --git a/src/renderer/iteration_context.rs b/src/renderer/iteration_context.rs index 9830137..687fb71 100644 --- a/src/renderer/iteration_context.rs +++ b/src/renderer/iteration_context.rs @@ -1,6 +1,9 @@ +use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement; use crate::renderer::context_element::CompareContextElement; use crate::renderer::context_element::ContextElement; +use crate::renderer::context_element::IceResult; use crate::renderer::context_element::IntoContextElement; +use crate::renderer::DustRenderer; use crate::renderer::Loopable; use crate::renderer::RenderError; use crate::renderer::Renderable; @@ -16,7 +19,7 @@ use std::cmp::Ordering; /// Functions the same as the injected parameters contexts for /// helpers/partials with parameters but this has no need for storing /// breadcrumbs since its simply storing two integers. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct IterationContext { idx: OwnedLiteral, len: OwnedLiteral, @@ -32,32 +35,13 @@ impl IterationContext { } } -impl ContextElement for IterationContext {} - -impl Truthiness for IterationContext { - fn is_truthy(&self) -> bool { - // 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 `{.}`. - true - } -} - -impl Renderable for IterationContext { - 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 IterationContext { - 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::new() +impl IntoContextElement for IterationContext { + fn into_context_element<'b>( + &'b self, + renderer: &DustRenderer, + breadcrumbs: &'b Vec>, + ) -> Option> { + panic!("into_context_element cannot be called on pseudo elements"); } } @@ -74,15 +58,3 @@ impl Walkable for IterationContext { true } } - -impl CompareContextElement for IterationContext { - fn equals(&self, other: &dyn ContextElement) -> bool { - // TODO: Does this ever happen? perhaps I should have a panic here. - false - } - - fn partial_compare(&self, other: &dyn ContextElement) -> Option { - // TODO: Does this ever happen? perhaps I should have a panic here. - None - } -} diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 6baa196..11f75f8 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -4,7 +4,7 @@ mod breadcrumb_tree; mod context_element; mod errors; mod inline_partial_tree; -// mod iteration_context; +mod iteration_context; mod parameters_context; mod renderer; mod tree_walking; diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index adb17c5..0506c0b 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -15,6 +15,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::iteration_context::IterationContext; use crate::renderer::parameters_context::ParametersContext; use crate::renderer::tree_walking::walk_path; use std::borrow::Borrow; @@ -170,8 +171,7 @@ impl<'a> DustRenderer<'a> { let val = walk_path(breadcrumbs, &container.path.keys) .map(|ice| ice.into_context_element(self, breadcrumbs)); match val { - Err(WalkError::CantWalk) => { - // TODO + Err(WalkError::CantWalk) | Ok(None) => { let new_breadcrumbs = self.new_breadcrumbs_section( breadcrumbs, None, @@ -179,9 +179,84 @@ impl<'a> DustRenderer<'a> { &container.explicit_context, None, ); + return self.render_maybe_body( + &container.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); } - Ok(final_val) => { - // TODO + Ok(Some(final_val)) => { + let context_element = final_val.get_context_element_reference(); + return if context_element.is_truthy() { + match &container.contents { + None => Ok("".to_owned()), + Some(body) => { + let loop_elements: Vec<&dyn ContextElement> = + context_element.get_loop_elements(); + if loop_elements.is_empty() { + // Scalar value + let new_breadcrumbs = self.new_breadcrumbs_section( + breadcrumbs, + None, + Some(&injected_context), + &container.explicit_context, + Some(context_element), + ); + self.render_body( + body, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ) + } else { + // Array-like value + let total_length = loop_elements.len(); + let rendered_results: Result, RenderError> = + loop_elements + .into_iter() + .enumerate() + .map(|(i, array_elem)| { + let index_context = + IterationContext::new(i, total_length); + let new_breadcrumbs = self + .new_breadcrumbs_section( + breadcrumbs, + Some(&index_context), + Some(&injected_context), + &container.explicit_context, + Some(array_elem), + ); + self.render_body( + &body, + new_breadcrumbs + .as_ref() + .unwrap_or(breadcrumbs), + blocks, + ) + }) + .collect(); + let rendered_slice: &[String] = &rendered_results?; + return Ok(rendered_slice.join("")); + } + } + } + } else { + // 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 + let new_breadcrumbs = self.new_breadcrumbs_section( + breadcrumbs, + None, + Some(&injected_context), + &container.explicit_context, + None, + ); + self.render_maybe_body( + &container.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ) + }; } } } @@ -247,7 +322,6 @@ impl<'a> DustRenderer<'a> { injected_context: Option<&'b dyn IntoContextElement>, explicit_context: &Option>, ) -> Option>> { - // If none of the additional contexts are present, return None // to signal that the original breadcrumbs should be used // rather than incurring a copy here. @@ -270,8 +344,11 @@ impl<'a> DustRenderer<'a> { // added after the current context but before the explicit // context. match explicit_context { - None => new_stack.insert(Self::get_index_of_first_non_pseudo_element(&new_stack).unwrap_or(0), BreadcrumbTreeElement::from_borrowed(ctx)), - _ => new_stack.push(BreadcrumbTreeElement::from_borrowed(ctx)) + None => new_stack.insert( + Self::get_index_of_first_non_pseudo_element(&new_stack).unwrap_or(0), + BreadcrumbTreeElement::from_borrowed(ctx), + ), + _ => new_stack.push(BreadcrumbTreeElement::from_borrowed(ctx)), } }); @@ -308,11 +385,12 @@ impl<'a> DustRenderer<'a> { final_filters } - fn get_index_of_first_non_pseudo_element<'b>(breadcrumbs: &'b Vec>) -> Option - { - breadcrumbs - .iter() - .rposition(|b| std::borrow::Borrow::::borrow(b).is_pseudo_element()) + fn get_index_of_first_non_pseudo_element<'b>( + breadcrumbs: &'b Vec>, + ) -> Option { + breadcrumbs.iter().rposition(|b| { + std::borrow::Borrow::::borrow(b).is_pseudo_element() + }) } } From b39676548801c4e9c51e0f57e5a96c5c8465466a Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jun 2020 23:15:12 -0400 Subject: [PATCH 56/67] Updated Exists/NotExists for the new architecture. --- src/renderer/renderer.rs | 46 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 0506c0b..0973708 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -260,6 +260,52 @@ impl<'a> DustRenderer<'a> { } } } + DustTag::DTExists(container) => { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + None, + &container.explicit_context, + ); + let val = walk_path(breadcrumbs, &container.path.keys) + .map(|ice| ice.into_context_element(self, breadcrumbs)); + return match val { + Ok(Some(v)) if v.get_context_element_reference().is_truthy() => self + .render_maybe_body( + &container.contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ), + _ => self.render_maybe_body( + &container.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ), + }; + } + DustTag::DTNotExists(container) => { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + None, + &container.explicit_context, + ); + let val = walk_path(breadcrumbs, &container.path.keys) + .map(|ice| ice.into_context_element(self, breadcrumbs)); + return match val { + Ok(Some(v)) if v.get_context_element_reference().is_truthy() => self + .render_maybe_body( + &container.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ), + _ => self.render_maybe_body( + &container.contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ), + }; + } _ => panic!("Unsupported tag"), } Ok("".to_owned()) From 250d4284646d98b58dca39bf9a093c1e9bedca48 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jun 2020 23:17:50 -0400 Subject: [PATCH 57/67] Updated Partial for the new architecture. --- src/renderer/renderer.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 0973708..cf24470 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -306,6 +306,38 @@ impl<'a> DustRenderer<'a> { ), }; } + DustTag::DTPartial(partial) => { + let partial_name = self.render_partial_name(&partial.name, breadcrumbs)?; + if partial.params.is_empty() { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + None, + &partial.explicit_context, + ); + let rendered_content = self.render_template( + &partial_name, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + Some(blocks.blocks), + )?; + return Ok(rendered_content); + } else { + let injected_context = + ParametersContext::new(self, breadcrumbs, &partial.params); + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + Some(&injected_context), + &partial.explicit_context, + ); + let rendered_content = self.render_template( + &partial_name, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + Some(blocks.blocks), + )?; + return Ok(rendered_content); + } + } _ => panic!("Unsupported tag"), } Ok("".to_owned()) From c8de3950382ee310eec0fe7155a54eb6bfeb5b60 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jun 2020 23:18:28 -0400 Subject: [PATCH 58/67] Inline partials and blocks worked as-is. --- src/renderer/renderer.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index cf24470..3baa0a3 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -338,6 +338,30 @@ impl<'a> DustRenderer<'a> { return Ok(rendered_content); } } + DustTag::DTInlinePartial(_named_block) => { + // Inline partials are blank during rendering (they get injected into blocks) + return Ok("".to_owned()); + } + DustTag::DTBlock(named_block) => { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + blocks.breadcrumbs, + None, + &named_block.explicit_context, + ); + return match blocks.blocks.get_block(named_block.path.keys[0]) { + None => self.render_maybe_body( + &named_block.contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ), + Some(inline_partial) => self.render_maybe_body( + inline_partial, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ), + }; + } _ => panic!("Unsupported tag"), } Ok("".to_owned()) From 8cbb31251cd04f6dd7c3d1cbb1f8dcf83f90795d Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 7 Jun 2020 00:03:51 -0400 Subject: [PATCH 59/67] port over the equals/not equals helpers. --- src/renderer/parameters_context.rs | 12 ++- src/renderer/renderer.rs | 141 +++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+), 4 deletions(-) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index c4b65e5..a52eb03 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -22,7 +22,7 @@ use std::rc::Rc; #[derive(Debug)] pub struct ParametersContext<'a> { - params: HashMap<&'a str, BreadcrumbTreeElement<'a>>, + params: HashMap<&'a str, (&'a RValue<'a>, BreadcrumbTreeElement<'a>)>, } impl<'a> ParametersContext<'a> { @@ -39,7 +39,7 @@ impl<'a> ParametersContext<'a> { // then those are resolved at the time of access rather than // the time of assignment, so we leave them into their // original IntoContextElement state. - let rendered_params: HashMap<&'a str, BreadcrumbTreeElement<'a>> = params + let rendered_params: HashMap<&'a str, (&'a RValue<'a>, BreadcrumbTreeElement<'a>)> = params .iter() .map(|kvpair| { let k = kvpair.key; @@ -55,7 +55,7 @@ impl<'a> ParametersContext<'a> { Some(BreadcrumbTreeElement::from_borrowed(&kvpair.value)) } }; - v.map(|some_v| (k, some_v)) + v.map(|some_v| (k, (&kvpair.value, some_v))) }) // TODO: Should a None value here be the same as a key not existing, or should we store the Nones? .filter_map(|pair| pair) @@ -65,6 +65,10 @@ impl<'a> ParametersContext<'a> { params: rendered_params, } } + + pub fn get_original_rvalue(&self, segment: &str) -> Option<&'a RValue<'a>> { + self.params.get(segment).map(|(rvalue, _bte)| *rvalue) + } } impl<'a> IntoContextElement for ParametersContext<'a> { @@ -81,7 +85,7 @@ impl<'a> Walkable for ParametersContext<'a> { fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { self.params .get(segment) - .map(|bte| bte.borrow()) + .map(|(_rvalue, bte)| bte.borrow()) .ok_or(WalkError::CantWalk) } diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 3baa0a3..582af12 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -2,14 +2,17 @@ use crate::parser::template; use crate::parser::Body; use crate::parser::DustTag; use crate::parser::Filter; +use crate::parser::KVPair; use crate::parser::PartialNameElement; use crate::parser::Path; +use crate::parser::RValue; use crate::parser::Special; use crate::parser::Template; use crate::parser::TemplateElement; use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement; use crate::renderer::context_element::ContextElement; use crate::renderer::context_element::IntoContextElement; +use crate::renderer::context_element::Walkable; use crate::renderer::errors::CompileError; use crate::renderer::errors::RenderError; use crate::renderer::errors::WalkError; @@ -362,6 +365,124 @@ impl<'a> DustRenderer<'a> { ), }; } + DustTag::DTHelperEquals(parameterized_block) => { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + None, + ¶meterized_block.explicit_context, + ); + + let param_map = + ParametersContext::new(self, breadcrumbs, ¶meterized_block.params); + + // Special case: when comparing two RVPaths, if the + // path is equal then dust assumes the values are + // equal (otherwise, non-scalar values are + // automatically not equal) + if Self::are_paths_identical(¶m_map, "key", "value") { + return match ¶meterized_block.contents { + None => Ok("".to_owned()), + Some(body) => { + let rendered_content = self.render_body( + body, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + )?; + Ok(rendered_content) + } + }; + } + + let left_side = match param_map + .walk("key") + .map(|ice| ice.into_context_element(self, breadcrumbs)) + { + Err(WalkError::CantWalk) | Ok(None) => return Ok("".to_owned()), + Ok(Some(res)) => res, + }; + let right_side = param_map + .walk("value") + .map(|ice| ice.into_context_element(self, breadcrumbs)); + if Ok(Some(left_side.get_context_element_reference())) + == right_side.as_ref().map(|maybe_ice| { + maybe_ice + .as_ref() + .map(|ice| ice.get_context_element_reference()) + }) + { + return self.render_maybe_body( + ¶meterized_block.contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } else { + return self.render_maybe_body( + ¶meterized_block.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } + } + DustTag::DTHelperNotEquals(parameterized_block) => { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + None, + ¶meterized_block.explicit_context, + ); + + let param_map = + ParametersContext::new(self, breadcrumbs, ¶meterized_block.params); + + // Special case: when comparing two RVPaths, if the + // path is equal then dust assumes the values are + // equal (otherwise, non-scalar values are + // automatically not equal) + if Self::are_paths_identical(¶m_map, "key", "value") { + return match ¶meterized_block.else_contents { + None => Ok("".to_owned()), + Some(body) => { + let rendered_content = self.render_body( + body, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + )?; + Ok(rendered_content) + } + }; + } + + let left_side = match param_map + .walk("key") + .map(|ice| ice.into_context_element(self, breadcrumbs)) + { + Err(WalkError::CantWalk) | Ok(None) => return Ok("".to_owned()), + Ok(Some(res)) => res, + }; + let right_side = param_map + .walk("value") + .map(|ice| ice.into_context_element(self, breadcrumbs)); + if Ok(Some(left_side.get_context_element_reference())) + != right_side.as_ref().map(|maybe_ice| { + maybe_ice + .as_ref() + .map(|ice| ice.get_context_element_reference()) + }) + { + return self.render_maybe_body( + ¶meterized_block.contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } else { + return self.render_maybe_body( + ¶meterized_block.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } + } _ => panic!("Unsupported tag"), } Ok("".to_owned()) @@ -472,6 +593,26 @@ impl<'a> DustRenderer<'a> { Some(new_stack) } + fn are_paths_identical<'b>( + param_map: &ParametersContext<'b>, + left_key: &str, + right_key: &str, + ) -> bool { + match ( + param_map.get_original_rvalue(left_key), + param_map.get_original_rvalue(right_key), + ) { + (None, _) => false, + (_, None) => false, + (Some(key_rval), Some(value_rval)) => match (key_rval, value_rval) { + (RValue::RVPath(key_path), RValue::RVPath(value_path)) => { + key_path.keys == value_path.keys + } + _ => false, + }, + } + } + fn preprocess_filters(filters: &Vec) -> Vec { let mut final_filters: Vec = filters .into_iter() From 422479bcf2bd6b994e73f9ac8397845d002110a8 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 7 Jun 2020 00:12:01 -0400 Subject: [PATCH 60/67] port over the remaining helpers. --- src/renderer/renderer.rs | 189 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 582af12..72cda7b 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -483,8 +483,197 @@ impl<'a> DustRenderer<'a> { ); } } + DustTag::DTHelperGreaterThan(parameterized_block) => { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + None, + ¶meterized_block.explicit_context, + ); + + let param_map = + ParametersContext::new(self, breadcrumbs, ¶meterized_block.params); + let left_side = match param_map + .walk("key") + .map(|ice| ice.into_context_element(self, breadcrumbs)) + { + Err(WalkError::CantWalk) | Ok(None) => return Ok("".to_owned()), + Ok(Some(res)) => res, + }; + let right_side = param_map + .walk("value") + .map(|ice| ice.into_context_element(self, breadcrumbs)); + match (right_side) { + Ok(Some(right_side_unwrapped)) => { + if left_side.get_context_element_reference() + > right_side_unwrapped.get_context_element_reference() + { + return self.render_maybe_body( + ¶meterized_block.contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } else { + return self.render_maybe_body( + ¶meterized_block.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } + } + _ => { + return self.render_maybe_body( + ¶meterized_block.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ) + } + } + } + DustTag::DTHelperGreaterThanOrEquals(parameterized_block) => { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + None, + ¶meterized_block.explicit_context, + ); + + let param_map = + ParametersContext::new(self, breadcrumbs, ¶meterized_block.params); + let left_side = match param_map + .walk("key") + .map(|ice| ice.into_context_element(self, breadcrumbs)) + { + Err(WalkError::CantWalk) | Ok(None) => return Ok("".to_owned()), + Ok(Some(res)) => res, + }; + let right_side = param_map + .walk("value") + .map(|ice| ice.into_context_element(self, breadcrumbs)); + match (right_side) { + Ok(Some(right_side_unwrapped)) => { + if left_side.get_context_element_reference() + >= right_side_unwrapped.get_context_element_reference() + { + return self.render_maybe_body( + ¶meterized_block.contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } else { + return self.render_maybe_body( + ¶meterized_block.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } + } + _ => { + return self.render_maybe_body( + ¶meterized_block.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ) + } + } + } + DustTag::DTHelperLessThan(parameterized_block) => { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + None, + ¶meterized_block.explicit_context, + ); + + let param_map = + ParametersContext::new(self, breadcrumbs, ¶meterized_block.params); + let left_side = match param_map + .walk("key") + .map(|ice| ice.into_context_element(self, breadcrumbs)) + { + Err(WalkError::CantWalk) | Ok(None) => return Ok("".to_owned()), + Ok(Some(res)) => res, + }; + let right_side = param_map + .walk("value") + .map(|ice| ice.into_context_element(self, breadcrumbs)); + match (right_side) { + Ok(Some(right_side_unwrapped)) => { + if left_side.get_context_element_reference() + < right_side_unwrapped.get_context_element_reference() + { + return self.render_maybe_body( + ¶meterized_block.contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } else { + return self.render_maybe_body( + ¶meterized_block.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } + } + _ => { + return self.render_maybe_body( + ¶meterized_block.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ) + } + } + } + DustTag::DTHelperLessThanOrEquals(parameterized_block) => { + let new_breadcrumbs = self.new_breadcrumbs_partial( + breadcrumbs, + breadcrumbs, + None, + ¶meterized_block.explicit_context, + ); + + let param_map = + ParametersContext::new(self, breadcrumbs, ¶meterized_block.params); + let left_side = match param_map + .walk("key") + .map(|ice| ice.into_context_element(self, breadcrumbs)) + { + Err(WalkError::CantWalk) | Ok(None) => return Ok("".to_owned()), + Ok(Some(res)) => res, + }; + let right_side = param_map + .walk("value") + .map(|ice| ice.into_context_element(self, breadcrumbs)); + match (right_side) { + Ok(Some(right_side_unwrapped)) => { + if left_side.get_context_element_reference() + <= right_side_unwrapped.get_context_element_reference() + { + return self.render_maybe_body( + ¶meterized_block.contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } else { + return self.render_maybe_body( + ¶meterized_block.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ); + } + } + _ => { + return self.render_maybe_body( + ¶meterized_block.else_contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ) + } + } + } _ => panic!("Unsupported tag"), } + Ok("".to_owned()) } From 669a698575b4fcc89a3126e7c150adc1364eb9ab Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 7 Jun 2020 00:17:55 -0400 Subject: [PATCH 61/67] Re-enable the ContextElement implementation for serde_json --- src/bin.rs | 547 ++++++++++++++++++++++++++--------------------------- 1 file changed, 273 insertions(+), 274 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index 84fe456..b58c8ef 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -53,13 +53,12 @@ fn main() { compiled_templates.iter().for_each(|(name, template)| { dust_renderer.load_source(template, name.to_owned()); }); - // let breadcrumbs = vec![&context as &dyn IntoContextElement]; - // println!( - // "{}", - // dust_renderer - // .render(main_template_name, &breadcrumbs) - // .expect("Failed to render") - // ); + println!( + "{}", + dust_renderer + .render(main_template_name, Some(&context)) + .expect("Failed to render") + ); } fn template_from_file<'a>( @@ -171,288 +170,288 @@ fn encode_uri_component(inp: &str) -> String { output } -// fn apply_filter( -// json_value: &serde_json::Value, -// filter: &Filter, -// ) -> Result { -// match (json_value, filter) { -// // Html escape filter -// (serde_json::Value::String(string), Filter::HtmlEncode) => { -// Ok(serde_json::Value::String(html_escape(string))) -// } -// (_, Filter::HtmlEncode) => Ok(serde_json::Value::String(html_escape( -// &json_value.render(&Vec::new())?, -// ))), -// // Disable html escape filter -// (_, Filter::DisableHtmlEncode) => panic!("The |s filter is automatically removed by the renderer since it is a no-op during rendering."), -// // Parse JSON filter -// (serde_json::Value::String(string), Filter::JsonParse) => { -// serde_json::from_str(&string).or(Err(RenderError::InvalidJson(string.to_owned()))) -// } -// (_, Filter::JsonParse) => { -// let rendered_value = json_value.render(&Vec::new())?; -// serde_json::from_str(&rendered_value).or(Err(RenderError::InvalidJson(rendered_value))) -// } -// // Json Stringify filter -// (_, Filter::JsonStringify) => { -// Ok(serde_json::Value::String(json_value.to_string())) -// } -// // Javascript escape filter -// (serde_json::Value::String(string), Filter::JavascriptStringEncode) => { -// Ok(serde_json::Value::String(javascript_escape(string))) -// } -// (serde_json::Value::Bool(boolean), Filter::JavascriptStringEncode) => { -// Ok(serde_json::Value::Bool(*boolean)) -// } -// (serde_json::Value::Number(number), Filter::JavascriptStringEncode) => { -// Ok(serde_json::Value::Number(number.clone())) -// } -// (serde_json::Value::Array(arr), Filter::JavascriptStringEncode) => { -// Ok(serde_json::Value::Array(arr.clone())) -// } -// (serde_json::Value::Object(obj), Filter::JavascriptStringEncode) => { -// Ok(serde_json::Value::Object(obj.clone())) -// } -// (_, Filter::JavascriptStringEncode) => Ok(serde_json::Value::String(javascript_escape( -// &json_value.render(&Vec::new())?, -// ))), -// // EncodeURI filter -// (serde_json::Value::String(string), Filter::EncodeUri) => { -// Ok(serde_json::Value::String(encode_uri(string))) -// } -// (_, Filter::EncodeUri) => Ok(serde_json::Value::String(encode_uri( -// &json_value.render(&Vec::new())?, -// ))), -// // EncodeURIComponent filter -// (serde_json::Value::String(string), Filter::EncodeUriComponent) => { -// Ok(serde_json::Value::String(encode_uri_component(string))) -// } -// (_, Filter::EncodeUriComponent) => Ok(serde_json::Value::String(encode_uri_component( -// &json_value.render(&Vec::new())?, -// ))), -// } -// } +fn apply_filter( + json_value: &serde_json::Value, + filter: &Filter, +) -> Result { + match (json_value, filter) { + // Html escape filter + (serde_json::Value::String(string), Filter::HtmlEncode) => { + Ok(serde_json::Value::String(html_escape(string))) + } + (_, Filter::HtmlEncode) => Ok(serde_json::Value::String(html_escape( + &json_value.render(&Vec::new())?, + ))), + // Disable html escape filter + (_, Filter::DisableHtmlEncode) => panic!("The |s filter is automatically removed by the renderer since it is a no-op during rendering."), + // Parse JSON filter + (serde_json::Value::String(string), Filter::JsonParse) => { + serde_json::from_str(&string).or(Err(RenderError::InvalidJson(string.to_owned()))) + } + (_, Filter::JsonParse) => { + let rendered_value = json_value.render(&Vec::new())?; + serde_json::from_str(&rendered_value).or(Err(RenderError::InvalidJson(rendered_value))) + } + // Json Stringify filter + (_, Filter::JsonStringify) => { + Ok(serde_json::Value::String(json_value.to_string())) + } + // Javascript escape filter + (serde_json::Value::String(string), Filter::JavascriptStringEncode) => { + Ok(serde_json::Value::String(javascript_escape(string))) + } + (serde_json::Value::Bool(boolean), Filter::JavascriptStringEncode) => { + Ok(serde_json::Value::Bool(*boolean)) + } + (serde_json::Value::Number(number), Filter::JavascriptStringEncode) => { + Ok(serde_json::Value::Number(number.clone())) + } + (serde_json::Value::Array(arr), Filter::JavascriptStringEncode) => { + Ok(serde_json::Value::Array(arr.clone())) + } + (serde_json::Value::Object(obj), Filter::JavascriptStringEncode) => { + Ok(serde_json::Value::Object(obj.clone())) + } + (_, Filter::JavascriptStringEncode) => Ok(serde_json::Value::String(javascript_escape( + &json_value.render(&Vec::new())?, + ))), + // EncodeURI filter + (serde_json::Value::String(string), Filter::EncodeUri) => { + Ok(serde_json::Value::String(encode_uri(string))) + } + (_, Filter::EncodeUri) => Ok(serde_json::Value::String(encode_uri( + &json_value.render(&Vec::new())?, + ))), + // EncodeURIComponent filter + (serde_json::Value::String(string), Filter::EncodeUriComponent) => { + Ok(serde_json::Value::String(encode_uri_component(string))) + } + (_, Filter::EncodeUriComponent) => Ok(serde_json::Value::String(encode_uri_component( + &json_value.render(&Vec::new())?, + ))), + } +} -// fn apply_filters( -// json_value: &serde_json::Value, -// filters: &[Filter], -// ) -> Result { -// let mut final_value: serde_json::Value = apply_filter(json_value, &filters[0])?; +fn apply_filters( + json_value: &serde_json::Value, + filters: &[Filter], +) -> Result { + let mut final_value: serde_json::Value = apply_filter(json_value, &filters[0])?; -// for filter in &filters[1..] { -// final_value = apply_filter(&final_value, filter)?; -// } + for filter in &filters[1..] { + final_value = apply_filter(&final_value, filter)?; + } -// Ok(final_value) -// } + Ok(final_value) +} -// impl ContextElement for serde_json::Value {} +impl ContextElement for serde_json::Value {} -// impl Truthiness for serde_json::Value { -// fn is_truthy(&self) -> bool { -// match self { -// serde_json::Value::Null => false, -// serde_json::Value::Bool(boolean) => *boolean, -// serde_json::Value::Number(_num) => true, -// serde_json::Value::String(string_value) => !string_value.is_empty(), -// serde_json::Value::Array(array_value) => !array_value.is_empty(), -// serde_json::Value::Object(_obj) => true, -// } -// } -// } +impl Truthiness for serde_json::Value { + fn is_truthy(&self) -> bool { + match self { + serde_json::Value::Null => false, + serde_json::Value::Bool(boolean) => *boolean, + serde_json::Value::Number(_num) => true, + serde_json::Value::String(string_value) => !string_value.is_empty(), + serde_json::Value::Array(array_value) => !array_value.is_empty(), + serde_json::Value::Object(_obj) => true, + } + } +} -// impl Renderable for serde_json::Value { -// fn render(&self, _filters: &Vec) -> Result { -// let after_apply = if _filters.is_empty() { -// None -// } else { -// Some(apply_filters(self, _filters)?) -// }; +impl Renderable for serde_json::Value { + fn render(&self, _filters: &Vec) -> Result { + let after_apply = if _filters.is_empty() { + None + } else { + Some(apply_filters(self, _filters)?) + }; -// match after_apply.as_ref().unwrap_or(self) { -// serde_json::Value::Null => Ok("".to_owned()), -// serde_json::Value::Bool(boolean) => Ok(boolean.to_string()), -// serde_json::Value::Number(num) => Ok(num.to_string()), -// serde_json::Value::String(string) => Ok(string.to_string()), -// serde_json::Value::Array(arr) => { -// // TODO: Handle the filters instead of passing a Vec::new() -// let rendered: Result, RenderError> = -// arr.iter().map(|val| val.render(&Vec::new())).collect(); -// let rendered_slice: &[String] = &rendered?; -// Ok(rendered_slice.join(",")) -// } -// serde_json::Value::Object(_obj) => Ok("[object Object]".to_owned()), -// } -// } -// } + match after_apply.as_ref().unwrap_or(self) { + serde_json::Value::Null => Ok("".to_owned()), + serde_json::Value::Bool(boolean) => Ok(boolean.to_string()), + serde_json::Value::Number(num) => Ok(num.to_string()), + serde_json::Value::String(string) => Ok(string.to_string()), + serde_json::Value::Array(arr) => { + // TODO: Handle the filters instead of passing a Vec::new() + let rendered: Result, RenderError> = + arr.iter().map(|val| val.render(&Vec::new())).collect(); + let rendered_slice: &[String] = &rendered?; + Ok(rendered_slice.join(",")) + } + serde_json::Value::Object(_obj) => Ok("[object Object]".to_owned()), + } + } +} -// impl Walkable for serde_json::Value { -// fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { -// match 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), -// } -// } -// } +impl Walkable for serde_json::Value { + fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { + match 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), + } + } +} -// impl Loopable for serde_json::Value { -// fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { -// match self { -// serde_json::Value::Array(array_value) => array_value.iter().map(|x| x as _).collect(), -// _ => Vec::new(), -// } -// } -// } +impl Loopable for serde_json::Value { + fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { + match self { + serde_json::Value::Array(array_value) => array_value.iter().map(|x| x as _).collect(), + _ => Vec::new(), + } + } +} -// impl CompareContextElement for serde_json::Value { -// fn equals(&self, other: &dyn ContextElement) -> bool { -// // println!("equals json {:?} | {:?}", self, other); -// // Handle other serde_json::Value -// match other.to_any().downcast_ref::() { -// None => (), -// Some(other_json_value) => match (self, other_json_value) { -// // Non-scalar values not caught in the renderer by the -// // identical-path shortcut are always not equal. -// (serde_json::Value::Array(_), _) -// | (_, serde_json::Value::Array(_)) -// | (serde_json::Value::Object(_), _) -// | (_, serde_json::Value::Object(_)) => return false, -// _ => return self == other_json_value, -// }, -// } -// // Handle literals -// match other.to_any().downcast_ref::() { -// None => (), -// Some(OwnedLiteral::LString(other_string)) => { -// return self.as_str().map_or(false, |s| s == other_string) -// } -// Some(OwnedLiteral::LPositiveInteger(other_num)) => { -// return self.as_u64().map_or(false, |n| n == *other_num) -// } -// } -// false -// } +impl CompareContextElement for serde_json::Value { + fn equals(&self, other: &dyn ContextElement) -> bool { + // println!("equals json {:?} | {:?}", self, other); + // Handle other serde_json::Value + match other.to_any().downcast_ref::() { + None => (), + Some(other_json_value) => match (self, other_json_value) { + // Non-scalar values not caught in the renderer by the + // identical-path shortcut are always not equal. + (serde_json::Value::Array(_), _) + | (_, serde_json::Value::Array(_)) + | (serde_json::Value::Object(_), _) + | (_, serde_json::Value::Object(_)) => return false, + _ => return self == other_json_value, + }, + } + // Handle literals + match other.to_any().downcast_ref::() { + None => (), + Some(OwnedLiteral::LString(other_string)) => { + return self.as_str().map_or(false, |s| s == other_string) + } + Some(OwnedLiteral::LPositiveInteger(other_num)) => { + return self.as_u64().map_or(false, |n| n == *other_num) + } + } + false + } -// fn partial_compare(&self, other: &dyn ContextElement) -> Option { -// // println!("partial_compare json {:?} | {:?}", self, other); -// // Handle type coerced objects + fn partial_compare(&self, other: &dyn ContextElement) -> Option { + // println!("partial_compare json {:?} | {:?}", self, other); + // Handle type coerced objects -// // When doing a greater than or less than comparison, -// // javascript coerces objects into "[object Object]". -// if let serde_json::Value::Object(_) = self { -// return OwnedLiteral::LString(self.render(&Vec::new()).unwrap_or("".to_owned())) -// .partial_compare(other); -// } + // When doing a greater than or less than comparison, + // javascript coerces objects into "[object Object]". + if let serde_json::Value::Object(_) = self { + return OwnedLiteral::LString(self.render(&Vec::new()).unwrap_or("".to_owned())) + .partial_compare(other); + } -// // When doing a greater than or less than comparison -// // javascript turns arrays into strings. -// if let serde_json::Value::Array(_) = self { -// return OwnedLiteral::LString(self.render(&Vec::new()).unwrap_or("".to_owned())) -// .partial_compare(other); -// } + // When doing a greater than or less than comparison + // javascript turns arrays into strings. + if let serde_json::Value::Array(_) = self { + return OwnedLiteral::LString(self.render(&Vec::new()).unwrap_or("".to_owned())) + .partial_compare(other); + } -// // Handle other serde_json::Value -// match other.to_any().downcast_ref::() { -// None => (), -// Some(other_json_value) => { -// return match (self, other_json_value) { -// ( -// serde_json::Value::Bool(self_boolean), -// serde_json::Value::Bool(other_boolean), -// ) => self_boolean.partial_cmp(other_boolean), -// ( -// serde_json::Value::Number(self_number), -// serde_json::Value::Number(other_number), -// ) => return compare_json_numbers(self_number, other_number), -// ( -// serde_json::Value::String(self_string), -// serde_json::Value::Number(other_number), -// ) => return compare_json_numbers(self_string, other_number), -// ( -// serde_json::Value::Number(self_number), -// serde_json::Value::String(other_string), -// ) => return compare_json_numbers(self_number, other_string), -// ( -// serde_json::Value::String(self_string), -// serde_json::Value::String(other_string), -// ) => self_string.partial_cmp(other_string), -// ( -// serde_json::Value::Array(self_array), -// serde_json::Value::Array(other_array), -// ) => { -// return self -// .render(&Vec::new()) -// .unwrap_or("".to_owned()) -// .partial_cmp( -// &other_json_value -// .render(&Vec::new()) -// .unwrap_or("".to_owned()), -// ) -// } -// _ => None, -// }; -// } -// } -// // Handle literals -// match other.to_any().downcast_ref::() { -// None => (), -// Some(other_literal) => match (self, other_literal) { -// (serde_json::Value::String(self_string), OwnedLiteral::LString(other_string)) => { -// return self_string.partial_cmp(other_string) -// } -// ( -// serde_json::Value::String(self_string), -// OwnedLiteral::LPositiveInteger(other_num), -// ) => return compare_json_numbers(self_string, other_literal), -// (serde_json::Value::Number(self_num), OwnedLiteral::LString(other_string)) => { -// return compare_json_numbers(self_num, other_string) -// } -// ( -// serde_json::Value::Number(self_num), -// OwnedLiteral::LPositiveInteger(other_num), -// ) => return compare_json_numbers(self_num, other_literal), -// (serde_json::Value::Array(_), _) => { -// // TODO -// todo!() -// } -// (serde_json::Value::Object(_), _) => { -// // TODO -// todo!() -// } -// (serde_json::Value::Bool(_), _) => { -// // TODO -// todo!() -// } -// (serde_json::Value::Null, _) => { -// // TODO -// todo!() -// } -// }, -// } -// None -// } -// } + // Handle other serde_json::Value + match other.to_any().downcast_ref::() { + None => (), + Some(other_json_value) => { + return match (self, other_json_value) { + ( + serde_json::Value::Bool(self_boolean), + serde_json::Value::Bool(other_boolean), + ) => self_boolean.partial_cmp(other_boolean), + ( + serde_json::Value::Number(self_number), + serde_json::Value::Number(other_number), + ) => return compare_json_numbers(self_number, other_number), + ( + serde_json::Value::String(self_string), + serde_json::Value::Number(other_number), + ) => return compare_json_numbers(self_string, other_number), + ( + serde_json::Value::Number(self_number), + serde_json::Value::String(other_string), + ) => return compare_json_numbers(self_number, other_string), + ( + serde_json::Value::String(self_string), + serde_json::Value::String(other_string), + ) => self_string.partial_cmp(other_string), + ( + serde_json::Value::Array(self_array), + serde_json::Value::Array(other_array), + ) => { + return self + .render(&Vec::new()) + .unwrap_or("".to_owned()) + .partial_cmp( + &other_json_value + .render(&Vec::new()) + .unwrap_or("".to_owned()), + ) + } + _ => None, + }; + } + } + // Handle literals + match other.to_any().downcast_ref::() { + None => (), + Some(other_literal) => match (self, other_literal) { + (serde_json::Value::String(self_string), OwnedLiteral::LString(other_string)) => { + return self_string.partial_cmp(other_string) + } + ( + serde_json::Value::String(self_string), + OwnedLiteral::LPositiveInteger(other_num), + ) => return compare_json_numbers(self_string, other_literal), + (serde_json::Value::Number(self_num), OwnedLiteral::LString(other_string)) => { + return compare_json_numbers(self_num, other_string) + } + ( + serde_json::Value::Number(self_num), + OwnedLiteral::LPositiveInteger(other_num), + ) => return compare_json_numbers(self_num, other_literal), + (serde_json::Value::Array(_), _) => { + // TODO + todo!() + } + (serde_json::Value::Object(_), _) => { + // TODO + todo!() + } + (serde_json::Value::Bool(_), _) => { + // TODO + todo!() + } + (serde_json::Value::Null, _) => { + // TODO + todo!() + } + }, + } + 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() -// } +/// 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() +} #[derive(Debug)] enum JsonNumber { From 09d015346cc47bb3e20febbbc100b1b3b2ef4db2 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 7 Jun 2020 00:29:51 -0400 Subject: [PATCH 62/67] Fixed a bug where I was failing to filter out pseudo elements during walking. --- src/renderer/tree_walking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/tree_walking.rs b/src/renderer/tree_walking.rs index dc2892b..023c8b7 100644 --- a/src/renderer/tree_walking.rs +++ b/src/renderer/tree_walking.rs @@ -44,7 +44,7 @@ fn get_first_non_pseudo_element<'a>( .iter() .rev() .filter(|b| { - std::borrow::Borrow::::borrow(*b).is_pseudo_element() + !std::borrow::Borrow::::borrow(*b).is_pseudo_element() }) .next() } From 4789a7d9310681603d1071083c3a60a2b62dcc1d Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 7 Jun 2020 00:37:47 -0400 Subject: [PATCH 63/67] Fix the same issue in the renderer. --- src/renderer/renderer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 72cda7b..79036b9 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -821,7 +821,7 @@ impl<'a> DustRenderer<'a> { breadcrumbs: &'b Vec>, ) -> Option { breadcrumbs.iter().rposition(|b| { - std::borrow::Borrow::::borrow(b).is_pseudo_element() + !std::borrow::Borrow::::borrow(b).is_pseudo_element() }) } } From b1a85165b08c904023285f47f5c63f6bbd38f56e Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 7 Jun 2020 01:04:00 -0400 Subject: [PATCH 64/67] All tests passing! --- src/renderer/parameters_context.rs | 55 +++++++------ src/renderer/renderer.rs | 122 ++++++++++++++--------------- 2 files changed, 89 insertions(+), 88 deletions(-) diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index a52eb03..926def2 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -22,7 +22,7 @@ use std::rc::Rc; #[derive(Debug)] pub struct ParametersContext<'a> { - params: HashMap<&'a str, (&'a RValue<'a>, BreadcrumbTreeElement<'a>)>, + params: HashMap<&'a str, (&'a RValue<'a>, Option>)>, } impl<'a> ParametersContext<'a> { @@ -39,27 +39,26 @@ impl<'a> ParametersContext<'a> { // then those are resolved at the time of access rather than // the time of assignment, so we leave them into their // original IntoContextElement state. - let rendered_params: HashMap<&'a str, (&'a RValue<'a>, BreadcrumbTreeElement<'a>)> = params - .iter() - .map(|kvpair| { - let k = kvpair.key; - let v: Option> = match &kvpair.value { - RValue::RVLiteral(owned_literal) => { - Some(BreadcrumbTreeElement::from_borrowed(&kvpair.value)) - } - RValue::RVPath(path) => kvpair - .value - .into_context_element(renderer, breadcrumbs) - .map(std::convert::From::from), - RValue::RVTemplate(template) => { - Some(BreadcrumbTreeElement::from_borrowed(&kvpair.value)) - } - }; - v.map(|some_v| (k, (&kvpair.value, some_v))) - }) - // TODO: Should a None value here be the same as a key not existing, or should we store the Nones? - .filter_map(|pair| pair) - .collect(); + let rendered_params: HashMap<&'a str, (&'a RValue<'a>, Option>)> = + params + .iter() + .map(|kvpair| { + let k = kvpair.key; + let v: Option> = match &kvpair.value { + RValue::RVLiteral(owned_literal) => { + Some(BreadcrumbTreeElement::from_borrowed(&kvpair.value)) + } + RValue::RVPath(path) => kvpair + .value + .into_context_element(renderer, breadcrumbs) + .map(std::convert::From::from), + RValue::RVTemplate(template) => { + Some(BreadcrumbTreeElement::from_borrowed(&kvpair.value)) + } + }; + (k, (&kvpair.value, v)) + }) + .collect(); ParametersContext { params: rendered_params, @@ -69,6 +68,10 @@ impl<'a> ParametersContext<'a> { pub fn get_original_rvalue(&self, segment: &str) -> Option<&'a RValue<'a>> { self.params.get(segment).map(|(rvalue, _bte)| *rvalue) } + + pub fn contains_key(&self, segment: &str) -> bool { + self.params.contains_key(segment) + } } impl<'a> IntoContextElement for ParametersContext<'a> { @@ -83,10 +86,10 @@ impl<'a> IntoContextElement for ParametersContext<'a> { impl<'a> Walkable for ParametersContext<'a> { fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { - self.params - .get(segment) - .map(|(_rvalue, bte)| bte.borrow()) - .ok_or(WalkError::CantWalk) + match self.params.get(segment).map(|(_rvalue, bte)| bte) { + Some(Some(bte)) => Ok(bte.borrow()), + _ => Err(WalkError::CantWalk), + } } fn is_pseudo_element(&self) -> bool { diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 79036b9..b6cc041 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -394,23 +394,24 @@ impl<'a> DustRenderer<'a> { }; } - let left_side = match param_map + if !param_map.contains_key("key") { + return Ok("".to_owned()); + } + let left_side = param_map .walk("key") - .map(|ice| ice.into_context_element(self, breadcrumbs)) - { - Err(WalkError::CantWalk) | Ok(None) => return Ok("".to_owned()), - Ok(Some(res)) => res, - }; + .map(|ice| ice.into_context_element(self, breadcrumbs)); let right_side = param_map .walk("value") .map(|ice| ice.into_context_element(self, breadcrumbs)); - if Ok(Some(left_side.get_context_element_reference())) - == right_side.as_ref().map(|maybe_ice| { - maybe_ice - .as_ref() - .map(|ice| ice.get_context_element_reference()) - }) - { + if left_side.as_ref().map(|maybe_ice| { + maybe_ice + .as_ref() + .map(|ice| ice.get_context_element_reference()) + }) == right_side.as_ref().map(|maybe_ice| { + maybe_ice + .as_ref() + .map(|ice| ice.get_context_element_reference()) + }) { return self.render_maybe_body( ¶meterized_block.contents, new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), @@ -453,23 +454,24 @@ impl<'a> DustRenderer<'a> { }; } - let left_side = match param_map + if !param_map.contains_key("key") { + return Ok("".to_owned()); + } + let left_side = param_map .walk("key") - .map(|ice| ice.into_context_element(self, breadcrumbs)) - { - Err(WalkError::CantWalk) | Ok(None) => return Ok("".to_owned()), - Ok(Some(res)) => res, - }; + .map(|ice| ice.into_context_element(self, breadcrumbs)); let right_side = param_map .walk("value") .map(|ice| ice.into_context_element(self, breadcrumbs)); - if Ok(Some(left_side.get_context_element_reference())) - != right_side.as_ref().map(|maybe_ice| { - maybe_ice - .as_ref() - .map(|ice| ice.get_context_element_reference()) - }) - { + if left_side.as_ref().map(|maybe_ice| { + maybe_ice + .as_ref() + .map(|ice| ice.get_context_element_reference()) + }) != right_side.as_ref().map(|maybe_ice| { + maybe_ice + .as_ref() + .map(|ice| ice.get_context_element_reference()) + }) { return self.render_maybe_body( ¶meterized_block.contents, new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), @@ -493,19 +495,18 @@ impl<'a> DustRenderer<'a> { let param_map = ParametersContext::new(self, breadcrumbs, ¶meterized_block.params); - let left_side = match param_map + if !param_map.contains_key("key") { + return Ok("".to_owned()); + } + let left_side = param_map .walk("key") - .map(|ice| ice.into_context_element(self, breadcrumbs)) - { - Err(WalkError::CantWalk) | Ok(None) => return Ok("".to_owned()), - Ok(Some(res)) => res, - }; + .map(|ice| ice.into_context_element(self, breadcrumbs)); let right_side = param_map .walk("value") .map(|ice| ice.into_context_element(self, breadcrumbs)); - match (right_side) { - Ok(Some(right_side_unwrapped)) => { - if left_side.get_context_element_reference() + match (left_side, right_side) { + (Ok(Some(left_side_unwrapped)), Ok(Some(right_side_unwrapped))) => { + if left_side_unwrapped.get_context_element_reference() > right_side_unwrapped.get_context_element_reference() { return self.render_maybe_body( @@ -540,19 +541,18 @@ impl<'a> DustRenderer<'a> { let param_map = ParametersContext::new(self, breadcrumbs, ¶meterized_block.params); - let left_side = match param_map + if !param_map.contains_key("key") { + return Ok("".to_owned()); + } + let left_side = param_map .walk("key") - .map(|ice| ice.into_context_element(self, breadcrumbs)) - { - Err(WalkError::CantWalk) | Ok(None) => return Ok("".to_owned()), - Ok(Some(res)) => res, - }; + .map(|ice| ice.into_context_element(self, breadcrumbs)); let right_side = param_map .walk("value") .map(|ice| ice.into_context_element(self, breadcrumbs)); - match (right_side) { - Ok(Some(right_side_unwrapped)) => { - if left_side.get_context_element_reference() + match (left_side, right_side) { + (Ok(Some(left_side_unwrapped)), Ok(Some(right_side_unwrapped))) => { + if left_side_unwrapped.get_context_element_reference() >= right_side_unwrapped.get_context_element_reference() { return self.render_maybe_body( @@ -587,19 +587,18 @@ impl<'a> DustRenderer<'a> { let param_map = ParametersContext::new(self, breadcrumbs, ¶meterized_block.params); - let left_side = match param_map + if !param_map.contains_key("key") { + return Ok("".to_owned()); + } + let left_side = param_map .walk("key") - .map(|ice| ice.into_context_element(self, breadcrumbs)) - { - Err(WalkError::CantWalk) | Ok(None) => return Ok("".to_owned()), - Ok(Some(res)) => res, - }; + .map(|ice| ice.into_context_element(self, breadcrumbs)); let right_side = param_map .walk("value") .map(|ice| ice.into_context_element(self, breadcrumbs)); - match (right_side) { - Ok(Some(right_side_unwrapped)) => { - if left_side.get_context_element_reference() + match (left_side, right_side) { + (Ok(Some(left_side_unwrapped)), Ok(Some(right_side_unwrapped))) => { + if left_side_unwrapped.get_context_element_reference() < right_side_unwrapped.get_context_element_reference() { return self.render_maybe_body( @@ -634,19 +633,18 @@ impl<'a> DustRenderer<'a> { let param_map = ParametersContext::new(self, breadcrumbs, ¶meterized_block.params); - let left_side = match param_map + if !param_map.contains_key("key") { + return Ok("".to_owned()); + } + let left_side = param_map .walk("key") - .map(|ice| ice.into_context_element(self, breadcrumbs)) - { - Err(WalkError::CantWalk) | Ok(None) => return Ok("".to_owned()), - Ok(Some(res)) => res, - }; + .map(|ice| ice.into_context_element(self, breadcrumbs)); let right_side = param_map .walk("value") .map(|ice| ice.into_context_element(self, breadcrumbs)); - match (right_side) { - Ok(Some(right_side_unwrapped)) => { - if left_side.get_context_element_reference() + match (left_side, right_side) { + (Ok(Some(left_side_unwrapped)), Ok(Some(right_side_unwrapped))) => { + if left_side_unwrapped.get_context_element_reference() <= right_side_unwrapped.get_context_element_reference() { return self.render_maybe_body( From 4dee230780e5646b4949f47e7249844fbbe26124 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 7 Jun 2020 01:11:40 -0400 Subject: [PATCH 65/67] Add some test cases for comparing equality on reference parameters. --- js/test_cases/reference_parameters/input1.json | 11 ++++++++++- js/test_cases/reference_parameters/main.dust | 9 +++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/js/test_cases/reference_parameters/input1.json b/js/test_cases/reference_parameters/input1.json index 3c704cd..7a597e1 100644 --- a/js/test_cases/reference_parameters/input1.json +++ b/js/test_cases/reference_parameters/input1.json @@ -19,5 +19,14 @@ "bar" ] } - ] + ], + "some_object": { + "foo": "bar" + }, + "some_same_object": { + "foo": "bar" + }, + "some_different_object": { + "foo": "baz" + } } diff --git a/js/test_cases/reference_parameters/main.dust b/js/test_cases/reference_parameters/main.dust index 0b553cb..4308d5a 100644 --- a/js/test_cases/reference_parameters/main.dust +++ b/js/test_cases/reference_parameters/main.dust @@ -53,3 +53,12 @@ Reference Parameters{~n} {#truthy name="chris" pet="{petname}" petname="{deeperpetname}" deeperpetname="fluffy"} Hello {name}, nice {pet}{~n} {/truthy} + +Equality{~n} +========{~n} +{@eq key=some_object value=some_object}some_object equals some_object{:else}some_object does not equal some_object{/eq}{~n} +{@eq key=some_object value=some_same_object}some_object equals some_same_object{:else}some_object does not equal some_same_object{/eq}{~n} +{@eq key=some_object value="{some_object}"}some_object equals reference(some_object){:else}some_object does not equal reference(some_object){/eq}{~n} +{@eq key="{some_object}" value="{some_object}"}reference(some_object) equals reference(some_object){:else}reference(some_object) does not equal reference(some_object){/eq}{~n} +{@eq key="{some_object}" value="{some_same_object}"}reference(some_object) equals reference(some_same_object){:else}reference(some_object) does not equal reference(some_same_object){/eq}{~n} +{@eq key="{some_object}" value="{some_different_object}"}reference(some_object) equals reference(some_different_object){:else}reference(some_object) does not equal reference(some_different_object){/eq}{~n} From 865cba6f4ed706761c167c2804113e2a3dbca2c1 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 7 Jun 2020 01:15:45 -0400 Subject: [PATCH 66/67] Add a test proving that renamed variables are still equal, so its not just based on path. --- js/test_cases/helpers_eq/main.dust | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/js/test_cases/helpers_eq/main.dust b/js/test_cases/helpers_eq/main.dust index 3d182d5..aad3f75 100644 --- a/js/test_cases/helpers_eq/main.dust +++ b/js/test_cases/helpers_eq/main.dust @@ -21,3 +21,9 @@ {@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} + +Do objects with different paths referencing the same variable match?{~n} +===================================================================={~n} +{#int renamed=some_obj} + {@eq key=some_obj value=renamed}some_obj equals renamed{:else}some_obj does not equal renamed{/eq}{~n} +{/int} From 4e1259f1c70d9886387f3e53ec9bcfafe0796673 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 7 Jun 2020 01:35:58 -0400 Subject: [PATCH 67/67] All tests working. --- src/renderer/renderer.rs | 111 +++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 56 deletions(-) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index b6cc041..6759c53 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -11,6 +11,7 @@ use crate::parser::Template; use crate::parser::TemplateElement; use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement; use crate::renderer::context_element::ContextElement; +use crate::renderer::context_element::IceResult; use crate::renderer::context_element::IntoContextElement; use crate::renderer::context_element::Walkable; use crate::renderer::errors::CompileError; @@ -376,24 +377,6 @@ impl<'a> DustRenderer<'a> { let param_map = ParametersContext::new(self, breadcrumbs, ¶meterized_block.params); - // Special case: when comparing two RVPaths, if the - // path is equal then dust assumes the values are - // equal (otherwise, non-scalar values are - // automatically not equal) - if Self::are_paths_identical(¶m_map, "key", "value") { - return match ¶meterized_block.contents { - None => Ok("".to_owned()), - Some(body) => { - let rendered_content = self.render_body( - body, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - )?; - Ok(rendered_content) - } - }; - } - if !param_map.contains_key("key") { return Ok("".to_owned()); } @@ -403,15 +386,22 @@ impl<'a> DustRenderer<'a> { let right_side = param_map .walk("value") .map(|ice| ice.into_context_element(self, breadcrumbs)); - if left_side.as_ref().map(|maybe_ice| { - maybe_ice - .as_ref() - .map(|ice| ice.get_context_element_reference()) - }) == right_side.as_ref().map(|maybe_ice| { - maybe_ice - .as_ref() - .map(|ice| ice.get_context_element_reference()) - }) { + // Special case: when comparing two RVPaths, if the + // path points to the same value then they are + // equal. This is particularly important for objects + // which compare memory locations rather than contents + // (javascript object equality). + if Self::new_are_paths_identical(&left_side, &right_side) + || left_side.as_ref().map(|maybe_ice| { + maybe_ice + .as_ref() + .map(|ice| ice.get_context_element_reference()) + }) == right_side.as_ref().map(|maybe_ice| { + maybe_ice + .as_ref() + .map(|ice| ice.get_context_element_reference()) + }) + { return self.render_maybe_body( ¶meterized_block.contents, new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), @@ -436,24 +426,6 @@ impl<'a> DustRenderer<'a> { let param_map = ParametersContext::new(self, breadcrumbs, ¶meterized_block.params); - // Special case: when comparing two RVPaths, if the - // path is equal then dust assumes the values are - // equal (otherwise, non-scalar values are - // automatically not equal) - if Self::are_paths_identical(¶m_map, "key", "value") { - return match ¶meterized_block.else_contents { - None => Ok("".to_owned()), - Some(body) => { - let rendered_content = self.render_body( - body, - new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - blocks, - )?; - Ok(rendered_content) - } - }; - } - if !param_map.contains_key("key") { return Ok("".to_owned()); } @@ -463,23 +435,30 @@ impl<'a> DustRenderer<'a> { let right_side = param_map .walk("value") .map(|ice| ice.into_context_element(self, breadcrumbs)); - if left_side.as_ref().map(|maybe_ice| { - maybe_ice - .as_ref() - .map(|ice| ice.get_context_element_reference()) - }) != right_side.as_ref().map(|maybe_ice| { - maybe_ice - .as_ref() - .map(|ice| ice.get_context_element_reference()) - }) { + // Special case: when comparing two RVPaths, if the + // path points to the same value then they are + // equal. This is particularly important for objects + // which compare memory locations rather than contents + // (javascript object equality). + if Self::new_are_paths_identical(&left_side, &right_side) + || left_side.as_ref().map(|maybe_ice| { + maybe_ice + .as_ref() + .map(|ice| ice.get_context_element_reference()) + }) == right_side.as_ref().map(|maybe_ice| { + maybe_ice + .as_ref() + .map(|ice| ice.get_context_element_reference()) + }) + { return self.render_maybe_body( - ¶meterized_block.contents, + ¶meterized_block.else_contents, new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), blocks, ); } else { return self.render_maybe_body( - ¶meterized_block.else_contents, + ¶meterized_block.contents, new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), blocks, ); @@ -800,6 +779,26 @@ impl<'a> DustRenderer<'a> { } } + fn new_are_paths_identical<'b>( + left_side: &Result>, WalkError>, + right_side: &Result>, WalkError>, + ) -> bool { + let left_resolved = left_side.as_ref().map(|maybe_ice| { + maybe_ice + .as_ref() + .map(|ice| ice.get_context_element_reference()) + }); + let right_resolved = right_side.as_ref().map(|maybe_ice| { + maybe_ice + .as_ref() + .map(|ice| ice.get_context_element_reference()) + }); + match (left_resolved, right_resolved) { + (Ok(Some(lce)), Ok(Some(rce))) => lce as *const _ == rce as *const _, + _ => false, + } + } + fn preprocess_filters(filters: &Vec) -> Vec { let mut final_filters: Vec = filters .into_iter()