From 6845df7f966bd7bd0400fd06a59f13ecfed2475f Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Mon, 25 May 2020 19:11:14 -0400 Subject: [PATCH] First attempt at an implementation of the new breadcrumbs for blocks/inline partials. --- src/renderer/inline_partial_tree.rs | 50 +++++++++++--- src/renderer/renderer.rs | 102 +++++++++++++++++++++++----- 2 files changed, 125 insertions(+), 27 deletions(-) diff --git a/src/renderer/inline_partial_tree.rs b/src/renderer/inline_partial_tree.rs index d0a2020..0dabb39 100644 --- a/src/renderer/inline_partial_tree.rs +++ b/src/renderer/inline_partial_tree.rs @@ -1,18 +1,23 @@ use crate::parser::Body; use crate::parser::DustTag; use crate::parser::Template; -use crate::parser::TemplateElement; +use crate::parser::{Path, TemplateElement}; use std::collections::HashMap; +pub struct InlinePartialTreeValue<'a> { + explicit_context: &'a Option>, + body: &'a Option>, +} + pub struct InlinePartialTreeElement<'a> { parent: Option<&'a InlinePartialTreeElement<'a>>, - blocks: HashMap<&'a str, &'a Option>>, + blocks: HashMap<&'a str, InlinePartialTreeValue<'a>>, } impl<'a> InlinePartialTreeElement<'a> { pub fn new( parent: Option<&'a InlinePartialTreeElement<'a>>, - blocks: HashMap<&'a str, &'a Option>>, + blocks: HashMap<&'a str, InlinePartialTreeValue<'a>>, ) -> InlinePartialTreeElement<'a> { InlinePartialTreeElement { parent: parent, @@ -20,21 +25,40 @@ impl<'a> InlinePartialTreeElement<'a> { } } - pub fn get_block(&self, name: &str) -> Option<&'a Option>> { + fn get_value(&self, name: &str) -> Option<&InlinePartialTreeValue<'a>> { match self.blocks.get(name) { None => match self.parent { None => None, - Some(parent_tree_element) => parent_tree_element.get_block(name), + Some(parent_tree_element) => parent_tree_element.get_value(name), }, Some(interior) => Some(interior), } } + + /// Recursively get the body of a block by name + /// + /// Returning a double-option because there is a functional + /// difference between not finding the block and not having a + /// body. + pub fn get_block(&self, name: &str) -> Option<&'a Option>> { + self.get_value(name).map(|interior| interior.body) + } + + /// Recursively get the explicit from the inline partial by name + /// + /// Returning a double-option because there is a functional + /// difference between not finding the block and not having a + /// body. + pub fn get_explicit_context(&self, name: &str) -> Option<&Option>> { + self.get_value(name) + .map(|interior| interior.explicit_context) + } } pub fn extract_inline_partials<'a>( template: &'a Template<'a>, -) -> HashMap<&'a str, &'a Option>> { - let mut blocks: HashMap<&'a str, &'a Option>> = HashMap::new(); +) -> HashMap<&'a str, InlinePartialTreeValue<'a>> { + let mut blocks: HashMap<&'a str, InlinePartialTreeValue<'a>> = HashMap::new(); extract_inline_partials_from_body(&mut blocks, &template.contents); @@ -42,7 +66,7 @@ pub fn extract_inline_partials<'a>( } fn extract_inline_partials_from_body<'a, 'b>( - blocks: &'b mut HashMap<&'a str, &'a Option>>, + blocks: &'b mut HashMap<&'a str, InlinePartialTreeValue<'a>>, body: &'a Body<'a>, ) { for elem in &body.elements { @@ -57,7 +81,7 @@ fn extract_inline_partials_from_body<'a, 'b>( } fn extract_inline_partials_from_tag<'a, 'b>( - blocks: &'b mut HashMap<&'a str, &'a Option>>, + blocks: &'b mut HashMap<&'a str, InlinePartialTreeValue<'a>>, tag: &'a DustTag, ) { match tag { @@ -97,7 +121,13 @@ fn extract_inline_partials_from_tag<'a, 'b>( } DustTag::DTPartial(..) => (), DustTag::DTInlinePartial(named_block) => { - blocks.insert(&named_block.name, &named_block.contents); + blocks.insert( + &named_block.name, + InlinePartialTreeValue { + explicit_context: &named_block.explicit_context, + body: &named_block.contents, + }, + ); } DustTag::DTBlock(..) => (), DustTag::DTHelperEquals(parameterized_block) => { diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 7ae9c64..e41bec0 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -76,14 +76,18 @@ impl<'a> DustRenderer<'a> { }; let extracted_inline_partials = extract_inline_partials(main_template); let new_blocks = InlinePartialTreeElement::new(blocks, extracted_inline_partials); - self.render_body(&main_template.contents, breadcrumbs, &new_blocks) + 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 ContextElement>, - blocks: &'a InlinePartialTreeElement<'a>, + blocks: &'a BlockContext<'a>, ) -> Result { match body { None => Ok("".to_owned()), @@ -95,7 +99,7 @@ impl<'a> DustRenderer<'a> { &'a self, body: &'a Body, breadcrumbs: &Vec<&'a dyn ContextElement>, - blocks: &'a InlinePartialTreeElement<'a>, + blocks: &'a BlockContext<'a>, ) -> Result { let mut output = String::new(); for elem in &body.elements { @@ -115,7 +119,7 @@ impl<'a> DustRenderer<'a> { &'a self, body: &'a Vec, breadcrumbs: &Vec<&'a dyn ContextElement>, - blocks: &'a InlinePartialTreeElement<'a>, + blocks: &'a BlockContext<'a>, ) -> Result { let converted_to_template_elements: Vec> = body.into_iter().map(|e| e.into()).collect(); @@ -132,7 +136,7 @@ impl<'a> DustRenderer<'a> { &'a self, tag: &'a DustTag, breadcrumbs: &Vec<&'a dyn ContextElement>, - blocks: &'a InlinePartialTreeElement<'a>, + blocks: &'a BlockContext<'a>, ) -> Result { match tag { DustTag::DTComment(_comment) => (), @@ -165,6 +169,7 @@ impl<'a> DustRenderer<'a> { match val { Err(WalkError::CantWalk) => { let new_breadcrumbs = Self::new_breadcrumbs( + breadcrumbs, breadcrumbs, None, &container.explicit_context, @@ -190,6 +195,7 @@ impl<'a> DustRenderer<'a> { if loop_elements.is_empty() { // Scalar value let new_breadcrumbs = Self::new_breadcrumbs( + breadcrumbs, breadcrumbs, None, &container.explicit_context, @@ -211,6 +217,7 @@ impl<'a> DustRenderer<'a> { let injected_context = IterationContext::new(i, total_length); let new_breadcrumbs = Self::new_breadcrumbs( + breadcrumbs, breadcrumbs, Some(&injected_context), &container.explicit_context, @@ -236,6 +243,7 @@ impl<'a> DustRenderer<'a> { // original context before walking the path as // the context for rendering the else block let new_breadcrumbs = Self::new_breadcrumbs( + breadcrumbs, breadcrumbs, None, &container.explicit_context, @@ -251,8 +259,13 @@ impl<'a> DustRenderer<'a> { } } DustTag::DTExists(container) => { - let new_breadcrumbs = - Self::new_breadcrumbs(breadcrumbs, None, &container.explicit_context, None); + let new_breadcrumbs = Self::new_breadcrumbs( + breadcrumbs, + breadcrumbs, + None, + &container.explicit_context, + None, + ); let val = walk_path(breadcrumbs, &container.path.keys); return if val.map(|v| v.is_truthy()).unwrap_or(false) { self.render_maybe_body( @@ -269,8 +282,13 @@ impl<'a> DustRenderer<'a> { }; } DustTag::DTNotExists(container) => { - let new_breadcrumbs = - Self::new_breadcrumbs(breadcrumbs, None, &container.explicit_context, None); + let new_breadcrumbs = Self::new_breadcrumbs( + breadcrumbs, + breadcrumbs, + None, + &container.explicit_context, + None, + ); let val = walk_path(breadcrumbs, &container.path.keys); return if !val.map(|v| v.is_truthy()).unwrap_or(false) { self.render_maybe_body( @@ -289,17 +307,23 @@ impl<'a> DustRenderer<'a> { DustTag::DTPartial(partial) => { let partial_name = self.render_partial_name(&partial.name, breadcrumbs, blocks)?; if partial.params.is_empty() { - let new_breadcrumbs = - Self::new_breadcrumbs(breadcrumbs, None, &partial.explicit_context, None); + let new_breadcrumbs = Self::new_breadcrumbs( + breadcrumbs, + breadcrumbs, + None, + &partial.explicit_context, + None, + ); let rendered_content = self.render_template( &partial_name, new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - Some(blocks), + Some(blocks.blocks), )?; return Ok(rendered_content); } else { let injected_context = ParametersContext::new(breadcrumbs, &partial.params); let new_breadcrumbs = Self::new_breadcrumbs( + breadcrumbs, breadcrumbs, Some(&injected_context), &partial.explicit_context, @@ -308,7 +332,7 @@ impl<'a> DustRenderer<'a> { let rendered_content = self.render_template( &partial_name, new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), - Some(blocks), + Some(blocks.blocks), )?; return Ok(rendered_content); } @@ -319,13 +343,35 @@ impl<'a> DustRenderer<'a> { return Ok("".to_owned()); } DustTag::DTBlock(named_block) => { - return match blocks.get_block(named_block.name) { - None => self.render_maybe_body(&named_block.contents, breadcrumbs, blocks), - Some(interior) => self.render_maybe_body(interior, breadcrumbs, blocks), + let new_breadcrumbs = blocks + .blocks + .get_explicit_context(named_block.name) + .map(|explicit_context| { + Self::new_breadcrumbs( + breadcrumbs, + blocks.breadcrumbs, + None, + explicit_context, + None, + ) + }) + .flatten(); + return match blocks.blocks.get_block(named_block.name) { + None => self.render_maybe_body( + &named_block.contents, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ), + Some(interior) => self.render_maybe_body( + interior, + new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), + blocks, + ), }; } DustTag::DTHelperEquals(parameterized_block) => { let new_breadcrumbs = Self::new_breadcrumbs( + breadcrumbs, breadcrumbs, None, ¶meterized_block.explicit_context, @@ -376,6 +422,7 @@ impl<'a> DustRenderer<'a> { } DustTag::DTHelperNotEquals(parameterized_block) => { let new_breadcrumbs = Self::new_breadcrumbs( + breadcrumbs, breadcrumbs, None, ¶meterized_block.explicit_context, @@ -425,6 +472,7 @@ impl<'a> DustRenderer<'a> { } DustTag::DTHelperGreaterThan(parameterized_block) => { let new_breadcrumbs = Self::new_breadcrumbs( + breadcrumbs, breadcrumbs, None, ¶meterized_block.explicit_context, @@ -467,6 +515,7 @@ impl<'a> DustRenderer<'a> { } DustTag::DTHelperGreaterThanOrEquals(parameterized_block) => { let new_breadcrumbs = Self::new_breadcrumbs( + breadcrumbs, breadcrumbs, None, ¶meterized_block.explicit_context, @@ -509,6 +558,7 @@ impl<'a> DustRenderer<'a> { } DustTag::DTHelperLessThan(parameterized_block) => { let new_breadcrumbs = Self::new_breadcrumbs( + breadcrumbs, breadcrumbs, None, ¶meterized_block.explicit_context, @@ -551,6 +601,7 @@ impl<'a> DustRenderer<'a> { } DustTag::DTHelperLessThanOrEquals(parameterized_block) => { let new_breadcrumbs = Self::new_breadcrumbs( + breadcrumbs, breadcrumbs, None, ¶meterized_block.explicit_context, @@ -662,6 +713,16 @@ impl<'a> DustRenderer<'a> { /// This function generates a new breadcrumbs object based on the /// new context information provided. /// + /// breadcrumbs are the breadcrumbs that will be used in the final + /// breadcrumbs (unless omitted due to an explicit context) + /// + /// explicit_context_breadcrumbs are the breadcrumbs used to + /// evaluate an explicit context path. Most of the time the two + /// breadcrumbs parameters will be identical, but for + /// blocks/inline partials the explicit_context_breadcrumbs will + /// be the breadcrumbs from the start of the partial containing + /// the block. + /// /// explicit_context is for contexts specified with a `:path` /// inside a dust tag. /// @@ -692,6 +753,7 @@ impl<'a> DustRenderer<'a> { /// ``` fn new_breadcrumbs<'b>( breadcrumbs: &'b Vec<&'b dyn ContextElement>, + explicit_context_breadcrumbs: &'b Vec<&'b dyn ContextElement>, injected_context: Option<&'b dyn ContextElement>, explicit_context: &Option>, new_context_element: Option<&'b dyn ContextElement>, @@ -721,7 +783,7 @@ impl<'a> DustRenderer<'a> { } }); explicit_context.as_ref().map(|path| { - walk_path(breadcrumbs, &path.keys).map(|val| { + walk_path(explicit_context_breadcrumbs, &path.keys).map(|val| { if val.is_truthy() { new_stack.push(val) } @@ -732,6 +794,12 @@ impl<'a> DustRenderer<'a> { } } +struct BlockContext<'a> { + /// The breadcrumbs at the time of entering the current partial + breadcrumbs: &'a Vec<&'a dyn ContextElement>, + blocks: &'a InlinePartialTreeElement<'a>, +} + #[cfg(test)] mod tests { use super::*;