tera/
errors.rs

1use std::convert::Into;
2use std::error::Error as StdError;
3use std::fmt;
4
5/// The kind of an error (non-exhaustive)
6#[derive(Debug)]
7#[allow(clippy::manual_non_exhaustive)] // reason = "we want to stay backwards compatible, therefore we keep the manual implementation of non_exhaustive"
8pub enum ErrorKind {
9    /// Generic error
10    Msg(String),
11    /// A loop was found while looking up the inheritance chain
12    CircularExtend {
13        /// Name of the template with the loop
14        tpl: String,
15        /// All the parents templates we found so far
16        inheritance_chain: Vec<String>,
17    },
18    /// A template is extending a template that wasn't found in the Tera instance
19    MissingParent {
20        /// The template we are currently looking at
21        current: String,
22        /// The missing template
23        parent: String,
24    },
25    /// A template was missing (more generic version of MissingParent)
26    TemplateNotFound(String),
27    /// A filter wasn't found
28    FilterNotFound(String),
29    /// A test wasn't found
30    TestNotFound(String),
31    /// A macro was defined in the middle of a template
32    InvalidMacroDefinition(String),
33    /// A function wasn't found
34    FunctionNotFound(String),
35    /// An error happened while serializing JSON
36    Json(serde_json::Error),
37    /// An error occured while executing a function.
38    CallFunction(String),
39    /// An error occured while executing a filter.
40    CallFilter(String),
41    /// An error occured while executing a test.
42    CallTest(String),
43    /// An IO error occured
44    Io(std::io::ErrorKind),
45    /// UTF-8 conversion error
46    ///
47    /// This should not occur unless invalid UTF8 chars are rendered
48    Utf8Conversion {
49        /// The context that indicates where the error occurs in the rendering process
50        context: String,
51    },
52    /// This enum may grow additional variants, so this makes sure clients
53    /// don't count on exhaustive matching. (Otherwise, adding a new variant
54    /// could break existing code.)
55    #[doc(hidden)]
56    __Nonexhaustive,
57}
58
59/// The Error type
60#[derive(Debug)]
61pub struct Error {
62    /// Kind of error
63    pub kind: ErrorKind,
64    source: Option<Box<dyn StdError + Sync + Send>>,
65}
66
67impl fmt::Display for Error {
68    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69        match self.kind {
70            ErrorKind::Msg(ref message) => write!(f, "{}", message),
71            ErrorKind::CircularExtend { ref tpl, ref inheritance_chain } => write!(
72                f,
73                "Circular extend detected for template '{}'. Inheritance chain: `{:?}`",
74                tpl, inheritance_chain
75            ),
76            ErrorKind::MissingParent { ref current, ref parent } => write!(
77                f,
78                "Template '{}' is inheriting from '{}', which doesn't exist or isn't loaded.",
79                current, parent
80            ),
81            ErrorKind::TemplateNotFound(ref name) => write!(f, "Template '{}' not found", name),
82            ErrorKind::FilterNotFound(ref name) => write!(f, "Filter '{}' not found", name),
83            ErrorKind::TestNotFound(ref name) => write!(f, "Test '{}' not found", name),
84            ErrorKind::FunctionNotFound(ref name) => write!(f, "Function '{}' not found", name),
85            ErrorKind::InvalidMacroDefinition(ref info) => {
86                write!(f, "Invalid macro definition: `{}`", info)
87            }
88            ErrorKind::Json(ref e) => write!(f, "{}", e),
89            ErrorKind::CallFunction(ref name) => write!(f, "Function call '{}' failed", name),
90            ErrorKind::CallFilter(ref name) => write!(f, "Filter call '{}' failed", name),
91            ErrorKind::CallTest(ref name) => write!(f, "Test call '{}' failed", name),
92            ErrorKind::Io(ref io_error) => {
93                write!(f, "Io error while writing rendered value to output: {:?}", io_error)
94            }
95            ErrorKind::Utf8Conversion { ref context } => {
96                write!(f, "UTF-8 conversion error occured while rendering template: {}", context)
97            }
98            ErrorKind::__Nonexhaustive => write!(f, "Nonexhaustive"),
99        }
100    }
101}
102
103impl StdError for Error {
104    fn source(&self) -> Option<&(dyn StdError + 'static)> {
105        self.source.as_ref().map(|c| &**c as &(dyn StdError + 'static))
106    }
107}
108
109impl Error {
110    /// Creates generic error
111    pub fn msg(value: impl ToString) -> Self {
112        Self { kind: ErrorKind::Msg(value.to_string()), source: None }
113    }
114
115    /// Creates a circular extend error
116    pub fn circular_extend(tpl: impl ToString, inheritance_chain: Vec<String>) -> Self {
117        Self {
118            kind: ErrorKind::CircularExtend { tpl: tpl.to_string(), inheritance_chain },
119            source: None,
120        }
121    }
122
123    /// Creates a missing parent error
124    pub fn missing_parent(current: impl ToString, parent: impl ToString) -> Self {
125        Self {
126            kind: ErrorKind::MissingParent {
127                current: current.to_string(),
128                parent: parent.to_string(),
129            },
130            source: None,
131        }
132    }
133
134    /// Creates a template not found error
135    pub fn template_not_found(tpl: impl ToString) -> Self {
136        Self { kind: ErrorKind::TemplateNotFound(tpl.to_string()), source: None }
137    }
138
139    /// Creates a filter not found error
140    pub fn filter_not_found(name: impl ToString) -> Self {
141        Self { kind: ErrorKind::FilterNotFound(name.to_string()), source: None }
142    }
143
144    /// Creates a test not found error
145    pub fn test_not_found(name: impl ToString) -> Self {
146        Self { kind: ErrorKind::TestNotFound(name.to_string()), source: None }
147    }
148
149    /// Creates a function not found error
150    pub fn function_not_found(name: impl ToString) -> Self {
151        Self { kind: ErrorKind::FunctionNotFound(name.to_string()), source: None }
152    }
153
154    /// Creates generic error with a source
155    pub fn chain(value: impl ToString, source: impl Into<Box<dyn StdError + Send + Sync>>) -> Self {
156        Self { kind: ErrorKind::Msg(value.to_string()), source: Some(source.into()) }
157    }
158
159    /// Creates an error wrapping a failed function call.
160    pub fn call_function(
161        name: impl ToString,
162        source: impl Into<Box<dyn StdError + Send + Sync>>,
163    ) -> Self {
164        Self { kind: ErrorKind::CallFunction(name.to_string()), source: Some(source.into()) }
165    }
166
167    /// Creates an error wrapping a failed filter call.
168    pub fn call_filter(
169        name: impl ToString,
170        source: impl Into<Box<dyn StdError + Send + Sync>>,
171    ) -> Self {
172        Self { kind: ErrorKind::CallFilter(name.to_string()), source: Some(source.into()) }
173    }
174
175    /// Creates an error wrapping a failed test call.
176    pub fn call_test(
177        name: impl ToString,
178        source: impl Into<Box<dyn StdError + Send + Sync>>,
179    ) -> Self {
180        Self { kind: ErrorKind::CallTest(name.to_string()), source: Some(source.into()) }
181    }
182
183    /// Creates JSON error
184    pub fn json(value: serde_json::Error) -> Self {
185        Self { kind: ErrorKind::Json(value), source: None }
186    }
187
188    /// Creates an invalid macro definition error
189    pub fn invalid_macro_def(name: impl ToString) -> Self {
190        Self { kind: ErrorKind::InvalidMacroDefinition(name.to_string()), source: None }
191    }
192
193    /// Creates an IO error
194    pub fn io_error(error: std::io::Error) -> Self {
195        Self { kind: ErrorKind::Io(error.kind()), source: Some(Box::new(error)) }
196    }
197
198    /// Creates an utf8 conversion error
199    pub fn utf8_conversion_error(error: std::string::FromUtf8Error, context: String) -> Self {
200        Self { kind: ErrorKind::Utf8Conversion { context }, source: Some(Box::new(error)) }
201    }
202}
203
204impl From<std::io::Error> for Error {
205    fn from(error: std::io::Error) -> Self {
206        Self::io_error(error)
207    }
208}
209impl From<&str> for Error {
210    fn from(e: &str) -> Self {
211        Self::msg(e)
212    }
213}
214impl From<String> for Error {
215    fn from(e: String) -> Self {
216        Self::msg(e)
217    }
218}
219impl From<serde_json::Error> for Error {
220    fn from(e: serde_json::Error) -> Self {
221        Self::json(e)
222    }
223}
224/// Convenient wrapper around std::Result.
225pub type Result<T> = ::std::result::Result<T, Error>;
226
227#[cfg(test)]
228mod tests {
229    #[test]
230    fn test_error_is_send_and_sync() {
231        fn test_send_sync<T: Send + Sync>() {}
232
233        test_send_sync::<super::Error>();
234    }
235}