First attempt at an implementation of the new breadcrumbs for blocks/inline partials.

This commit is contained in:
Tom Alexander 2020-05-25 19:11:14 -04:00
parent 83623897af
commit 6845df7f96
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
2 changed files with 125 additions and 27 deletions

View File

@ -1,18 +1,23 @@
use crate::parser::Body; use crate::parser::Body;
use crate::parser::DustTag; use crate::parser::DustTag;
use crate::parser::Template; use crate::parser::Template;
use crate::parser::TemplateElement; use crate::parser::{Path, TemplateElement};
use std::collections::HashMap; use std::collections::HashMap;
pub struct InlinePartialTreeValue<'a> {
explicit_context: &'a Option<Path<'a>>,
body: &'a Option<Body<'a>>,
}
pub struct InlinePartialTreeElement<'a> { pub struct InlinePartialTreeElement<'a> {
parent: Option<&'a InlinePartialTreeElement<'a>>, parent: Option<&'a InlinePartialTreeElement<'a>>,
blocks: HashMap<&'a str, &'a Option<Body<'a>>>, blocks: HashMap<&'a str, InlinePartialTreeValue<'a>>,
} }
impl<'a> InlinePartialTreeElement<'a> { impl<'a> InlinePartialTreeElement<'a> {
pub fn new( pub fn new(
parent: Option<&'a InlinePartialTreeElement<'a>>, parent: Option<&'a InlinePartialTreeElement<'a>>,
blocks: HashMap<&'a str, &'a Option<Body<'a>>>, blocks: HashMap<&'a str, InlinePartialTreeValue<'a>>,
) -> InlinePartialTreeElement<'a> { ) -> InlinePartialTreeElement<'a> {
InlinePartialTreeElement { InlinePartialTreeElement {
parent: parent, parent: parent,
@ -20,21 +25,40 @@ impl<'a> InlinePartialTreeElement<'a> {
} }
} }
pub fn get_block(&self, name: &str) -> Option<&'a Option<Body<'a>>> { fn get_value(&self, name: &str) -> Option<&InlinePartialTreeValue<'a>> {
match self.blocks.get(name) { match self.blocks.get(name) {
None => match self.parent { None => match self.parent {
None => None, 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), 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<Body<'a>>> {
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<Path<'a>>> {
self.get_value(name)
.map(|interior| interior.explicit_context)
}
} }
pub fn extract_inline_partials<'a>( pub fn extract_inline_partials<'a>(
template: &'a Template<'a>, template: &'a Template<'a>,
) -> HashMap<&'a str, &'a Option<Body<'a>>> { ) -> HashMap<&'a str, InlinePartialTreeValue<'a>> {
let mut blocks: HashMap<&'a str, &'a Option<Body<'a>>> = HashMap::new(); let mut blocks: HashMap<&'a str, InlinePartialTreeValue<'a>> = HashMap::new();
extract_inline_partials_from_body(&mut blocks, &template.contents); 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>( fn extract_inline_partials_from_body<'a, 'b>(
blocks: &'b mut HashMap<&'a str, &'a Option<Body<'a>>>, blocks: &'b mut HashMap<&'a str, InlinePartialTreeValue<'a>>,
body: &'a Body<'a>, body: &'a Body<'a>,
) { ) {
for elem in &body.elements { 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>( fn extract_inline_partials_from_tag<'a, 'b>(
blocks: &'b mut HashMap<&'a str, &'a Option<Body<'a>>>, blocks: &'b mut HashMap<&'a str, InlinePartialTreeValue<'a>>,
tag: &'a DustTag, tag: &'a DustTag,
) { ) {
match tag { match tag {
@ -97,7 +121,13 @@ fn extract_inline_partials_from_tag<'a, 'b>(
} }
DustTag::DTPartial(..) => (), DustTag::DTPartial(..) => (),
DustTag::DTInlinePartial(named_block) => { 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::DTBlock(..) => (),
DustTag::DTHelperEquals(parameterized_block) => { DustTag::DTHelperEquals(parameterized_block) => {

View File

@ -76,14 +76,18 @@ impl<'a> DustRenderer<'a> {
}; };
let extracted_inline_partials = extract_inline_partials(main_template); let extracted_inline_partials = extract_inline_partials(main_template);
let new_blocks = InlinePartialTreeElement::new(blocks, extracted_inline_partials); 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( fn render_maybe_body(
&'a self, &'a self,
body: &'a Option<Body>, body: &'a Option<Body>,
breadcrumbs: &Vec<&'a dyn ContextElement>, breadcrumbs: &Vec<&'a dyn ContextElement>,
blocks: &'a InlinePartialTreeElement<'a>, blocks: &'a BlockContext<'a>,
) -> Result<String, RenderError> { ) -> Result<String, RenderError> {
match body { match body {
None => Ok("".to_owned()), None => Ok("".to_owned()),
@ -95,7 +99,7 @@ impl<'a> DustRenderer<'a> {
&'a self, &'a self,
body: &'a Body, body: &'a Body,
breadcrumbs: &Vec<&'a dyn ContextElement>, breadcrumbs: &Vec<&'a dyn ContextElement>,
blocks: &'a InlinePartialTreeElement<'a>, blocks: &'a BlockContext<'a>,
) -> Result<String, RenderError> { ) -> Result<String, RenderError> {
let mut output = String::new(); let mut output = String::new();
for elem in &body.elements { for elem in &body.elements {
@ -115,7 +119,7 @@ impl<'a> DustRenderer<'a> {
&'a self, &'a self,
body: &'a Vec<PartialNameElement>, body: &'a Vec<PartialNameElement>,
breadcrumbs: &Vec<&'a dyn ContextElement>, breadcrumbs: &Vec<&'a dyn ContextElement>,
blocks: &'a InlinePartialTreeElement<'a>, blocks: &'a BlockContext<'a>,
) -> Result<String, RenderError> { ) -> Result<String, RenderError> {
let converted_to_template_elements: Vec<TemplateElement<'a>> = let converted_to_template_elements: Vec<TemplateElement<'a>> =
body.into_iter().map(|e| e.into()).collect(); body.into_iter().map(|e| e.into()).collect();
@ -132,7 +136,7 @@ impl<'a> DustRenderer<'a> {
&'a self, &'a self,
tag: &'a DustTag, tag: &'a DustTag,
breadcrumbs: &Vec<&'a dyn ContextElement>, breadcrumbs: &Vec<&'a dyn ContextElement>,
blocks: &'a InlinePartialTreeElement<'a>, blocks: &'a BlockContext<'a>,
) -> Result<String, RenderError> { ) -> Result<String, RenderError> {
match tag { match tag {
DustTag::DTComment(_comment) => (), DustTag::DTComment(_comment) => (),
@ -165,6 +169,7 @@ impl<'a> DustRenderer<'a> {
match val { match val {
Err(WalkError::CantWalk) => { Err(WalkError::CantWalk) => {
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs(
breadcrumbs,
breadcrumbs, breadcrumbs,
None, None,
&container.explicit_context, &container.explicit_context,
@ -190,6 +195,7 @@ impl<'a> DustRenderer<'a> {
if loop_elements.is_empty() { if loop_elements.is_empty() {
// Scalar value // Scalar value
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs(
breadcrumbs,
breadcrumbs, breadcrumbs,
None, None,
&container.explicit_context, &container.explicit_context,
@ -211,6 +217,7 @@ impl<'a> DustRenderer<'a> {
let injected_context = let injected_context =
IterationContext::new(i, total_length); IterationContext::new(i, total_length);
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs(
breadcrumbs,
breadcrumbs, breadcrumbs,
Some(&injected_context), Some(&injected_context),
&container.explicit_context, &container.explicit_context,
@ -236,6 +243,7 @@ impl<'a> DustRenderer<'a> {
// original context before walking the path as // original context before walking the path as
// the context for rendering the else block // the context for rendering the else block
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs(
breadcrumbs,
breadcrumbs, breadcrumbs,
None, None,
&container.explicit_context, &container.explicit_context,
@ -251,8 +259,13 @@ impl<'a> DustRenderer<'a> {
} }
} }
DustTag::DTExists(container) => { DustTag::DTExists(container) => {
let new_breadcrumbs = let new_breadcrumbs = Self::new_breadcrumbs(
Self::new_breadcrumbs(breadcrumbs, None, &container.explicit_context, None); breadcrumbs,
breadcrumbs,
None,
&container.explicit_context,
None,
);
let val = walk_path(breadcrumbs, &container.path.keys); let val = walk_path(breadcrumbs, &container.path.keys);
return if val.map(|v| v.is_truthy()).unwrap_or(false) { return if val.map(|v| v.is_truthy()).unwrap_or(false) {
self.render_maybe_body( self.render_maybe_body(
@ -269,8 +282,13 @@ impl<'a> DustRenderer<'a> {
}; };
} }
DustTag::DTNotExists(container) => { DustTag::DTNotExists(container) => {
let new_breadcrumbs = let new_breadcrumbs = Self::new_breadcrumbs(
Self::new_breadcrumbs(breadcrumbs, None, &container.explicit_context, None); breadcrumbs,
breadcrumbs,
None,
&container.explicit_context,
None,
);
let val = walk_path(breadcrumbs, &container.path.keys); let val = walk_path(breadcrumbs, &container.path.keys);
return if !val.map(|v| v.is_truthy()).unwrap_or(false) { return if !val.map(|v| v.is_truthy()).unwrap_or(false) {
self.render_maybe_body( self.render_maybe_body(
@ -289,17 +307,23 @@ impl<'a> DustRenderer<'a> {
DustTag::DTPartial(partial) => { DustTag::DTPartial(partial) => {
let partial_name = self.render_partial_name(&partial.name, breadcrumbs, blocks)?; let partial_name = self.render_partial_name(&partial.name, breadcrumbs, blocks)?;
if partial.params.is_empty() { if partial.params.is_empty() {
let new_breadcrumbs = let new_breadcrumbs = Self::new_breadcrumbs(
Self::new_breadcrumbs(breadcrumbs, None, &partial.explicit_context, None); breadcrumbs,
breadcrumbs,
None,
&partial.explicit_context,
None,
);
let rendered_content = self.render_template( let rendered_content = self.render_template(
&partial_name, &partial_name,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
Some(blocks), Some(blocks.blocks),
)?; )?;
return Ok(rendered_content); return Ok(rendered_content);
} else { } else {
let injected_context = ParametersContext::new(breadcrumbs, &partial.params); let injected_context = ParametersContext::new(breadcrumbs, &partial.params);
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs(
breadcrumbs,
breadcrumbs, breadcrumbs,
Some(&injected_context), Some(&injected_context),
&partial.explicit_context, &partial.explicit_context,
@ -308,7 +332,7 @@ impl<'a> DustRenderer<'a> {
let rendered_content = self.render_template( let rendered_content = self.render_template(
&partial_name, &partial_name,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
Some(blocks), Some(blocks.blocks),
)?; )?;
return Ok(rendered_content); return Ok(rendered_content);
} }
@ -319,13 +343,35 @@ impl<'a> DustRenderer<'a> {
return Ok("".to_owned()); return Ok("".to_owned());
} }
DustTag::DTBlock(named_block) => { DustTag::DTBlock(named_block) => {
return match blocks.get_block(named_block.name) { let new_breadcrumbs = blocks
None => self.render_maybe_body(&named_block.contents, breadcrumbs, blocks), .blocks
Some(interior) => self.render_maybe_body(interior, breadcrumbs, 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) => { DustTag::DTHelperEquals(parameterized_block) => {
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs(
breadcrumbs,
breadcrumbs, breadcrumbs,
None, None,
&parameterized_block.explicit_context, &parameterized_block.explicit_context,
@ -376,6 +422,7 @@ impl<'a> DustRenderer<'a> {
} }
DustTag::DTHelperNotEquals(parameterized_block) => { DustTag::DTHelperNotEquals(parameterized_block) => {
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs(
breadcrumbs,
breadcrumbs, breadcrumbs,
None, None,
&parameterized_block.explicit_context, &parameterized_block.explicit_context,
@ -425,6 +472,7 @@ impl<'a> DustRenderer<'a> {
} }
DustTag::DTHelperGreaterThan(parameterized_block) => { DustTag::DTHelperGreaterThan(parameterized_block) => {
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs(
breadcrumbs,
breadcrumbs, breadcrumbs,
None, None,
&parameterized_block.explicit_context, &parameterized_block.explicit_context,
@ -467,6 +515,7 @@ impl<'a> DustRenderer<'a> {
} }
DustTag::DTHelperGreaterThanOrEquals(parameterized_block) => { DustTag::DTHelperGreaterThanOrEquals(parameterized_block) => {
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs(
breadcrumbs,
breadcrumbs, breadcrumbs,
None, None,
&parameterized_block.explicit_context, &parameterized_block.explicit_context,
@ -509,6 +558,7 @@ impl<'a> DustRenderer<'a> {
} }
DustTag::DTHelperLessThan(parameterized_block) => { DustTag::DTHelperLessThan(parameterized_block) => {
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs(
breadcrumbs,
breadcrumbs, breadcrumbs,
None, None,
&parameterized_block.explicit_context, &parameterized_block.explicit_context,
@ -551,6 +601,7 @@ impl<'a> DustRenderer<'a> {
} }
DustTag::DTHelperLessThanOrEquals(parameterized_block) => { DustTag::DTHelperLessThanOrEquals(parameterized_block) => {
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs(
breadcrumbs,
breadcrumbs, breadcrumbs,
None, None,
&parameterized_block.explicit_context, &parameterized_block.explicit_context,
@ -662,6 +713,16 @@ impl<'a> DustRenderer<'a> {
/// This function generates a new breadcrumbs object based on the /// This function generates a new breadcrumbs object based on the
/// new context information provided. /// 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` /// explicit_context is for contexts specified with a `:path`
/// inside a dust tag. /// inside a dust tag.
/// ///
@ -692,6 +753,7 @@ impl<'a> DustRenderer<'a> {
/// ``` /// ```
fn new_breadcrumbs<'b>( fn new_breadcrumbs<'b>(
breadcrumbs: &'b Vec<&'b dyn ContextElement>, breadcrumbs: &'b Vec<&'b dyn ContextElement>,
explicit_context_breadcrumbs: &'b Vec<&'b dyn ContextElement>,
injected_context: Option<&'b dyn ContextElement>, injected_context: Option<&'b dyn ContextElement>,
explicit_context: &Option<Path<'b>>, explicit_context: &Option<Path<'b>>,
new_context_element: Option<&'b dyn ContextElement>, new_context_element: Option<&'b dyn ContextElement>,
@ -721,7 +783,7 @@ impl<'a> DustRenderer<'a> {
} }
}); });
explicit_context.as_ref().map(|path| { 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() { if val.is_truthy() {
new_stack.push(val) 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;