Merge branch 'blocks' into render

master
Tom Alexander 4 years ago
commit f9212d1fe4
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE

@ -33,12 +33,12 @@ while read -r test_group; do
test_case_name=${test_case_file_name%.*}
set +e
(
if cmp -s <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust') node "$DIR/dustjs_shim.js" < "$test_case") <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust') "$DIR/../target/debug/duster-cli" < "$test_case"); then
if cmp -s <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) node "$DIR/dustjs_shim.js" < "$test_case") <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) "$DIR/../target/debug/duster-cli" < "$test_case"); then
echo "$test_group_name::$test_case_name PASSED"
else
echo "$test_group_name::$test_case_name FAILED"
if [ $show_diff -eq 1 ]; then
diff --label "dustjs-linked" --label "duster" <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust') node "$DIR/dustjs_shim.js" < "$test_case") <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust') "$DIR/../target/debug/duster-cli" < "$test_case")
diff --label "dustjs-linked" --label "duster" <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) node "$DIR/dustjs_shim.js" < "$test_case" 2>/dev/null ) <(xargs -a <(find "$test_group" -maxdepth 1 -mindepth 1 -type f -name 'main.dust'; find "$test_group" -maxdepth 1 -mindepth 1 -type f -name '*.dust' ! -name 'main.dust' | sort) "$DIR/../target/debug/duster-cli" < "$test_case" 2>/dev/null )
fi
exit 1
fi

@ -0,0 +1 @@
The block content is from {+inject/}

@ -0,0 +1,4 @@
{"people": [
{"name": "Alice", "item": "cat"},
{"name": "Bob"}
]}

@ -0,0 +1,2 @@
{<inject}Before Partial{/inject}
{>"base"/}

@ -0,0 +1 @@
The block content is from {+inject/}

@ -0,0 +1,14 @@
{
"people": [
{
"name": "Alice",
"item": "cat"
},
{
"name": "Bob"
}
],
"pet": {
"name": "fluffy"
}
}

