Merge branch 'blocks' into render
This commit is contained in:
commit
f9212d1fe4
@ -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
|
||||
|
1
js/test_cases/block_before/base.dust
Normal file
1
js/test_cases/block_before/base.dust
Normal file
@ -0,0 +1 @@
|
||||
The block content is from {+inject/}
|
4
js/test_cases/block_before/input1.json
Normal file
4
js/test_cases/block_before/input1.json
Normal file
@ -0,0 +1,4 @@
|
||||
{"people": [
|
||||
{"name": "Alice", "item": "cat"},
|
||||
{"name": "Bob"}
|
||||
]}
|
2
js/test_cases/block_before/main.dust
Normal file
2
js/test_cases/block_before/main.dust
Normal file
@ -0,0 +1,2 @@
|
||||
{<inject}Before Partial{/inject}
|
||||
{>"base"/}
|
1
js/test_cases/block_context/base.dust
Normal file
1
js/test_cases/block_context/base.dust
Normal file
@ -0,0 +1 @@
|
||||
The block content is from {+inject/}
|
14
js/test_cases/block_context/input1.json
Normal file
14
js/test_cases/block_context/input1.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"people": [
|
||||
{
|
||||
"name": "Alice",
|
||||
"item": "cat"
|
||||
},
|
||||
{
|
||||
"name": "Bob"
|
||||
}
|
||||
],
|
||||
"pet": {
|
||||
"name": "fluffy"
|
||||
}
|
||||
}
|
6
js/test_cases/block_context/main.dust
Normal file
6
js/test_cases/block_context/main.dust
Normal file
@ -0,0 +1,6 @@
|
||||
{#pet}
|
||||
{>"base"/}
|
||||
{/pet}
|
||||
{#people}
|
||||
{<inject}by {name}{/inject}
|
||||
{/people}
|
1
js/test_cases/block_inverse_register_order/alpha.dust
Normal file
1
js/test_cases/block_inverse_register_order/alpha.dust
Normal file
@ -0,0 +1 @@
|
||||
The block content is from {+inject/}{~n}
|
2
js/test_cases/block_inverse_register_order/beta.dust
Normal file
2
js/test_cases/block_inverse_register_order/beta.dust
Normal file
@ -0,0 +1,2 @@
|
||||
{<inject}beta{/inject}
|
||||
{>"alpha"/}
|
4
js/test_cases/block_inverse_register_order/input1.json
Normal file
4
js/test_cases/block_inverse_register_order/input1.json
Normal file
@ -0,0 +1,4 @@
|
||||
{"people": [
|
||||
{"name": "Alice", "item": "cat"},
|
||||
{"name": "Bob"}
|
||||
]}
|
3
js/test_cases/block_inverse_register_order/main.dust
Normal file
3
js/test_cases/block_inverse_register_order/main.dust
Normal file
@ -0,0 +1,3 @@
|
||||
{>"beta"/}
|
||||
{>"alpha"/}
|
||||
{<inject}main{/inject}
|
1
js/test_cases/block_register_order/alpha.dust
Normal file
1
js/test_cases/block_register_order/alpha.dust
Normal file
@ -0,0 +1 @@
|
||||
{<inject}alpha{/inject}
|
2
js/test_cases/block_register_order/beta.dust
Normal file
2
js/test_cases/block_register_order/beta.dust
Normal file
@ -0,0 +1,2 @@
|
||||
{<inject}beta{/inject}
|
||||
{>"alpha"/}
|
4
js/test_cases/block_register_order/input1.json
Normal file
4
js/test_cases/block_register_order/input1.json
Normal file
@ -0,0 +1,4 @@
|
||||
{"people": [
|
||||
{"name": "Alice", "item": "cat"},
|
||||
{"name": "Bob"}
|
||||
]}
|
3
js/test_cases/block_register_order/main.dust
Normal file
3
js/test_cases/block_register_order/main.dust
Normal file
@ -0,0 +1,3 @@
|
||||
The block content is from {+inject/}{~n}
|
||||
{>"beta"/}
|
||||
The block content is from {+inject/}{~n}
|
9
js/test_cases/block_simple/README.md
Normal file
9
js/test_cases/block_simple/README.md
Normal file
@ -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.
|
1
js/test_cases/block_simple/base.dust
Normal file
1
js/test_cases/block_simple/base.dust
Normal file
@ -0,0 +1 @@
|
||||
The block content is from {+inject/}
|
4
js/test_cases/block_simple/input1.json
Normal file
4
js/test_cases/block_simple/input1.json
Normal file
@ -0,0 +1,4 @@
|
||||
{"people": [
|
||||
{"name": "Alice", "item": "cat"},
|
||||
{"name": "Bob"}
|
||||
]}
|
3
js/test_cases/block_simple/main.dust
Normal file
3
js/test_cases/block_simple/main.dust
Normal file
@ -0,0 +1,3 @@
|
||||
{<inject}Before Partial{/inject}
|
||||
{>"base"/}
|
||||
{<inject}After Partial{/inject}
|
1
js/test_cases/block_unrendered/base.dust
Normal file
1
js/test_cases/block_unrendered/base.dust
Normal file
@ -0,0 +1 @@
|
||||
The block content is from {+inject/}
|
14
js/test_cases/block_unrendered/input1.json
Normal file
14
js/test_cases/block_unrendered/input1.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"people": [
|
||||
{
|
||||
"name": "Alice",
|
||||
"item": "cat"
|
||||
},
|
||||
{
|
||||
"name": "Bob"
|
||||
}
|
||||
],
|
||||
"pet": {
|
||||
"name": "fluffy"
|
||||
}
|
||||
}
|
10
js/test_cases/block_unrendered/main.dust
Normal file
10
js/test_cases/block_unrendered/main.dust
Normal file
@ -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)]
|
||||
|
104
src/renderer/inline_partial_tree.rs
Normal file
104
src/renderer/inline_partial_tree.rs
Normal file
@ -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…
Reference in New Issue
Block a user