diff --git a/src/bin.rs b/src/bin.rs index 3e1e7e9..bcdf0eb 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -5,6 +5,7 @@ use renderer::compile_template; use renderer::CompiledTemplate; use renderer::ContextElement; use renderer::DustRenderer; +use renderer::Loopable; use renderer::RenderError; use renderer::Renderable; use renderer::Walkable; @@ -123,3 +124,34 @@ impl Walkable for serde_json::Value { } } } + +impl Loopable for serde_json::Value { + fn get_loop_elements(&self) -> Result, RenderError> { + match self { + serde_json::Value::Null => Ok(Vec::new()), + serde_json::Value::Bool(boolean) => { + if *boolean { + Ok(vec![self]) + } else { + Ok(Vec::new()) + } + } + serde_json::Value::Number(_num) => Ok(vec![self]), + serde_json::Value::String(string_value) => { + if string_value.is_empty() { + Ok(Vec::new()) + } else { + Ok(vec![self]) + } + } + serde_json::Value::Array(array_value) => { + if array_value.is_empty() { + Ok(Vec::new()) + } else { + Ok(array_value.iter().map(|x| x as _).collect()) + } + } + serde_json::Value::Object(_obj) => Ok(vec![self]), + } + } +} diff --git a/src/renderer/context_element.rs b/src/renderer/context_element.rs index 84ad197..6b8f7e2 100644 --- a/src/renderer/context_element.rs +++ b/src/renderer/context_element.rs @@ -2,7 +2,7 @@ use crate::parser::Filter; use crate::renderer::errors::RenderError; use std::fmt::Debug; -pub trait ContextElement: Walkable + Renderable + Debug {} +pub trait ContextElement: Debug + Walkable + Renderable + Loopable {} pub trait Walkable { fn walk(&self, segment: &str) -> Result<&dyn ContextElement, RenderError>; @@ -11,3 +11,18 @@ pub trait Walkable { pub trait Renderable { fn render(&self, filters: &Vec) -> Result; } + +pub trait Loopable { + /// Return the elements for a Dust section + /// + /// Sections in dust are accomplished with the {#path} syntax. A + /// section has a truthiness check performed on it. If that + /// truthiness check fails, then it will render the + /// else-block. Otherwise if its a scalar value it will render + /// once with the context being the element at that path. Finally, + /// if its an array-like value then it will render n-times, once + /// for each element of the array. + /// + /// TODO: Should this return an iterator instead of a vec? + fn get_loop_elements(&self) -> Result, RenderError>; +} diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 991bc92..ce43f4d 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -5,6 +5,7 @@ mod errors; mod renderer; pub use context_element::ContextElement; +pub use context_element::Loopable; pub use context_element::Renderable; pub use context_element::Walkable; pub use errors::CompileError; diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 7cf8ed0..4af3d3c 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -114,6 +114,7 @@ fn walk_path<'a>( mod tests { use super::*; use crate::parser::Filter; + use crate::renderer::context_element::Loopable; use crate::renderer::context_element::Renderable; use crate::renderer::context_element::Walkable; @@ -172,6 +173,28 @@ mod tests { } } + impl Loopable for &str { + fn get_loop_elements(&self) -> Result, RenderError> { + if self.is_empty() { + Ok(Vec::new()) + } else { + Ok(vec![self]) + } + } + } + + impl Loopable for u32 { + fn get_loop_elements(&self) -> Result, RenderError> { + Ok(vec![self]) + } + } + + impl Loopable for HashMap<&str, I> { + fn get_loop_elements(&self) -> Result, RenderError> { + Ok(vec![self]) + } + } + let context: HashMap<&str, &str> = [("cat", "kitty"), ("dog", "doggy"), ("tiger", "murderkitty")] .iter()