@ -0,0 +1,6 @@
{#pet}
{>"base"/}
{/pet}
{#people}
{<inject}by {name}{/inject}
{/people}

@ -0,0 +1 @@
The block content is from {+inject/}{~n}

@ -0,0 +1,2 @@
{<inject}beta{/inject}
{>"alpha"/}

@ -0,0 +1,4 @@
{"people": [
{"name": "Alice", "item": "cat"},
{"name": "Bob"}
]}

@ -0,0 +1,3 @@
{>"beta"/}
{>"alpha"/}
{<inject}main{/inject}

@ -0,0 +1,2 @@
{<inject}beta{/inject}
{>"alpha"/}

@ -0,0 +1,4 @@
{"people": [
{"name": "Alice", "item": "cat"},
{"name": "Bob"}
]}

@ -0,0 +1,3 @@
The block content is from {+inject/}{~n}
{>"beta"/}
The block content is from {+inject/}{~n}

@ -0,0 +1,9 @@
Blocks seem to be rendered with the last inline partial of the same name.
Blocks appear to be able to be registered in a loop, however, their context is defined by the context during the call to the block `{+}` as opposed to when their override is defined by an inline partial `{<}`.
Even if the surrounding dust would prevent a section from being rendered, the inline context is still registered, which makes me think inline contexts are parsed out in a single pass regardless of the context.
Inline partials in sub-templates do not seem to bubble up to blocks in the higher templates.
After the inverse register order test it seems that it takes the last inline partial with that name at that level, walking up the tree of partials. So each file will have exactly one value for each block name, consisting of the final inline partial with that name. Then when rendering the block, it will walk up the tree just like the context tree.

@ -0,0 +1 @@
The block content is from {+inject/}

@ -0,0 +1,4 @@
{"people": [
{"name": "Alice", "item": "cat"},
{"name": "Bob"}
]}

@ -0,0 +1,3 @@
{<inject}Before Partial{/inject}
{>"base"/}
{<inject}After Partial{/inject}

@ -0,0 +1 @@
The block content is from {+inject/}

@ -0,0 +1,14 @@
{
"people": [
{
"name": "Alice",
"item": "cat"
},
{
"name": "Bob"
}
],
"pet": {
"name": "fluffy"
}
}

@ -0,0 +1,10 @@
{#pet}
{>"base"/}
{/pet}
{#people}
{<inject}by {name}{/inject}
{/people}
{#cake}
OMG CAKE
{<inject}with cake{/inject}
{/cake}

@ -100,8 +100,8 @@ pub struct Container<'a> {
#[derive(Clone, Debug, PartialEq)]
pub struct NamedBlock<'a> {
name: &'a str,
contents: Option<Body<'a>>,
pub name: &'a str,
pub contents: Option<Body<'a>>,
}
#[derive(Clone, Debug, PartialEq)]

@ -0,0 +1,104 @@
use crate::parser::Body;
use crate::parser::DustTag;
use crate::parser::Template;
use crate::parser::TemplateElement;
use std::collections::HashMap;
pub struct InlinePartialTreeElement<'a> {
parent: Option<&'a InlinePartialTreeElement<'a>>,
blocks: HashMap<&'a str, &'a Option<Body<'a>>>,
}
impl<'a> InlinePartialTreeElement<'a> {
pub fn new(
parent: Option<&'a InlinePartialTreeElement<'a>>,
blocks: HashMap<&'a str, &'a Option<Body<'a>>>,
) -> InlinePartialTreeElement<'a> {
InlinePartialTreeElement {
parent: parent,
blocks: blocks,
}
}
pub fn get_block(&self, name: &str) -> Option<&'a Option<Body<'a>>> {
match self.blocks.get(name) {
None => match self.parent {
None => None,
Some(parent_tree_element) => parent_tree_element.get_block(name),
},
Some(interior) => Some(interior),
}
}
}
pub fn extract_inline_partials<'a>(
template: &'a Template<'a>,
) -> HashMap<&'a str, &'a Option<Body<'a>>> {
let mut blocks: HashMap<&'a str, &'a Option<Body<'a>>> = HashMap::new();
extract_inline_partials_from_body(&mut blocks, &template.contents);
blocks
}
fn extract_inline_partials_from_body<'a, 'b>(
blocks: &'b mut HashMap<&'a str, &'a Option<Body<'a>>>,
body: &'a Body<'a>,
) {
for elem in &body.elements {
match elem {
TemplateElement::TEIgnoredWhitespace(_) => (),
TemplateElement::TESpan(span) => (),
TemplateElement::TETag(dt) => {
extract_inline_partials_from_tag(blocks, dt);
}
}
}
}
fn extract_inline_partials_from_tag<'a, 'b>(
blocks: &'b mut HashMap<&'a str, &'a Option<Body<'a>>>,
tag: &'a DustTag,
) {
match tag {
DustTag::DTComment(..) => (),
DustTag::DTSpecial(..) => (),
DustTag::DTReference(..) => (),
DustTag::DTSection(container) => {
match &container.contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
match &container.else_contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
}
DustTag::DTExists(container) => {
match &container.contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
match &container.else_contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
}
DustTag::DTNotExists(container) => {
match &container.contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
match &container.else_contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
}
DustTag::DTPartial(..) => (),
DustTag::DTInlinePartial(named_block) => {
blocks.insert(&named_block.name, &named_block.contents);
}
DustTag::DTBlock(..) => (),
_ => (), // TODO: Implement the rest
}
}

@ -2,6 +2,7 @@
mod context_element;
mod errors;
mod inline_partial_tree;
mod parameters_context;
mod renderer;
mod walking;

