From 26fe996b0dbdbfc39a9095f7424591d3578b638c Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Tue, 5 May 2020 19:51:07 -0400 Subject: [PATCH] Implement the new render functions. --- src/bin.rs | 3 +- src/renderer/renderer.rs | 105 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 104 insertions(+), 4 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index 7535818..6d709fc 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -44,10 +44,11 @@ fn main() { .first() .expect("There should be more than 1 template") .name; + let breadcrumbs = vec![&context as &dyn ContextElement]; println!( "{}", dust_renderer - .render(main_template_name, &context) + .new_render(main_template_name, &breadcrumbs) .expect("Failed to render") ); } diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index f430d28..bb820d0 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -63,6 +63,23 @@ impl<'a> DustRenderer<'a> { .insert(template.name.clone(), &template.template); } + pub fn new_render( + &'a self, + name: &str, + breadcrumbs: &Vec<&'a dyn ContextElement>, + ) -> Result> { + 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(&'a self, name: &str, context: &'a C) -> Result> where C: ContextElement, @@ -79,6 +96,24 @@ impl<'a> DustRenderer<'a> { self.render_body(&main_template.contents, context) } + fn new_render_body( + &'a self, + body: &'a Body, + breadcrumbs: &Vec<&'a dyn ContextElement>, + ) -> Result> { + 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(&'a self, body: &Body, context: &'a C) -> Result> where C: ContextElement, @@ -96,6 +131,70 @@ impl<'a> DustRenderer<'a> { Ok(output) } + fn new_render_tag( + &'a self, + tag: &'a DustTag, + breadcrumbs: &Vec<&'a dyn ContextElement>, + ) -> Result> { + 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, 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(&'a self, tag: &DustTag, context: &'a C) -> Result> where C: ContextElement, @@ -209,7 +308,7 @@ fn walk_path_from_single_level<'a>( } fn new_walk_path<'a>( - breadcrumbs: Vec<&'a dyn ContextElement>, + breadcrumbs: &Vec<&'a dyn ContextElement>, path: &'a Vec<&str>, ) -> Result<&'a dyn ContextElement, RenderError<'a>> { for context in breadcrumbs.iter().rev() { @@ -221,7 +320,7 @@ fn new_walk_path<'a>( WalkResult::PartialWalk => { return Err(RenderError::NotFound { path: path, - breadcrumbs: breadcrumbs, + breadcrumbs: breadcrumbs.clone(), }) } WalkResult::FullyWalked(new_context) => return Ok(new_context), @@ -229,7 +328,7 @@ fn new_walk_path<'a>( } Err(RenderError::NotFound { path: path, - breadcrumbs: breadcrumbs, + breadcrumbs: breadcrumbs.clone(), }) }