Merge branch 'render_result' into render
This commit is contained in:
commit
cc52c3ad05
52
src/bin.rs
52
src/bin.rs
@ -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;
|
||||||
@ -72,33 +73,46 @@ fn read_context_from_stdin() -> serde_json::Value {
|
|||||||
impl ContextElement for serde_json::Value {}
|
impl ContextElement for serde_json::Value {}
|
||||||
|
|
||||||
impl Renderable for serde_json::Value {
|
impl Renderable for serde_json::Value {
|
||||||
fn render(&self) -> String {
|
fn render(&self) -> Result<String, RenderError> {
|
||||||
match self {
|
match self {
|
||||||
serde_json::Value::Null => "".to_owned(),
|
serde_json::Value::Null => Ok("".to_owned()),
|
||||||
serde_json::Value::Bool(boolean) => boolean.to_string(),
|
serde_json::Value::Bool(boolean) => Ok(boolean.to_string()),
|
||||||
serde_json::Value::Number(num) => num.to_string(),
|
serde_json::Value::Number(num) => Ok(num.to_string()),
|
||||||
serde_json::Value::String(string) => string.to_string(),
|
serde_json::Value::String(string) => Ok(string.to_string()),
|
||||||
serde_json::Value::Array(_arr) => panic!("Attempted to render an array"),
|
serde_json::Value::Array(_arr) => Err(RenderError::CantRender { elem: self }),
|
||||||
serde_json::Value::Object(_obj) => panic!("Attempted to render an object"),
|
serde_json::Value::Object(_obj) => Err(RenderError::CantRender { elem: self }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 => Err(RenderError::CantWalk {
|
||||||
serde_json::Value::Bool(_boolean) => {
|
segment: segment.to_string(),
|
||||||
panic!("Attempted to walk to {} on a boolean value", segment)
|
elem: self,
|
||||||
}
|
}),
|
||||||
serde_json::Value::Number(_num) => {
|
serde_json::Value::Bool(_boolean) => Err(RenderError::CantWalk {
|
||||||
panic!("Attempted to walk to {} on a number value", segment)
|
segment: segment.to_string(),
|
||||||
}
|
elem: self,
|
||||||
serde_json::Value::String(_string) => {
|
}),
|
||||||
panic!("Attempted to walk to {} on a string value", segment)
|
serde_json::Value::Number(_num) => Err(RenderError::CantWalk {
|
||||||
}
|
segment: segment.to_string(),
|
||||||
|
elem: self,
|
||||||
|
}),
|
||||||
|
serde_json::Value::String(_string) => Err(RenderError::CantWalk {
|
||||||
|
segment: segment.to_string(),
|
||||||
|
elem: self,
|
||||||
|
}),
|
||||||
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(),
|
serde_json::Value::Object(obj) => {
|
||||||
|
obj.get(segment)
|
||||||
|
.map(|val| val as _)
|
||||||
|
.ok_or(RenderError::WontWalk {
|
||||||
|
segment: segment.to_string(),
|
||||||
|
elem: self,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,23 @@
|
|||||||
|
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,
|
||||||
|
},
|
||||||
|
/// Attempting to render and unrenderable type (for example, an object without any filters)
|
||||||
|
CantRender {
|
||||||
|
elem: &'a dyn ContextElement,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -11,19 +25,37 @@ 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)
|
||||||
|
}
|
||||||
|
RenderError::CantRender { elem } => write!(f, "Cant render {:?}", 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)
|
||||||
|
}
|
||||||
|
RenderError::CantRender { elem } => write!(f, "Cant render {:?}", 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
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use crate::renderer::errors::RenderError;
|
||||||
|
|
||||||
pub trait Renderable {
|
pub trait Renderable {
|
||||||
fn render(&self) -> String;
|
fn render(&self) -> Result<String, RenderError>;
|
||||||
}
|
}
|
||||||
|
@ -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,15 +80,15 @@ 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 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)]
|
||||||
@ -115,38 +120,48 @@ mod tests {
|
|||||||
impl<I: ContextElement> ContextElement for HashMap<&str, I> {}
|
impl<I: ContextElement> ContextElement for HashMap<&str, I> {}
|
||||||
|
|
||||||
impl Renderable for u32 {
|
impl Renderable for u32 {
|
||||||
fn render(&self) -> std::string::String {
|
fn render(&self) -> Result<String, RenderError> {
|
||||||
self.to_string()
|
Ok(self.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderable for &str {
|
impl Renderable for &str {
|
||||||
fn render(&self) -> std::string::String {
|
fn render(&self) -> Result<String, RenderError> {
|
||||||
self.to_string()
|
Ok(self.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: ContextElement> Renderable for HashMap<&str, I> {
|
impl<I: ContextElement> Renderable for HashMap<&str, I> {
|
||||||
fn render(&self) -> std::string::String {
|
fn render(&self) -> Result<String, RenderError> {
|
||||||
panic!("Attempted to render a hashmap");
|
Err(RenderError::CantRender { elem: self })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
let child = self.get(segment).ok_or(RenderError::WontWalk {
|
||||||
|
segment: segment.to_string(),
|
||||||
|
elem: self,
|
||||||
|
})?;
|
||||||
|
Ok(child)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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");
|
Err(RenderError::CantWalk {
|
||||||
|
segment: segment.to_string(),
|
||||||
|
elem: self,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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");
|
Err(RenderError::CantWalk {
|
||||||
|
segment: segment.to_string(),
|
||||||
|
elem: self,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,11 +182,23 @@ mod tests {
|
|||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect();
|
||||||
assert_eq!(walk_path(&context, &vec!["cat"]).render(), "kitty");
|
|
||||||
assert_eq!(walk_path(&number_context, &vec!["tiger"]).render(), "3");
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
walk_path(&deep_context, &vec!["tiger", "food"]).render(),
|
walk_path(&context, &vec!["cat"]).unwrap().render().unwrap(),
|
||||||
"people"
|
"kitty".to_owned()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
walk_path(&number_context, &vec!["tiger"])
|
||||||
|
.unwrap()
|
||||||
|
.render()
|
||||||
|
.unwrap(),
|
||||||
|
"3".to_owned()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
walk_path(&deep_context, &vec!["tiger", "food"])
|
||||||
|
.unwrap()
|
||||||
|
.render()
|
||||||
|
.unwrap(),
|
||||||
|
"people".to_owned()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user