Implement the new render functions.

This commit is contained in:
Tom Alexander 2020-05-05 19:51:07 -04:00
parent a3bb8e47c1
commit 26fe996b0d
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
2 changed files with 104 additions and 4 deletions

View File

@ -44,10 +44,11 @@ fn main() {
.first() .first()
.expect("There should be more than 1 template") .expect("There should be more than 1 template")
.name; .name;
let breadcrumbs = vec![&context as &dyn ContextElement];
println!( println!(
"{}", "{}",
dust_renderer dust_renderer
.render(main_template_name, &context) .new_render(main_template_name, &breadcrumbs)
.expect("Failed to render") .expect("Failed to render")
); );
} }

View File

@ -63,6 +63,23 @@ impl<'a> DustRenderer<'a> {
.insert(template.name.clone(), &template.template); .insert(template.name.clone(), &template.template);
} }
pub fn new_render(
&'a self,
name: &str,
breadcrumbs: &Vec<&'a dyn ContextElement>,
) -> Result<String, RenderError<'a>> {
let main_template = match self.templates.get(name) {
Some(tmpl) => tmpl,
None => {
return Err(RenderError::Generic(format!(
"No template named {} in context",
name
)));
}
};
self.new_render_body(&main_template.contents, breadcrumbs)
}
pub fn render<C>(&'a self, name: &str, context: &'a C) -> Result<String, RenderError<'a>> pub fn render<C>(&'a self, name: &str, context: &'a C) -> Result<String, RenderError<'a>>
where where
C: ContextElement, C: ContextElement,
@ -79,6 +96,24 @@ impl<'a> DustRenderer<'a> {
self.render_body(&main_template.contents, context) self.render_body(&main_template.contents, context)
} }
fn new_render_body(
&'a self,
body: &'a Body,
breadcrumbs: &Vec<&'a dyn ContextElement>,
) -> Result<String, RenderError<'a>> {
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.new_render_tag(dt, breadcrumbs)?);
}
}
}
Ok(output)
}
fn render_body<C>(&'a self, body: &Body, context: &'a C) -> Result<String, RenderError<'a>> fn render_body<C>(&'a self, body: &Body, context: &'a C) -> Result<String, RenderError<'a>>
where where
C: ContextElement, C: ContextElement,
@ -96,6 +131,70 @@ impl<'a> DustRenderer<'a> {
Ok(output) Ok(output)
} }
fn new_render_tag(
&'a self,
tag: &'a DustTag,
breadcrumbs: &Vec<&'a dyn ContextElement>,
) -> Result<String, RenderError<'a>> {
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::DTReference(reference) => {
let val = new_walk_path(breadcrumbs, &reference.path.keys);
if let Err(RenderError::NotFound { .. }) = val {
// If reference does not exist in the context, it becomes an empty string
return Ok("".to_owned());
} else {
return val?.render(&reference.filters);
}
}
DustTag::DTSection(container) => {
let val = new_walk_path(breadcrumbs, &container.path.keys);
let loop_elements: Vec<&dyn ContextElement> = self.get_loop_elements(val)?;
if loop_elements.is_empty() {
// 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
//
// TODO: do filters apply? I don't think so
// but I should test
return match &container.else_contents {
Some(body) => self.new_render_body(&body, breadcrumbs),
None => Ok("".to_owned()),
};
} else {
match &container.contents {
None => return Ok("".to_owned()),
Some(body) => {
let rendered_results: Result<Vec<String>, RenderError> = loop_elements
.into_iter()
.map(|array_elem| {
let mut new_breadcumbs = breadcrumbs.clone();
new_breadcumbs.push(array_elem);
self.new_render_body(&body, &new_breadcumbs)
})
.collect();
let rendered_slice: &[String] = &rendered_results?;
return Ok(rendered_slice.join(""));
}
}
}
}
_ => (), // TODO: Implement the rest
}
Ok("".to_owned())
}
fn render_tag<C>(&'a self, tag: &DustTag, context: &'a C) -> Result<String, RenderError<'a>> fn render_tag<C>(&'a self, tag: &DustTag, context: &'a C) -> Result<String, RenderError<'a>>
where where
C: ContextElement, C: ContextElement,
@ -209,7 +308,7 @@ fn walk_path_from_single_level<'a>(
} }
fn new_walk_path<'a>( fn new_walk_path<'a>(
breadcrumbs: Vec<&'a dyn ContextElement>, breadcrumbs: &Vec<&'a dyn ContextElement>,
path: &'a Vec<&str>, path: &'a Vec<&str>,
) -> Result<&'a dyn ContextElement, RenderError<'a>> { ) -> Result<&'a dyn ContextElement, RenderError<'a>> {
for context in breadcrumbs.iter().rev() { for context in breadcrumbs.iter().rev() {
@ -221,7 +320,7 @@ fn new_walk_path<'a>(
WalkResult::PartialWalk => { WalkResult::PartialWalk => {
return Err(RenderError::NotFound { return Err(RenderError::NotFound {
path: path, path: path,
breadcrumbs: breadcrumbs, breadcrumbs: breadcrumbs.clone(),
}) })
} }
WalkResult::FullyWalked(new_context) => return Ok(new_context), WalkResult::FullyWalked(new_context) => return Ok(new_context),
@ -229,7 +328,7 @@ fn new_walk_path<'a>(
} }
Err(RenderError::NotFound { Err(RenderError::NotFound {
path: path, path: path,
breadcrumbs: breadcrumbs, breadcrumbs: breadcrumbs.clone(),
}) })
} }