Initial move to returning results from render calls.

This commit is contained in:
Tom Alexander 2020-04-12 18:29:40 -04:00
parent d30749f709
commit d51392fe8a
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
5 changed files with 66 additions and 25 deletions

View File

@ -4,6 +4,7 @@ use renderer::compile_template;
use renderer::CompiledTemplate; use renderer::CompiledTemplate;
use renderer::ContextElement; use renderer::ContextElement;
use renderer::DustRenderer; use renderer::DustRenderer;
use renderer::RenderError;
use renderer::Renderable; use renderer::Renderable;
use renderer::Walkable; use renderer::Walkable;
use std::env; use std::env;
@ -85,7 +86,7 @@ impl Renderable for serde_json::Value {
} }
impl Walkable for serde_json::Value { impl Walkable for serde_json::Value {
fn walk(&self, segment: &str) -> &dyn ContextElement { fn walk(&self, segment: &str) -> Result<&dyn ContextElement, RenderError> {
match self { match self {
serde_json::Value::Null => panic!("Attempted to walk to {} on a null value", segment), serde_json::Value::Null => panic!("Attempted to walk to {} on a null value", segment),
serde_json::Value::Bool(_boolean) => { serde_json::Value::Bool(_boolean) => {
@ -98,7 +99,8 @@ impl Walkable for serde_json::Value {
panic!("Attempted to walk to {} on a string value", segment) panic!("Attempted to walk to {} on a string value", segment)
} }
serde_json::Value::Array(_arr) => todo!("Arrays not supported yet"), serde_json::Value::Array(_arr) => todo!("Arrays not supported yet"),
serde_json::Value::Object(obj) => obj.get(segment).unwrap(), // TODO: Handle this error better
serde_json::Value::Object(obj) => Ok(obj.get(segment).unwrap()),
} }
} }
} }

View File

@ -1,9 +1,19 @@
use crate::renderer::walkable::ContextElement;
use std::error; use std::error;
use std::fmt; use std::fmt;
#[derive(Clone)] pub enum RenderError<'a> {
pub struct RenderError { Generic(String),
pub message: String, /// For when walking is absolutely impossible
CantWalk {
segment: String,
elem: &'a dyn ContextElement,
},
/// For when walking fails (example, a missing key on a map)
WontWalk {
segment: String,
elem: &'a dyn ContextElement,
},
} }
#[derive(Clone)] #[derive(Clone)]
@ -11,19 +21,35 @@ pub struct CompileError {
pub message: String, pub message: String,
} }
impl fmt::Display for RenderError { impl fmt::Display for RenderError<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Error rendering: {}", self.message) match self {
RenderError::Generic(msg) => write!(f, "{}", msg),
RenderError::CantWalk { segment, elem } => {
write!(f, "Tried to walk to {} from {:?}", segment, elem)
}
RenderError::WontWalk { segment, elem } => {
write!(f, "Failed to walk to {} from {:?}", segment, elem)
}
}
} }
} }
impl fmt::Debug for RenderError { impl fmt::Debug for RenderError<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Error rendering: {}", self.message) match self {
RenderError::Generic(msg) => write!(f, "{}", msg),
RenderError::CantWalk { segment, elem } => {
write!(f, "Tried to walk to {} from {:?}", segment, elem)
}
RenderError::WontWalk { segment, elem } => {
write!(f, "Failed to walk to {} from {:?}", segment, elem)
}
}
} }
} }
impl error::Error for RenderError { impl error::Error for RenderError<'_> {
fn source(&self) -> Option<&(dyn error::Error + 'static)> { fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None None
} }

View File

@ -5,6 +5,8 @@ mod renderable;
mod renderer; mod renderer;
mod walkable; mod walkable;
pub use errors::CompileError;
pub use errors::RenderError;
pub use renderable::Renderable; pub use renderable::Renderable;
pub use renderer::compile_template; pub use renderer::compile_template;
pub use renderer::CompiledTemplate; pub use renderer::CompiledTemplate;

View File

