Skip to content

Commit

Permalink
refactor: move more errors types into RenderErrorReason
Browse files Browse the repository at this point in the history
  • Loading branch information
sunng87 committed Dec 31, 2023
1 parent 58a32b6 commit 0d30e2d
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 66 deletions.
20 changes: 8 additions & 12 deletions examples/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,8 @@ pub fn error_helper(
.param(0)
.ok_or(RenderErrorReason::ParamNotFoundForIndex("error", 0))?;
match param.value().as_str() {
Some("db") => Err(RenderError::from_error(
"helper error",
HelperError::DbError,
)),
Some("api") => Err(RenderError::from_error(
"helper error",
HelperError::ApiError,
)),
Some("db") => Err(RenderErrorReason::NestedError(Box::new(HelperError::DbError)).into()),
Some("api") => Err(RenderErrorReason::NestedError(Box::new(HelperError::ApiError)).into()),
_ => Ok(()),
}
}
Expand Down Expand Up @@ -76,10 +70,12 @@ fn main() -> Result<(), Box<dyn StdError>> {
let e2 = handlebars
.render_template("{{err \"db\"}}", &json!({}))
.unwrap_err();
// down-casting the error to user defined type
match e2.source().and_then(|e| e.downcast_ref::<HelperError>()) {
Some(HelperError::DbError) => {
println!("Detected error from helper: db error",)
// get nested error to from `RenderError`
match e2.reason() {
Some(RenderErrorReason::NestedError(e)) => {
if let Some(HelperError::DbError) = e.downcast_ref::<HelperError>() {
println!("Detected error from helper: db error",)
}
}
_ => {}
}
Expand Down
9 changes: 6 additions & 3 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use serde::Serialize;
use serde_json::value::{to_value, Map, Value as Json};

use crate::block::{BlockContext, BlockParamHolder};
use crate::error::RenderError;
use crate::error::{RenderError, RenderErrorReason};
use crate::grammar::Rule;
use crate::json::path::*;
use crate::json::value::ScopedJson;
Expand Down Expand Up @@ -116,7 +116,10 @@ fn parse_json_visitor<'a>(

fn get_data<'a>(d: Option<&'a Json>, p: &str) -> Result<Option<&'a Json>, RenderError> {
let result = match d {
Some(Json::Array(l)) => p.parse::<usize>().map(|idx_u| l.get(idx_u))?,
Some(Json::Array(l)) => p
.parse::<usize>()
.map(|idx_u| l.get(idx_u))
.map_err(|_| RenderErrorReason::InvalidJsonIndex(p.to_owned()))?,
Some(Json::Object(m)) => m.get(p),
Some(_) => None,
None => None,
Expand Down Expand Up @@ -160,7 +163,7 @@ impl Context {
/// Create a context with given data
pub fn wraps<T: Serialize>(e: T) -> Result<Context, RenderError> {
to_value(e)
.map_err(RenderError::from)
.map_err(|e| RenderErrorReason::SerdeError(e).into())
.map(|d| Context { data: d })
}

Expand Down
72 changes: 33 additions & 39 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::error::Error as StdError;
use std::fmt::{self, Write};
use std::io::Error as IOError;
use std::num::ParseIntError;
use std::string::FromUtf8Error;

use serde_json::error::Error as SerdeError;
Expand All @@ -21,7 +20,7 @@ pub struct RenderError {
pub line_no: Option<usize>,
pub column_no: Option<usize>,
#[source]
cause: Option<Box<dyn StdError + Send + Sync + 'static>>,
cause: Option<RenderErrorReason>,
unimplemented: bool,
// backtrace: Backtrace,
}
Expand All @@ -44,31 +43,13 @@ impl fmt::Display for RenderError {

impl From<IOError> for RenderError {
fn from(e: IOError) -> RenderError {
RenderError::from_error("Cannot generate output.", e)
}
}

impl From<SerdeError> for RenderError {
fn from(e: SerdeError) -> RenderError {
RenderError::from_error("Failed to access JSON data.", e)
RenderErrorReason::IOError(e).into()
}
}

impl From<FromUtf8Error> for RenderError {
fn from(e: FromUtf8Error) -> RenderError {
RenderError::from_error("Failed to generate bytes.", e)
}
}

impl From<ParseIntError> for RenderError {
fn from(e: ParseIntError) -> RenderError {
RenderError::from_error("Cannot access array/vector with string index.", e)
}
}

impl From<TemplateError> for RenderError {
fn from(e: TemplateError) -> RenderError {
RenderError::from_error("Failed to parse template.", e)
fn from(e: FromUtf8Error) -> Self {
RenderErrorReason::Utf8Error(e).into()
}
}

Expand All @@ -77,6 +58,8 @@ impl From<TemplateError> for RenderError {
pub enum RenderErrorReason {
#[error("Template not found {0}")]
TemplateNotFound(String),
#[error("Failed to parse template {0}")]
TemplateError(#[from] TemplateError),
#[error("Failed to access variable in strict mode {0:?}")]
MissingVariable(Option<String>),
#[error("Partial not found {0}")]
Expand All @@ -103,27 +86,33 @@ pub enum RenderErrorReason {
BlockContentRequired,
#[error("Invalid json path {0}")]
InvalidJsonPath(String),
#[error("Cannot access array/vector with string index, {0}")]
InvalidJsonIndex(String),
#[error("Failed to access JSON data: {0}")]
SerdeError(#[from] SerdeError),
#[error("IO Error: {0}")]
IOError(#[from] IOError),
#[error("FromUtf8Error: {0}")]
Utf8Error(#[from] FromUtf8Error),
#[error("Nested error: {0}")]
NestedError(Box<dyn StdError + Send + Sync + 'static>),
#[cfg(feature = "script_helper")]
#[error("Cannot convert data to Rhai dynamic: {0}")]
ScriptValueError(#[from] Box<EvalAltResult>),
#[cfg(feature = "script_helper")]
#[error("Failed to load rhai script: {0}")]
ScriptLoadError(#[from] ScriptError),
#[error("{0}")]
Other(String),
}

impl From<RenderErrorReason> for RenderError {
fn from(e: RenderErrorReason) -> RenderError {
RenderError::from_error(&e.to_string(), e)
}
}

#[cfg(feature = "script_helper")]
impl From<Box<EvalAltResult>> for RenderError {
fn from(e: Box<EvalAltResult>) -> RenderError {
RenderError::from_error("Cannot convert data to Rhai dynamic", e)
}
}

#[cfg(feature = "script_helper")]
impl From<ScriptError> for RenderError {
fn from(e: ScriptError) -> RenderError {
RenderError::from_error("Failed to load rhai script", e)
RenderError {
desc: e.to_string(),
cause: Some(e),
..Default::default()
}
}
}

Expand All @@ -147,13 +136,14 @@ impl RenderError {
RenderErrorReason::MissingVariable(path.map(|p| p.to_owned())).into()
}

#[deprecated(since = "5.0.0", note = "Use RenderErrorReason::NestedError instead")]
pub fn from_error<E>(error_info: &str, cause: E) -> RenderError
where
E: StdError + Send + Sync + 'static,
{
RenderError {
desc: error_info.to_owned(),
cause: Some(Box::new(cause)),
cause: Some(RenderErrorReason::NestedError(Box::new(cause))),
..Default::default()
}
}
Expand All @@ -162,6 +152,10 @@ impl RenderError {
pub(crate) fn is_unimplemented(&self) -> bool {
self.unimplemented
}

pub fn reason(&self) -> Option<&RenderErrorReason> {
self.cause.as_ref()
}
}

/// Template parsing error
Expand Down
12 changes: 7 additions & 5 deletions src/helpers/scripting.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::{BTreeMap, HashMap};

use crate::context::Context;
use crate::error::RenderError;
use crate::error::{RenderError, RenderErrorReason};
use crate::helpers::HelperDef;
use crate::json::value::{PathAndJson, ScopedJson};
use crate::registry::Registry;
Expand All @@ -23,23 +23,25 @@ fn call_script_helper<'reg: 'rc, 'rc>(
engine: &Engine,
script: &AST,
) -> Result<ScopedJson<'rc>, RenderError> {
let params: Dynamic = to_dynamic(params.iter().map(|p| p.value()).collect::<Vec<&Json>>())?;
let params: Dynamic = to_dynamic(params.iter().map(|p| p.value()).collect::<Vec<&Json>>())
.map_err(RenderErrorReason::from)?;

let hash: Dynamic = to_dynamic(
hash.iter()
.map(|(k, v)| ((*k).to_owned(), v.value()))
.collect::<HashMap<String, &Json>>(),
)?;
)
.map_err(RenderErrorReason::from)?;

let mut scope = Scope::new();
scope.push_dynamic("params", params);
scope.push_dynamic("hash", hash);

let result = engine
.eval_ast_with_scope::<Dynamic>(&mut scope, script)
.map_err(RenderError::from)?;
.map_err(RenderErrorReason::from)?;

let result_json: Json = from_dynamic(&result)?;
let result_json: Json = from_dynamic(&result).map_err(RenderErrorReason::from)?;

Ok(ScopedJson::Derived(result_json))
}
Expand Down
13 changes: 7 additions & 6 deletions src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@ use crate::context::Context;
use crate::decorators::{self, DecoratorDef};
#[cfg(feature = "script_helper")]
use crate::error::ScriptError;
use crate::error::{RenderError, TemplateError};
use crate::error::{RenderError, RenderErrorReason, TemplateError};
use crate::helpers::{self, HelperDef};
use crate::output::{Output, StringOutput, WriteOutput};
use crate::render::{RenderContext, Renderable};
use crate::sources::{FileSource, Source};
use crate::support::str::{self, StringWriter};
use crate::template::{Template, TemplateOptions};
use crate::RenderErrorReason;

#[cfg(feature = "dir_source")]
use walkdir::WalkDir;
Expand Down Expand Up @@ -597,7 +596,7 @@ impl<'reg> Registry<'reg> {
)
})
.map(Cow::Owned)
.map_err(RenderError::from);
.map_err(|e| RenderErrorReason::from(e).into());
Some(r)
} else {
self.templates.get(name).map(|t| Ok(Cow::Borrowed(t)))
Expand Down Expand Up @@ -633,7 +632,7 @@ impl<'reg> Registry<'reg> {
}) as Box<dyn HelperDef + Send + Sync>;
Ok(Some(helper.into()))
})
.map_err(RenderError::from);
.map_err(|e| RenderError::from(RenderErrorReason::from(e)));
}

Ok(self.helpers.get(name).cloned())
Expand Down Expand Up @@ -755,7 +754,8 @@ impl<'reg> Registry<'reg> {
prevent_indent: self.prevent_indent,
..Default::default()
},
)?;
)
.map_err(RenderErrorReason::from)?;

let mut out = StringOutput::new();
{
Expand Down Expand Up @@ -783,7 +783,8 @@ impl<'reg> Registry<'reg> {
prevent_indent: self.prevent_indent,
..Default::default()
},
)?;
)
.map_err(RenderErrorReason::from)?;
let mut render_context = RenderContext::new(None);
let mut out = WriteOutput::new(writer);
tpl.render(self, ctx, &mut render_context, &mut out)
Expand Down
3 changes: 2 additions & 1 deletion src/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,8 @@ pub trait Renderable {
) -> Result<String, RenderError> {
let mut so = StringOutput::new();
self.render(registry, ctx, rc, &mut so)?;
so.into_string().map_err(RenderError::from)
so.into_string()
.map_err(|e| RenderErrorReason::from(e).into())
}
}

Expand Down

0 comments on commit 0d30e2d

Please sign in to comment.