1 | //! Module containing the error type returned by TinyTemplate if an error occurs.
|
2 |
|
3 | use instruction::{path_to_str, PathSlice};
|
4 | use serde_json::Error as SerdeJsonError;
|
5 | use serde_json::Value;
|
6 | use std::error::Error as StdError;
|
7 | use std::fmt;
|
8 |
|
9 | /// Enum representing the potential errors that TinyTemplate can encounter.
|
10 | #[derive(Debug)]
|
11 | pub enum Error {
|
12 | ParseError {
|
13 | msg: String,
|
14 | line: usize,
|
15 | column: usize,
|
16 | },
|
17 | RenderError {
|
18 | msg: String,
|
19 | line: usize,
|
20 | column: usize,
|
21 | },
|
22 | SerdeError {
|
23 | err: SerdeJsonError,
|
24 | },
|
25 | GenericError {
|
26 | msg: String,
|
27 | },
|
28 | StdFormatError {
|
29 | err: fmt::Error,
|
30 | },
|
31 | CalledTemplateError {
|
32 | name: String,
|
33 | err: Box<Error>,
|
34 | line: usize,
|
35 | column: usize,
|
36 | },
|
37 | CalledFormatterError {
|
38 | name: String,
|
39 | err: Box<Error>,
|
40 | line: usize,
|
41 | column: usize,
|
42 | },
|
43 |
|
44 | #[doc (hidden)]
|
45 | __NonExhaustive,
|
46 | }
|
47 | impl From<SerdeJsonError> for Error {
|
48 | fn from(err: SerdeJsonError) -> Error {
|
49 | Error::SerdeError { err }
|
50 | }
|
51 | }
|
52 | impl From<fmt::Error> for Error {
|
53 | fn from(err: fmt::Error) -> Error {
|
54 | Error::StdFormatError { err }
|
55 | }
|
56 | }
|
57 | impl fmt::Display for Error {
|
58 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
59 | match self {
|
60 | Error::ParseError { msg, line, column } => write!(
|
61 | f,
|
62 | "Failed to parse the template (line {}, column {}). Reason: {}" ,
|
63 | line, column, msg
|
64 | ),
|
65 | Error::RenderError { msg, line, column } => {
|
66 | write!(
|
67 | f,
|
68 | "Encountered rendering error on line {}, column {}. Reason: {}" ,
|
69 | line, column, msg
|
70 | )
|
71 | }
|
72 | Error::SerdeError { err } => {
|
73 | write!(f, "Unexpected serde error while converting the context to a serde_json::Value. Error: {}" , err)
|
74 | }
|
75 | Error::GenericError { msg } => {
|
76 | write!(f, "{}" , msg)
|
77 | }
|
78 | Error::StdFormatError { err } => {
|
79 | write!(f, "Unexpected formatting error: {}" , err)
|
80 | }
|
81 | Error::CalledTemplateError {
|
82 | name,
|
83 | err,
|
84 | line,
|
85 | column,
|
86 | } => {
|
87 | write!(
|
88 | f,
|
89 | "Call to sub-template \"{} \" on line {}, column {} failed. Reason: {}" ,
|
90 | name, line, column, err
|
91 | )
|
92 | }
|
93 | Error::CalledFormatterError {
|
94 | name,
|
95 | err,
|
96 | line,
|
97 | column,
|
98 | } => {
|
99 | write!(
|
100 | f,
|
101 | "Call to value formatter \"{} \" on line {}, column {} failed. Reason: {}" ,
|
102 | name, line, column, err
|
103 | )
|
104 | }
|
105 | Error::__NonExhaustive => unreachable!(),
|
106 | }
|
107 | }
|
108 | }
|
109 | impl StdError for Error {
|
110 | fn description(&self) -> &str {
|
111 | match self {
|
112 | Error::ParseError { .. } => "ParseError" ,
|
113 | Error::RenderError { .. } => "RenderError" ,
|
114 | Error::SerdeError { .. } => "SerdeError" ,
|
115 | Error::GenericError { msg } => &msg,
|
116 | Error::StdFormatError { .. } => "StdFormatError" ,
|
117 | Error::CalledTemplateError { .. } => "CalledTemplateError" ,
|
118 | Error::CalledFormatterError { .. } => "CalledFormatterError" ,
|
119 | Error::__NonExhaustive => unreachable!(),
|
120 | }
|
121 | }
|
122 | }
|
123 |
|
124 | pub type Result<T> = ::std::result::Result<T, Error>;
|
125 |
|
126 | pub(crate) fn lookup_error(source: &str, step: &str, path: PathSlice, current: &Value) -> Error {
|
127 | let avail_str = if let Value::Object(object_map) = current {
|
128 | let mut avail_str = " Available values at this level are " .to_string();
|
129 | for (i, key) in object_map.keys().enumerate() {
|
130 | if i > 0 {
|
131 | avail_str.push_str(", " );
|
132 | }
|
133 | avail_str.push(' \'' );
|
134 | avail_str.push_str(key);
|
135 | avail_str.push(' \'' );
|
136 | }
|
137 | avail_str
|
138 | } else {
|
139 | "" .to_string()
|
140 | };
|
141 |
|
142 | let (line, column) = get_offset(source, step);
|
143 |
|
144 | Error::RenderError {
|
145 | msg: format!(
|
146 | "Failed to find value '{}' from path '{}'.{}" ,
|
147 | step,
|
148 | path_to_str(path),
|
149 | avail_str
|
150 | ),
|
151 | line,
|
152 | column,
|
153 | }
|
154 | }
|
155 |
|
156 | pub(crate) fn truthiness_error(source: &str, path: PathSlice) -> Error {
|
157 | let (line, column) = get_offset(source, path.last().unwrap());
|
158 | Error::RenderError {
|
159 | msg: format!(
|
160 | "Path '{}' produced a value which could not be checked for truthiness." ,
|
161 | path_to_str(path)
|
162 | ),
|
163 | line,
|
164 | column,
|
165 | }
|
166 | }
|
167 |
|
168 | pub(crate) fn unprintable_error() -> Error {
|
169 | Error::GenericError {
|
170 | msg: "Expected a printable value but found array or object." .to_string(),
|
171 | }
|
172 | }
|
173 |
|
174 | pub(crate) fn not_iterable_error(source: &str, path: PathSlice) -> Error {
|
175 | let (line, column) = get_offset(source, path.last().unwrap());
|
176 | Error::RenderError {
|
177 | msg: format!(
|
178 | "Expected an array for path '{}' but found a non-iterable value." ,
|
179 | path_to_str(path)
|
180 | ),
|
181 | line,
|
182 | column,
|
183 | }
|
184 | }
|
185 |
|
186 | pub(crate) fn unknown_template(source: &str, name: &str) -> Error {
|
187 | let (line, column) = get_offset(source, name);
|
188 | Error::RenderError {
|
189 | msg: format!("Tried to call an unknown template '{}'" , name),
|
190 | line,
|
191 | column,
|
192 | }
|
193 | }
|
194 |
|
195 | pub(crate) fn unknown_formatter(source: &str, name: &str) -> Error {
|
196 | let (line, column) = get_offset(source, name);
|
197 | Error::RenderError {
|
198 | msg: format!("Tried to call an unknown formatter '{}'" , name),
|
199 | line,
|
200 | column,
|
201 | }
|
202 | }
|
203 |
|
204 | pub(crate) fn called_template_error(source: &str, template_name: &str, err: Error) -> Error {
|
205 | let (line, column) = get_offset(source, template_name);
|
206 | Error::CalledTemplateError {
|
207 | name: template_name.to_string(),
|
208 | err: Box::new(err),
|
209 | line,
|
210 | column,
|
211 | }
|
212 | }
|
213 |
|
214 | pub(crate) fn called_formatter_error(source: &str, formatter_name: &str, err: Error) -> Error {
|
215 | let (line, column) = get_offset(source, formatter_name);
|
216 | Error::CalledFormatterError {
|
217 | name: formatter_name.to_string(),
|
218 | err: Box::new(err),
|
219 | line,
|
220 | column,
|
221 | }
|
222 | }
|
223 |
|
224 | /// Find the line number and column of the target string within the source string. Will panic if
|
225 | /// target is not a substring of source.
|
226 | pub(crate) fn get_offset(source: &str, target: &str) -> (usize, usize) {
|
227 | let offset = target.as_ptr() as isize - source.as_ptr() as isize;
|
228 | let to_scan = &source[0..(offset as usize)];
|
229 |
|
230 | let mut line = 1;
|
231 | let mut column = 0;
|
232 |
|
233 | for byte in to_scan.bytes() {
|
234 | match byte as char {
|
235 | ' \n' => {
|
236 | line += 1;
|
237 | column = 0;
|
238 | }
|
239 | _ => {
|
240 | column += 1;
|
241 | }
|
242 | }
|
243 | }
|
244 |
|
245 | (line, column)
|
246 | }
|
247 | |