From f8149a658b93f67476c1b16b283c28dd4bcfa858 Mon Sep 17 00:00:00 2001 From: Ning Sun Date: Sun, 14 May 2023 22:49:17 +0800 Subject: [PATCH 1/5] refactor: use RenderErrorReason over custom RenderError messagec --- src/decorators/inline.rs | 7 ++++--- src/error.rs | 27 +++++++++++++++++---------- src/helpers/helper_each.rs | 5 ++--- src/helpers/helper_if.rs | 6 ++---- src/helpers/helper_log.rs | 9 +++------ src/helpers/helper_lookup.rs | 9 +++------ src/helpers/helper_with.rs | 5 ++--- src/lib.rs | 2 +- src/partial.rs | 2 +- src/render.rs | 15 ++++++--------- 10 files changed, 41 insertions(+), 46 deletions(-) diff --git a/src/decorators/inline.rs b/src/decorators/inline.rs index 72fac604d..90ab5b942 100644 --- a/src/decorators/inline.rs +++ b/src/decorators/inline.rs @@ -3,18 +3,19 @@ use crate::decorators::{DecoratorDef, DecoratorResult}; use crate::error::RenderError; use crate::registry::Registry; use crate::render::{Decorator, RenderContext}; +use crate::RenderErrorReason; #[derive(Clone, Copy)] pub struct InlineDecorator; fn get_name<'reg: 'rc, 'rc>(d: &Decorator<'rc>) -> Result { d.param(0) - .ok_or_else(|| RenderError::new("Param required for decorator \"inline\"")) + .ok_or_else(|| RenderErrorReason::ParamNotFound.into()) .and_then(|v| { v.value() .as_str() .map(|v| v.to_owned()) - .ok_or_else(|| RenderError::new("inline name must be string")) + .ok_or_else(|| RenderErrorReason::InvalidParamType("String").into()) }) } @@ -30,7 +31,7 @@ impl DecoratorDef for InlineDecorator { let template = d .template() - .ok_or_else(|| RenderError::new("inline should have a block"))?; + .ok_or_else(|| RenderErrorReason::BlockContentRequired)?; rc.set_partial(name, template); Ok(()) diff --git a/src/error.rs b/src/error.rs index 9947f7628..9afbf2c8c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -75,22 +75,29 @@ impl From for RenderError { /// Template rendering error #[derive(Debug, Error)] pub enum RenderErrorReason { - #[error("missing variable path {0:?}")] + #[error("Failed to access variable in strict mode {0:?}")] MissingVariable(Option), - #[error("partial not found {0}")] + #[error("Partial not found {0}")] PartialNotFound(String), + #[error("Helper not found {0}")] + HelperNotFound(String), + #[error("Helper param required but not found")] + ParamNotFound, + #[error("Decorator not found {0}")] + DecoratorNotFound(String), + #[error("Can not include current template in partial")] + CannotIncludeSelf, + #[error("Invalid logging level: {0}")] + InvalidLoggingLevel(String), + #[error("Invalid param type, {0} expected")] + InvalidParamType(&'static str), + #[error("Block content required")] + BlockContentRequired, } impl From for RenderError { fn from(e: RenderErrorReason) -> RenderError { - match e { - RenderErrorReason::MissingVariable(_) => { - RenderError::from_error("Failed to access variable in strict mode.", e) - } - RenderErrorReason::PartialNotFound(_) => { - RenderError::from_error("Partial not found.", e) - } - } + RenderError::from_error(&e.to_string(), e) } } diff --git a/src/helpers/helper_each.rs b/src/helpers/helper_each.rs index 2fec8f010..60e6b1ff7 100644 --- a/src/helpers/helper_each.rs +++ b/src/helpers/helper_each.rs @@ -10,6 +10,7 @@ use crate::output::Output; use crate::registry::Registry; use crate::render::{Helper, RenderContext, Renderable}; use crate::util::copy_on_push_vec; +use crate::RenderErrorReason; fn update_block_context( block: &mut BlockContext<'_>, @@ -72,9 +73,7 @@ impl HelperDef for EachHelper { rc: &mut RenderContext<'reg, 'rc>, out: &mut dyn Output, ) -> HelperResult { - let value = h - .param(0) - .ok_or_else(|| RenderError::new("Param not found for helper \"each\""))?; + let value = h.param(0).ok_or_else(|| RenderErrorReason::ParamNotFound)?; let template = h.template(); diff --git a/src/helpers/helper_if.rs b/src/helpers/helper_if.rs index e9ea33c32..33d1aea6e 100644 --- a/src/helpers/helper_if.rs +++ b/src/helpers/helper_if.rs @@ -1,10 +1,10 @@ use crate::context::Context; -use crate::error::RenderError; use crate::helpers::{HelperDef, HelperResult}; use crate::json::value::JsonTruthy; use crate::output::Output; use crate::registry::Registry; use crate::render::{Helper, RenderContext, Renderable}; +use crate::RenderErrorReason; #[derive(Clone, Copy)] pub struct IfHelper { @@ -20,9 +20,7 @@ impl HelperDef for IfHelper { rc: &mut RenderContext<'reg, 'rc>, out: &mut dyn Output, ) -> HelperResult { - let param = h - .param(0) - .ok_or_else(|| RenderError::new("Param not found for helper \"if\""))?; + let param = h.param(0).ok_or_else(|| RenderErrorReason::ParamNotFound)?; let include_zero = h .hash_get("includeZero") .and_then(|v| v.value().as_bool()) diff --git a/src/helpers/helper_log.rs b/src/helpers/helper_log.rs index 36394be32..720ef7792 100644 --- a/src/helpers/helper_log.rs +++ b/src/helpers/helper_log.rs @@ -1,6 +1,4 @@ use crate::context::Context; -#[cfg(not(feature = "no_logging"))] -use crate::error::RenderError; use crate::helpers::{HelperDef, HelperResult}; #[cfg(not(feature = "no_logging"))] use crate::json::value::JsonRender; @@ -8,6 +6,8 @@ use crate::output::Output; use crate::registry::Registry; use crate::render::{Helper, RenderContext}; #[cfg(not(feature = "no_logging"))] +use crate::RenderErrorReason; +#[cfg(not(feature = "no_logging"))] use log::Level; #[cfg(not(feature = "no_logging"))] use std::str::FromStr; @@ -46,10 +46,7 @@ impl HelperDef for LogHelper { if let Ok(log_level) = Level::from_str(level) { log!(log_level, "{}", param_to_log) } else { - return Err(RenderError::new(&format!( - "Unsupported logging level {}", - level - ))); + return Err(RenderErrorReason::InvalidLoggingLevel(level.to_string()).into()); } Ok(()) } diff --git a/src/helpers/helper_lookup.rs b/src/helpers/helper_lookup.rs index 5afb14e99..665784b44 100644 --- a/src/helpers/helper_lookup.rs +++ b/src/helpers/helper_lookup.rs @@ -6,6 +6,7 @@ use crate::helpers::HelperDef; use crate::json::value::ScopedJson; use crate::registry::Registry; use crate::render::{Helper, RenderContext}; +use crate::RenderErrorReason; #[derive(Clone, Copy)] pub struct LookupHelper; @@ -18,12 +19,8 @@ impl HelperDef for LookupHelper { _: &'rc Context, _: &mut RenderContext<'reg, 'rc>, ) -> Result, RenderError> { - let collection_value = h - .param(0) - .ok_or_else(|| RenderError::new("Param not found for helper \"lookup\""))?; - let index = h - .param(1) - .ok_or_else(|| RenderError::new("Insufficient params for helper \"lookup\""))?; + let collection_value = h.param(0).ok_or_else(|| RenderErrorReason::ParamNotFound)?; + let index = h.param(1).ok_or_else(|| RenderErrorReason::ParamNotFound)?; let value = match *collection_value.value() { Json::Array(ref v) => index.value().as_u64().and_then(|u| v.get(u as usize)), diff --git a/src/helpers/helper_with.rs b/src/helpers/helper_with.rs index 96f07929d..535959fb4 100644 --- a/src/helpers/helper_with.rs +++ b/src/helpers/helper_with.rs @@ -7,6 +7,7 @@ use crate::json::value::JsonTruthy; use crate::output::Output; use crate::registry::Registry; use crate::render::{Helper, RenderContext, Renderable}; +use crate::RenderErrorReason; #[derive(Clone, Copy)] pub struct WithHelper; @@ -20,9 +21,7 @@ impl HelperDef for WithHelper { rc: &mut RenderContext<'reg, 'rc>, out: &mut dyn Output, ) -> HelperResult { - let param = h - .param(0) - .ok_or_else(|| RenderError::new("Param not found for helper \"with\""))?; + let param = h.param(0).ok_or_else(|| RenderErrorReason::ParamNotFound)?; if param.value().is_truthy(false) { let mut block = create_block(param); diff --git a/src/lib.rs b/src/lib.rs index 0693765ce..86786b3dd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -287,7 +287,7 @@ //! // via closure //! handlebars.register_helper("closure-helper", //! Box::new(|h: &Helper, r: &Handlebars, _: &Context, rc: &mut RenderContext, out: &mut dyn Output| -> HelperResult { -//! let param = h.param(0).ok_or(RenderError::new("param not found"))?; +//! let param = h.param(0).ok_or(RenderErrorReason::ParamNotFound)?; //! //! out.write("3rd helper: ")?; //! out.write(param.value().render().as_ref())?; diff --git a/src/partial.rs b/src/partial.rs index 54b31b78b..d1cc7afab 100644 --- a/src/partial.rs +++ b/src/partial.rs @@ -50,7 +50,7 @@ pub fn expand_partial<'reg: 'rc, 'rc>( let tname = d.name(); if rc.is_current_template(tname) { - return Err(RenderError::new("Cannot include self in >")); + return Err(RenderErrorReason::CannotIncludeSelf.into()); } let partial = find_partial(rc, r, d, tname)?; diff --git a/src/render.rs b/src/render.rs index 894b1dc7f..ba7f4ae89 100644 --- a/src/render.rs +++ b/src/render.rs @@ -12,7 +12,6 @@ use crate::helpers::HelperDef; use crate::json::path::Path; use crate::json::value::{JsonRender, PathAndJson, ScopedJson}; use crate::output::{Output, StringOutput}; -use crate::partial; use crate::registry::Registry; use crate::support; use crate::template::TemplateElement::*; @@ -20,6 +19,7 @@ use crate::template::{ BlockParam, DecoratorTemplate, HelperTemplate, Parameter, Template, TemplateElement, TemplateMapping, }; +use crate::{partial, RenderErrorReason}; const HELPER_MISSING: &str = "helperMissing"; const BLOCK_HELPER_MISSING: &str = "blockHelperMissing"; @@ -367,7 +367,7 @@ impl<'reg: 'rc, 'rc> Helper<'rc> { /// /// fn my_helper(h: &Helper, rc: &mut RenderContext) -> Result<(), RenderError> { /// let v = h.param(0).map(|v| v.value()) - /// .ok_or(RenderError::new("param not found")); + /// .ok_or(RenderErrorReason::ParamNotFound); /// // .. /// Ok(()) /// } @@ -394,7 +394,7 @@ impl<'reg: 'rc, 'rc> Helper<'rc> { /// /// fn my_helper(h: &Helper, rc: &mut RenderContext) -> Result<(), RenderError> { /// let v = h.hash_get("v").map(|v| v.value()) - /// .ok_or(RenderError::new("param not found")); + /// .ok_or(RenderErrorReason::ParamNotFound); /// // .. /// Ok(()) /// } @@ -653,7 +653,7 @@ impl Parameter { helper .ok_or_else(|| { - RenderError::new(format!("Helper not defined: {:?}", ht.name)) + RenderErrorReason::HelperNotFound(name.to_string()).into() }) .and_then(|d| call_helper_for_value(d.as_ref(), &h, registry, ctx, rc)) } @@ -759,7 +759,7 @@ fn render_helper<'reg: 'rc, 'rc>( } helper - .ok_or_else(|| RenderError::new(format!("Helper not defined: {:?}", h.name()))) + .ok_or_else(|| RenderErrorReason::HelperNotFound(h.name().to_owned()).into()) .and_then(|d| d.call(&h, registry, ctx, rc, out)) } } @@ -863,10 +863,7 @@ impl Evaluable for TemplateElement { let di = Decorator::try_from_template(dt, registry, ctx, rc)?; match registry.get_decorator(di.name()) { Some(d) => d.call(&di, registry, ctx, rc), - None => Err(RenderError::new(format!( - "Decorator not defined: {:?}", - dt.name - ))), + None => Err(RenderErrorReason::DecoratorNotFound(di.name().to_owned()).into()), } } _ => Ok(()), From 821e97bb41c45bf20cd471c73c534cc3cbb7c30a Mon Sep 17 00:00:00 2001 From: Ning Sun Date: Sun, 14 May 2023 23:03:31 +0800 Subject: [PATCH 2/5] refactor: resolve lint issues after switching to RenderErrorReason --- src/decorators/inline.rs | 2 +- src/helpers/helper_each.rs | 2 +- src/helpers/helper_if.rs | 2 +- src/helpers/helper_lookup.rs | 4 ++-- src/helpers/helper_with.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/decorators/inline.rs b/src/decorators/inline.rs index 90ab5b942..d457ccd62 100644 --- a/src/decorators/inline.rs +++ b/src/decorators/inline.rs @@ -31,7 +31,7 @@ impl DecoratorDef for InlineDecorator { let template = d .template() - .ok_or_else(|| RenderErrorReason::BlockContentRequired)?; + .ok_or(RenderErrorReason::BlockContentRequired)?; rc.set_partial(name, template); Ok(()) diff --git a/src/helpers/helper_each.rs b/src/helpers/helper_each.rs index 60e6b1ff7..8ced25bf5 100644 --- a/src/helpers/helper_each.rs +++ b/src/helpers/helper_each.rs @@ -73,7 +73,7 @@ impl HelperDef for EachHelper { rc: &mut RenderContext<'reg, 'rc>, out: &mut dyn Output, ) -> HelperResult { - let value = h.param(0).ok_or_else(|| RenderErrorReason::ParamNotFound)?; + let value = h.param(0).ok_or(RenderErrorReason::ParamNotFound)?; let template = h.template(); diff --git a/src/helpers/helper_if.rs b/src/helpers/helper_if.rs index 33d1aea6e..64c7251c6 100644 --- a/src/helpers/helper_if.rs +++ b/src/helpers/helper_if.rs @@ -20,7 +20,7 @@ impl HelperDef for IfHelper { rc: &mut RenderContext<'reg, 'rc>, out: &mut dyn Output, ) -> HelperResult { - let param = h.param(0).ok_or_else(|| RenderErrorReason::ParamNotFound)?; + let param = h.param(0).ok_or(RenderErrorReason::ParamNotFound)?; let include_zero = h .hash_get("includeZero") .and_then(|v| v.value().as_bool()) diff --git a/src/helpers/helper_lookup.rs b/src/helpers/helper_lookup.rs index 665784b44..5c02392d2 100644 --- a/src/helpers/helper_lookup.rs +++ b/src/helpers/helper_lookup.rs @@ -19,8 +19,8 @@ impl HelperDef for LookupHelper { _: &'rc Context, _: &mut RenderContext<'reg, 'rc>, ) -> Result, RenderError> { - let collection_value = h.param(0).ok_or_else(|| RenderErrorReason::ParamNotFound)?; - let index = h.param(1).ok_or_else(|| RenderErrorReason::ParamNotFound)?; + let collection_value = h.param(0).ok_or(RenderErrorReason::ParamNotFound)?; + let index = h.param(1).ok_or(RenderErrorReason::ParamNotFound)?; let value = match *collection_value.value() { Json::Array(ref v) => index.value().as_u64().and_then(|u| v.get(u as usize)), diff --git a/src/helpers/helper_with.rs b/src/helpers/helper_with.rs index 535959fb4..37e2db24f 100644 --- a/src/helpers/helper_with.rs +++ b/src/helpers/helper_with.rs @@ -21,7 +21,7 @@ impl HelperDef for WithHelper { rc: &mut RenderContext<'reg, 'rc>, out: &mut dyn Output, ) -> HelperResult { - let param = h.param(0).ok_or_else(|| RenderErrorReason::ParamNotFound)?; + let param = h.param(0).ok_or(RenderErrorReason::ParamNotFound)?; if param.value().is_truthy(false) { let mut block = create_block(param); From 563d5f468d951a3917f71ddf17605668a6e23ea2 Mon Sep 17 00:00:00 2001 From: Ning Sun Date: Mon, 15 May 2023 21:52:41 +0800 Subject: [PATCH 3/5] refactor: update more error types into RenderErrorReason --- src/decorators/inline.rs | 2 +- src/error.rs | 16 ++++++++++++++-- src/helpers/helper_each.rs | 4 +++- src/helpers/helper_if.rs | 4 +++- src/helpers/helper_lookup.rs | 8 ++++++-- src/helpers/helper_with.rs | 4 +++- src/json/path.rs | 3 ++- src/lib.rs | 5 +++-- src/macros.rs | 21 +++++---------------- src/registry.rs | 3 ++- src/render.rs | 4 ++-- tests/subexpression.rs | 15 +++++++++++---- 12 files changed, 55 insertions(+), 34 deletions(-) diff --git a/src/decorators/inline.rs b/src/decorators/inline.rs index d457ccd62..8eec1a689 100644 --- a/src/decorators/inline.rs +++ b/src/decorators/inline.rs @@ -10,7 +10,7 @@ pub struct InlineDecorator; fn get_name<'reg: 'rc, 'rc>(d: &Decorator<'rc>) -> Result { d.param(0) - .ok_or_else(|| RenderErrorReason::ParamNotFound.into()) + .ok_or_else(|| RenderErrorReason::ParamNotFoundForIndex("inline", 0).into()) .and_then(|v| { v.value() .as_str() diff --git a/src/error.rs b/src/error.rs index 9afbf2c8c..43f4de2d1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -75,14 +75,22 @@ impl From for RenderError { /// Template rendering error #[derive(Debug, Error)] pub enum RenderErrorReason { + #[error("Template not found {0}")] + TemplateNotFound(String), #[error("Failed to access variable in strict mode {0:?}")] MissingVariable(Option), #[error("Partial not found {0}")] PartialNotFound(String), #[error("Helper not found {0}")] HelperNotFound(String), - #[error("Helper param required but not found")] - ParamNotFound, + #[error("Helper/Decorator {0} param at index {1} required but not found")] + ParamNotFoundForIndex(&'static str, usize), + #[error("Helper/Decorator {0} param with name {1} required but not found")] + ParamNotFoundForName(&'static str, String), + #[error("Helper/Decorator {0} param with name {1} type mismatch for {2}")] + ParamTypeMismatchForName(&'static str, String, String), + #[error("Helper/Decorator {0} hash with name {1} type mismatch for {2}")] + HashTypeMismatchForName(&'static str, String, String), #[error("Decorator not found {0}")] DecoratorNotFound(String), #[error("Can not include current template in partial")] @@ -93,6 +101,10 @@ pub enum RenderErrorReason { InvalidParamType(&'static str), #[error("Block content required")] BlockContentRequired, + #[error("Invalid json path {0}")] + InvalidJsonPath(String), + #[error("{0}")] + Other(String), } impl From for RenderError { diff --git a/src/helpers/helper_each.rs b/src/helpers/helper_each.rs index 8ced25bf5..6246d35f9 100644 --- a/src/helpers/helper_each.rs +++ b/src/helpers/helper_each.rs @@ -73,7 +73,9 @@ impl HelperDef for EachHelper { rc: &mut RenderContext<'reg, 'rc>, out: &mut dyn Output, ) -> HelperResult { - let value = h.param(0).ok_or(RenderErrorReason::ParamNotFound)?; + let value = h + .param(0) + .ok_or(RenderErrorReason::ParamNotFoundForIndex("each", 0))?; let template = h.template(); diff --git a/src/helpers/helper_if.rs b/src/helpers/helper_if.rs index 64c7251c6..60aee45ee 100644 --- a/src/helpers/helper_if.rs +++ b/src/helpers/helper_if.rs @@ -20,7 +20,9 @@ impl HelperDef for IfHelper { rc: &mut RenderContext<'reg, 'rc>, out: &mut dyn Output, ) -> HelperResult { - let param = h.param(0).ok_or(RenderErrorReason::ParamNotFound)?; + let param = h + .param(0) + .ok_or(RenderErrorReason::ParamNotFoundForIndex("if", 0))?; let include_zero = h .hash_get("includeZero") .and_then(|v| v.value().as_bool()) diff --git a/src/helpers/helper_lookup.rs b/src/helpers/helper_lookup.rs index 5c02392d2..0794b6863 100644 --- a/src/helpers/helper_lookup.rs +++ b/src/helpers/helper_lookup.rs @@ -19,8 +19,12 @@ impl HelperDef for LookupHelper { _: &'rc Context, _: &mut RenderContext<'reg, 'rc>, ) -> Result, RenderError> { - let collection_value = h.param(0).ok_or(RenderErrorReason::ParamNotFound)?; - let index = h.param(1).ok_or(RenderErrorReason::ParamNotFound)?; + let collection_value = h + .param(0) + .ok_or(RenderErrorReason::ParamNotFoundForIndex("lookup", 0))?; + let index = h + .param(1) + .ok_or(RenderErrorReason::ParamNotFoundForIndex("lookup", 1))?; let value = match *collection_value.value() { Json::Array(ref v) => index.value().as_u64().and_then(|u| v.get(u as usize)), diff --git a/src/helpers/helper_with.rs b/src/helpers/helper_with.rs index 37e2db24f..eb85133b6 100644 --- a/src/helpers/helper_with.rs +++ b/src/helpers/helper_with.rs @@ -21,7 +21,9 @@ impl HelperDef for WithHelper { rc: &mut RenderContext<'reg, 'rc>, out: &mut dyn Output, ) -> HelperResult { - let param = h.param(0).ok_or(RenderErrorReason::ParamNotFound)?; + let param = h + .param(0) + .ok_or(RenderErrorReason::ParamNotFoundForIndex("with", 0))?; if param.value().is_truthy(false) { let mut block = create_block(param); diff --git a/src/json/path.rs b/src/json/path.rs index 17a7a91ee..288c1561a 100644 --- a/src/json/path.rs +++ b/src/json/path.rs @@ -5,6 +5,7 @@ use pest::Parser; use crate::error::RenderError; use crate::grammar::{HandlebarsParser, Rule}; +use crate::RenderErrorReason; #[derive(PartialEq, Eq, Clone, Debug)] pub enum PathSeg { @@ -38,7 +39,7 @@ impl Path { let segs = parse_json_path_from_iter(&mut parsed.peekable(), raw.len()); Ok(Path::new(raw, segs)) }) - .map_err(|_| RenderError::new("Invalid JSON path"))? + .map_err(|_| RenderErrorReason::InvalidJsonPath(raw.to_owned()))? } pub(crate) fn raw(&self) -> &str { diff --git a/src/lib.rs b/src/lib.rs index 86786b3dd..9959c90a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -254,7 +254,7 @@ //! ``` //! use std::io::Write; //! # use std::error::Error; -//! use handlebars::{Handlebars, HelperDef, RenderContext, Helper, Context, JsonRender, HelperResult, Output, RenderError}; +//! use handlebars::*; //! //! // implement by a structure impls HelperDef //! #[derive(Clone, Copy)] @@ -287,7 +287,8 @@ //! // via closure //! handlebars.register_helper("closure-helper", //! Box::new(|h: &Helper, r: &Handlebars, _: &Context, rc: &mut RenderContext, out: &mut dyn Output| -> HelperResult { -//! let param = h.param(0).ok_or(RenderErrorReason::ParamNotFound)?; +//! let param = +//! h.param(0).ok_or(RenderErrorReason::ParamNotFoundForIndex("closure-helper", 0))?; //! //! out.write("3rd helper: ")?; //! out.write(param.value().render().as_ref())?; diff --git a/src/macros.rs b/src/macros.rs index 92f77780f..c299adb88 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -66,18 +66,10 @@ macro_rules! handlebars_helper { Some(x.value()) } }) - .ok_or_else(|| $crate::RenderError::new(&format!( - "`{}` helper: Couldn't read parameter {}", - stringify!($struct_name), stringify!($name), - ))) + .ok_or_else(|| $crate::RenderErrorReason::ParamNotFoundForName(stringify!($struct_name), stringify!($name).to_string())) .and_then(|x| handlebars_helper!(@as_json_value x, $tpe$(<$($gen),+>)?) - .ok_or_else(|| $crate::RenderError::new(&format!( - "`{}` helper: Couldn't convert parameter {} to type `{}`. \ - It's {:?} as JSON. Got these params: {:?}", - stringify!($struct_name), stringify!($name), stringify!($tpe$(<$($gen),+>)?), - x, h.params(), - ))) + .ok_or_else(|| $crate::RenderErrorReason::ParamTypeMismatchForName(stringify!($struct_name), stringify!($name).to_string(), stringify!($tpe$(<$($gen),+>)?).to_string()).into()) )?; param_idx += 1; )* @@ -88,12 +80,9 @@ macro_rules! handlebars_helper { .map(|x| x.value()) .map(|x| handlebars_helper!(@as_json_value x, $hash_tpe) - .ok_or_else(|| $crate::RenderError::new(&format!( - "`{}` helper: Couldn't convert hash {} to type `{}`. \ - It's {:?} as JSON. Got these hash: {:?}", - stringify!($struct_name), stringify!($hash_name), stringify!($hash_tpe), - x, h.hash(), - ))) + .ok_or_else(|| $crate::RenderErrorReason::HashTypeMismatchForName( + stringify!($struct_name), stringify!($hash_name).to_string(), stringify!($hash_tpe).to_string() + )) ) .unwrap_or_else(|| Ok($dft_val))?; )* diff --git a/src/registry.rs b/src/registry.rs index f810628d2..d1a69de41 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -18,6 +18,7 @@ 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; @@ -521,7 +522,7 @@ impl<'reg> Registry<'reg> { if let Some(result) = self.get_or_load_template_optional(name) { result } else { - Err(RenderError::new(format!("Template not found: {name}"))) + Err(RenderErrorReason::TemplateNotFound(name.to_owned()).into()) } } diff --git a/src/render.rs b/src/render.rs index ba7f4ae89..f9a4fc867 100644 --- a/src/render.rs +++ b/src/render.rs @@ -367,7 +367,7 @@ impl<'reg: 'rc, 'rc> Helper<'rc> { /// /// fn my_helper(h: &Helper, rc: &mut RenderContext) -> Result<(), RenderError> { /// let v = h.param(0).map(|v| v.value()) - /// .ok_or(RenderErrorReason::ParamNotFound); + /// .ok_or(RenderErrorReason::ParamNotFoundForIndex("myhelper", 0)); /// // .. /// Ok(()) /// } @@ -394,7 +394,7 @@ impl<'reg: 'rc, 'rc> Helper<'rc> { /// /// fn my_helper(h: &Helper, rc: &mut RenderContext) -> Result<(), RenderError> { /// let v = h.hash_get("v").map(|v| v.value()) - /// .ok_or(RenderErrorReason::ParamNotFound); + /// .ok_or(RenderErrorReason::ParamNotFoundForIndex("my_helper", 0)); /// // .. /// Ok(()) /// } diff --git a/tests/subexpression.rs b/tests/subexpression.rs index 267770840..bcd4baa0d 100644 --- a/tests/subexpression.rs +++ b/tests/subexpression.rs @@ -2,10 +2,14 @@ extern crate handlebars; #[macro_use] extern crate serde_json; +use std::error::Error; use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::Arc; -use handlebars::{Context, Handlebars, Helper, HelperDef, RenderContext, RenderError, ScopedJson}; +use handlebars::{ + Context, Handlebars, Helper, HelperDef, RenderContext, RenderError, RenderErrorReason, + ScopedJson, +}; #[test] fn test_subexpression() { @@ -78,10 +82,13 @@ fn invalid_json_path() { let hbs = Handlebars::new(); let error = hbs.render_template("{{x[]@this}}", &data).unwrap_err(); + let cause = error + .source() + .unwrap() + .downcast_ref::() + .unwrap(); - let expected = "Error rendering \"Unnamed template\" line 1, col 1: Helper not defined: \"x\""; - - assert_eq!(format!("{}", error), expected); + assert!(matches!(cause, RenderErrorReason::HelperNotFound(_))); } struct MyHelper; From 743acbe6c2298a4e9566c775d64bb0263e7eb50c Mon Sep 17 00:00:00 2001 From: Ning Sun Date: Mon, 15 May 2023 22:07:12 +0800 Subject: [PATCH 4/5] refactor: update examples and tests for RenderErrorReason --- examples/decorator.rs | 24 ++++++++++++------------ examples/error.rs | 6 ++++-- examples/render.rs | 18 +++++++++--------- examples/render_file.rs | 19 ++++++++++--------- tests/helper_function_lifetime.rs | 7 +++---- 5 files changed, 38 insertions(+), 36 deletions(-) diff --git a/examples/decorator.rs b/examples/decorator.rs index 6d5357ba0..7dc926a42 100644 --- a/examples/decorator.rs +++ b/examples/decorator.rs @@ -8,7 +8,8 @@ use std::error::Error; use serde_json::value::{Map, Value as Json}; use handlebars::{ - to_json, Context, Decorator, Handlebars, Helper, JsonRender, Output, RenderContext, RenderError, + to_json, Context, Decorator, Handlebars, Helper, JsonRender, Output, RenderContext, + RenderError, RenderErrorReason, }; // default format helper @@ -22,7 +23,7 @@ fn format_helper( // get parameter from helper or throw an error let param = h .param(0) - .ok_or(RenderError::new("Param 0 is required for format helper."))?; + .ok_or(RenderErrorReason::ParamNotFoundForIndex("format", 0))?; write!(out, "{} pts", param.value().render())?; Ok(()) } @@ -49,7 +50,7 @@ fn format_decorator( // get parameter from helper or throw an error let param = h .param(0) - .ok_or(RenderError::new("Param 0 is required for format helper."))?; + .ok_or(RenderErrorReason::ParamNotFoundForIndex("format", 0))?; write!(out, "{} {}", param.value().render(), suffix)?; Ok(()) }, @@ -80,7 +81,7 @@ fn set_decorator( rc.set_context(Context::wraps(new_ctx_data)?); Ok(()) } else { - Err(RenderError::new("Cannot extend non-object data")) + Err(RenderErrorReason::Other("Cannot extend non-object data".to_owned()).into()) } } @@ -92,19 +93,18 @@ fn rank_helper( _: &mut RenderContext, out: &mut dyn Output, ) -> Result<(), RenderError> { - let rank = h - .param(0) - .and_then(|v| v.value().as_u64()) - .ok_or(RenderError::new( - "Param 0 with u64 type is required for rank helper.", - ))? as usize; + let rank = h.param(0).and_then(|v| v.value().as_u64()).ok_or( + RenderErrorReason::ParamTypeMismatchForName("rank", "0".to_string(), "u64".to_string()), + )? as usize; let total = h .param(1) .as_ref() .and_then(|v| v.value().as_array()) .map(|arr| arr.len()) - .ok_or(RenderError::new( - "Param 1 with array type is required for rank helper", + .ok_or(RenderErrorReason::ParamTypeMismatchForName( + "rank", + "1".to_string(), + "array".to_string(), ))?; if rank == 0 { out.write("champion")?; diff --git a/examples/error.rs b/examples/error.rs index ed0f7c423..672c4bc1d 100644 --- a/examples/error.rs +++ b/examples/error.rs @@ -5,7 +5,9 @@ extern crate serde_json; use std::error::Error as StdError; -use handlebars::{Context, Handlebars, Helper, Output, RenderContext, RenderError}; +use handlebars::{ + Context, Handlebars, Helper, Output, RenderContext, RenderError, RenderErrorReason, +}; use thiserror::Error; #[derive(Debug, Error)] @@ -26,7 +28,7 @@ pub fn error_helper( ) -> Result<(), RenderError> { let param = h .param(0) - .ok_or(RenderError::new("Param 0 is required for error helper."))?; + .ok_or(RenderErrorReason::ParamNotFoundForIndex("error", 0))?; match param.value().as_str() { Some("db") => Err(RenderError::from_error( "helper error", diff --git a/examples/render.rs b/examples/render.rs index a6f26d0da..e31e07268 100644 --- a/examples/render.rs +++ b/examples/render.rs @@ -10,6 +10,7 @@ use std::error::Error; use handlebars::{ to_json, Context, Handlebars, Helper, JsonRender, Output, RenderContext, RenderError, + RenderErrorReason, }; // define a custom helper @@ -23,7 +24,7 @@ fn format_helper( // get parameter from helper or throw an error let param = h .param(0) - .ok_or(RenderError::new("Param 0 is required for format helper."))?; + .ok_or(RenderErrorReason::ParamNotFoundForIndex("format", 0))?; write!(out, "{} pts", param.value().render())?; Ok(()) } @@ -36,19 +37,18 @@ fn rank_helper( _: &mut RenderContext, out: &mut dyn Output, ) -> Result<(), RenderError> { - let rank = h - .param(0) - .and_then(|v| v.value().as_u64()) - .ok_or(RenderError::new( - "Param 0 with u64 type is required for rank helper.", - ))? as usize; + let rank = h.param(0).and_then(|v| v.value().as_u64()).ok_or( + RenderErrorReason::ParamTypeMismatchForName("rank", "0".to_string(), "u64".to_string()), + )? as usize; let total = h .param(1) .as_ref() .and_then(|v| v.value().as_array()) .map(|arr| arr.len()) - .ok_or(RenderError::new( - "Param 1 with array type is required for rank helper", + .ok_or(RenderErrorReason::ParamTypeMismatchForName( + "rank", + "1".to_string(), + "array".to_string(), ))?; if rank == 0 { out.write("champion")?; diff --git a/examples/render_file.rs b/examples/render_file.rs index 786308608..f3f8e53c8 100644 --- a/examples/render_file.rs +++ b/examples/render_file.rs @@ -14,6 +14,7 @@ use std::io::{Read, Write}; use handlebars::{ to_json, Context, Handlebars, Helper, JsonRender, Output, RenderContext, RenderError, + RenderErrorReason, }; // define a custom helper @@ -24,9 +25,10 @@ fn format_helper( _: &mut RenderContext, out: &mut dyn Output, ) -> Result<(), RenderError> { + // get parameter from helper or throw an error let param = h .param(0) - .ok_or(RenderError::new("Param 0 is required for format helper."))?; + .ok_or(RenderErrorReason::ParamNotFoundForIndex("format", 0))?; write!(out, "{} pts", param.value().render())?; Ok(()) } @@ -39,19 +41,18 @@ fn rank_helper( _: &mut RenderContext, out: &mut dyn Output, ) -> Result<(), RenderError> { - let rank = h - .param(0) - .and_then(|ref v| v.value().as_u64()) - .ok_or(RenderError::new( - "Param 0 with u64 type is required for rank helper.", - ))? as usize; + let rank = h.param(0).and_then(|v| v.value().as_u64()).ok_or( + RenderErrorReason::ParamTypeMismatchForName("rank", "0".to_string(), "u64".to_string()), + )? as usize; let total = h .param(1) .as_ref() .and_then(|v| v.value().as_array()) .map(|arr| arr.len()) - .ok_or(RenderError::new( - "Param 1 with array type is required for rank helper", + .ok_or(RenderErrorReason::ParamTypeMismatchForName( + "rank", + "1".to_string(), + "array".to_string(), ))?; if rank == 0 { out.write("champion")?; diff --git a/tests/helper_function_lifetime.rs b/tests/helper_function_lifetime.rs index ccea42790..59ade4b33 100644 --- a/tests/helper_function_lifetime.rs +++ b/tests/helper_function_lifetime.rs @@ -7,10 +7,9 @@ fn ifcond<'reg, 'rc>( render_ctx: &mut RenderContext<'reg, 'rc>, out: &mut dyn Output, ) -> Result<(), RenderError> { - let cond = h - .param(0) - .and_then(|ref v| v.value().as_bool()) - .ok_or(RenderError::new("Ifcond takes a boolean !"))? as bool; + let cond = h.param(0).and_then(|ref v| v.value().as_bool()).ok_or( + RenderErrorReason::ParamTypeMismatchForName("ifcond", "0".to_owned(), "bool".to_owned()), + )? as bool; let temp = if cond { h.template() } else { h.inverse() }; match temp { Some(t) => t.render(handle, ctx, render_ctx, out), From 275629e25bc211882437fbc2898a39446ff24b72 Mon Sep 17 00:00:00 2001 From: Ning Sun Date: Mon, 15 May 2023 22:11:40 +0800 Subject: [PATCH 5/5] refactor: mark RenderError::new as deprecated --- src/error.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/error.rs b/src/error.rs index 43f4de2d1..97de3f94d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -128,6 +128,7 @@ impl From for RenderError { } impl RenderError { + #[deprecated(since = "5.0.0", note = "Use RenderErrorReason instead")] pub fn new>(desc: T) -> RenderError { RenderError { desc: desc.as_ref().to_owned(), @@ -150,10 +151,11 @@ impl RenderError { where E: StdError + Send + Sync + 'static, { - let mut e = RenderError::new(error_info); - e.cause = Some(Box::new(cause)); - - e + RenderError { + desc: error_info.to_owned(), + cause: Some(Box::new(cause)), + ..Default::default() + } } #[inline]