Add a Loopable trait for dust sections.

This commit is contained in:
Tom Alexander 2020-04-28 19:34:52 -04:00
parent c961cf7ab8
commit e5c4ba8c82
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
4 changed files with 72 additions and 1 deletions

View File

@ -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<Vec<&dyn ContextElement>, 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]),
}
}
}

View File

@ -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<Filter>) -> Result<String, RenderError>;
}
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<Vec<&dyn ContextElement>, RenderError>;
}

View File

@ -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;

View File

@ -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<Vec<&dyn ContextElement>, RenderError> {
if self.is_empty() {
Ok(Vec::new())
} else {
Ok(vec![self])
}
}
}
impl Loopable for u32 {
fn get_loop_elements(&self) -> Result<Vec<&dyn ContextElement>, RenderError> {
Ok(vec![self])
}
}
impl<I: ContextElement> Loopable for HashMap<&str, I> {
fn get_loop_elements(&self) -> Result<Vec<&dyn ContextElement>, RenderError> {
Ok(vec![self])
}
}
let context: HashMap<&str, &str> =
[("cat", "kitty"), ("dog", "doggy"), ("tiger", "murderkitty")]
.iter()