@ -8,6 +8,8 @@ use crate::renderer::context_element::ContextElement;
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::parameters_context::ParametersContext;
use crate::renderer::walking::walk_path;
use std::collections::HashMap;
@ -52,6 +54,15 @@ impl<'a> DustRenderer<'a> {
&'a self,
name: &str,
breadcrumbs: &Vec<&'a dyn ContextElement>,
) -> Result<String, RenderError> {
self.render_template(name, breadcrumbs, None)
}
fn render_template(
&'a self,
name: &str,
breadcrumbs: &Vec<&'a dyn ContextElement>,
blocks: Option<&'a InlinePartialTreeElement<'a>>,
) -> Result<String, RenderError> {
let main_template = match self.templates.get(name) {
Some(tmpl) => tmpl,
@ -59,13 +70,16 @@ impl<'a> DustRenderer<'a> {
return Err(RenderError::TemplateNotFound(name.to_owned()));
}
};
self.render_body(&main_template.contents, breadcrumbs)
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)
}
fn render_body(
&'a self,
body: &'a Body,
breadcrumbs: &Vec<&'a dyn ContextElement>,
blocks: &'a InlinePartialTreeElement<'a>,
) -> Result<String, RenderError> {
let mut output = String::new();
for elem in &body.elements {
@ -73,7 +87,7 @@ impl<'a> DustRenderer<'a> {
TemplateElement::TEIgnoredWhitespace(_) => {}
TemplateElement::TESpan(span) => output.push_str(span.contents),
TemplateElement::TETag(dt) => {
output.push_str(&self.render_tag(dt, breadcrumbs)?);
output.push_str(&self.render_tag(dt, breadcrumbs, blocks)?);
}
}
}
@ -84,6 +98,7 @@ impl<'a> DustRenderer<'a> {
&'a self,
tag: &'a DustTag,
breadcrumbs: &Vec<&'a dyn ContextElement>,
blocks: &'a InlinePartialTreeElement<'a>,
) -> Result<String, RenderError> {
match tag {
DustTag::DTComment(_comment) => (),
@ -120,7 +135,7 @@ impl<'a> DustRenderer<'a> {
// original context before walking the path as
// the context for rendering the else block
return match &container.else_contents {
Some(body) => self.render_body(&body, breadcrumbs),
Some(body) => self.render_body(&body, breadcrumbs, blocks),
None => Ok("".to_owned()),
};
} else {
@ -132,7 +147,7 @@ impl<'a> DustRenderer<'a> {
.map(|array_elem| {
let mut new_breadcumbs = breadcrumbs.clone();
new_breadcumbs.push(array_elem);
self.render_body(&body, &new_breadcumbs)
self.render_body(&body, &new_breadcumbs, blocks)
})
.collect();
let rendered_slice: &[String] = &rendered_results?;
@ -146,13 +161,13 @@ impl<'a> DustRenderer<'a> {
let loop_elements: Vec<&dyn ContextElement> = self.get_loop_elements(val);
if loop_elements.is_empty() {
return match &container.else_contents {
Some(body) => self.render_body(&body, breadcrumbs),
Some(body) => self.render_body(&body, breadcrumbs, blocks),
None => Ok("".to_owned()),
};
} else {
return match &container.contents {
None => Ok("".to_owned()),
Some(body) => self.render_body(&body, breadcrumbs),
Some(body) => self.render_body(&body, breadcrumbs, blocks),
};
}
}
@ -161,28 +176,52 @@ impl<'a> DustRenderer<'a> {
let loop_elements: Vec<&dyn ContextElement> = self.get_loop_elements(val);
if !loop_elements.is_empty() {
return match &container.else_contents {
Some(body) => self.render_body(&body, breadcrumbs),
Some(body) => self.render_body(&body, breadcrumbs, blocks),
None => Ok("".to_owned()),
};
} else {
return match &container.contents {
None => Ok("".to_owned()),
Some(body) => self.render_body(&body, breadcrumbs),
Some(body) => self.render_body(&body, breadcrumbs, blocks),
};
}
}
DustTag::DTPartial(partial) => {
if partial.params.is_empty() {
let rendered_content = self.render(&partial.name, breadcrumbs)?;
let rendered_content =
self.render_template(&partial.name, breadcrumbs, Some(blocks))?;
return Ok(rendered_content);
} else {
let injected_context = ParametersContext::new(breadcrumbs, &partial.params);
let mut new_breadcrumbs = breadcrumbs.clone();
new_breadcrumbs.insert(new_breadcrumbs.len() - 1, &injected_context);
let rendered_content = self.render(&partial.name, &new_breadcrumbs)?;
let rendered_content =
self.render_template(&partial.name, &new_breadcrumbs, Some(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) => {
match blocks.get_block(named_block.name) {
None => match &named_block.contents {
None => return Ok("".to_owned()),
Some(body) => {
let rendered_content = self.render_body(body, breadcrumbs, blocks)?;
return Ok(rendered_content);
}
},
Some(interior) => match interior {
None => return Ok("".to_owned()),
Some(body) => {
let rendered_content = self.render_body(body, breadcrumbs, blocks)?;
return Ok(rendered_content);
}
},
};
}
_ => (), // TODO: Implement the rest
}
Ok("".to_owned())

Loading…
Cancel
Save