@ -9,6 +9,7 @@ use crate::renderer::renderable::Renderable;
use crate::renderer::walkable::ContextElement; use crate::renderer::walkable::ContextElement;
use crate::renderer::walkable::Walkable; use crate::renderer::walkable::Walkable;
use std::collections::HashMap; use std::collections::HashMap;
use std::error::Error;
use std::ops::Index; use std::ops::Index;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -47,22 +48,23 @@ impl<'a> DustRenderer<'a> {
.insert(template.name.clone(), &template.template); .insert(template.name.clone(), &template.template);
} }
pub fn render<C>(&self, name: &str, context: &C) -> Result<String, RenderError> pub fn render<C>(&self, name: &str, context: &'a C) -> Result<String, RenderError<'a>>
where where
C: ContextElement, C: ContextElement,
{ {
let main_template = match self.templates.get(name) { let main_template = match self.templates.get(name) {
Some(tmpl) => tmpl, Some(tmpl) => tmpl,
None => { None => {
return Err(RenderError { return Err(RenderError::Generic(format!(
message: format!("No template named {} in context", name), "No template named {} in context",
}); name
)));
} }
}; };
self.render_body(&main_template.contents, context) self.render_body(&main_template.contents, context)
} }
fn render_body<C>(&self, body: &Body, context: &C) -> Result<String, RenderError> fn render_body<C>(&self, body: &Body, context: &'a C) -> Result<String, RenderError<'a>>
where where
C: ContextElement, C: ContextElement,
{ {
@ -78,14 +80,14 @@ impl<'a> DustRenderer<'a> {
Ok(output) Ok(output)
} }
fn render_tag<C>(&self, tag: &DustTag, context: &C) -> Result<String, RenderError> fn render_tag<C>(&self, tag: &DustTag, context: &'a C) -> Result<String, RenderError<'a>>
where where
C: ContextElement, C: ContextElement,
{ {
match tag { match tag {
DustTag::DTComment(comment) => (), DustTag::DTComment(comment) => (),
DustTag::DTReference(reference) => { DustTag::DTReference(reference) => {
let val = walk_path(context, &reference.path.keys); let val = walk_path(context, &reference.path.keys)?;
return Ok(val.render()); return Ok(val.render());
} }
_ => (), // TODO: Implement the rest _ => (), // TODO: Implement the rest
@ -94,14 +96,17 @@ impl<'a> DustRenderer<'a> {
} }
} }
fn walk_path<'a>(context: &'a dyn ContextElement, path: &Vec<&str>) -> &'a dyn ContextElement { fn walk_path<'a>(
context: &'a dyn ContextElement,
path: &Vec<&str>,
) -> Result<&'a dyn ContextElement, RenderError<'a>> {
let mut output = context; let mut output = context;
for elem in path.iter() { for elem in path.iter() {
output = output.walk(elem); output = output.walk(elem)?;
} }
output Ok(output)
} }
#[cfg(test)] #[cfg(test)]
@ -133,19 +138,24 @@ mod tests {
} }
impl<I: ContextElement> Walkable for HashMap<&str, I> { impl<I: ContextElement> Walkable for HashMap<&str, I> {
fn walk(&self, segment: &str) -> &dyn ContextElement { fn walk(&self, segment: &str) -> Result<&dyn ContextElement, RenderError> {
self.get(segment).unwrap() Ok(self.get(segment).unwrap())
// TODO: Handle error here better
// self.get(segment).ok_or(RenderError::WontWalk {
// segment: segment.to_string(),
// elem: self,
// })
} }
} }
impl Walkable for &str { impl Walkable for &str {
fn walk(&self, _segment: &str) -> &dyn ContextElement { fn walk(&self, _segment: &str) -> Result<&dyn ContextElement, RenderError> {
panic!("Tried to walk down a str"); panic!("Tried to walk down a str");
} }
} }
impl Walkable for u32 { impl Walkable for u32 {
fn walk(&self, _segment: &str) -> &dyn ContextElement { fn walk(&self, _segment: &str) -> Result<&dyn ContextElement, RenderError> {
panic!("Tried to walk down a str"); panic!("Tried to walk down a str");
} }
} }

View File

@ -1,8 +1,9 @@
use super::renderable::Renderable; use super::renderable::Renderable;
use crate::renderer::errors::RenderError;
use std::fmt::Debug; use std::fmt::Debug;
pub trait ContextElement: Walkable + Renderable + Debug {} pub trait ContextElement: Walkable + Renderable + Debug {}
pub trait Walkable { pub trait Walkable {
fn walk(&self, segment: &str) -> &dyn ContextElement; fn walk(&self, segment: &str) -> Result<&dyn ContextElement, RenderError>;
} }