1use instruction::{path_to_str, PathSlice};
4use serde_json::Error as SerdeJsonError;
5use serde_json::Value;
6use std::error::Error as StdError;
7use std::fmt;
8
9#[derive(Debug)]
11pub 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}
47impl From<SerdeJsonError> for Error {
48 fn from(err: SerdeJsonError) -> Error {
49 Error::SerdeError { err }
50 }
51}
52impl From<fmt::Error> for Error {
53 fn from(err: fmt::Error) -> Error {
54 Error::StdFormatError { err }
55 }
56}
57impl fmt::Display for Error {
58 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59 match self {
60 Error::ParseError { msg, line, column } => write!(f, "Failed to parse the template (line {}, column {}). Reason: {}", line, column, msg),
61 Error::RenderError { msg, line, column } => {
62 write!(f, "Encountered rendering error on line {}, column {}. Reason: {}", line, column, msg)
63 }
64 Error::SerdeError{ err } => {
65 write!(f, "Unexpected serde error while converting the context to a serde_json::Value. Error: {}", err)
66 }
67 Error::GenericError { msg } => {
68 write!(f, "{}", msg)
69 }
70 Error::StdFormatError{ err } => {
71 write!(f, "Unexpected formatting error: {}", err )
72 }
73 Error::CalledTemplateError{ name, err, line, column } => {
74 write!(f, "Call to sub-template \"{}\" on line {}, column {} failed. Reason: {}", name, line, column, err)
75 }
76 Error::CalledFormatterError{ name, err, line, column } => {
77 write!(f, "Call to value formatter \"{}\" on line {}, column {} failed. Reason: {}", name, line, column, err)
78 }
79 Error::__NonExhaustive => unreachable!(),
80 }
81 }
82}
83impl StdError for Error {
84 fn description(&self) -> &str {
85 match self {
86 Error::ParseError { .. } => "ParseError",
87 Error::RenderError { .. } => "RenderError",
88 Error::SerdeError { .. } => "SerdeError",
89 Error::GenericError { msg } => &msg,
90 Error::StdFormatError { .. } => "StdFormatError",
91 Error::CalledTemplateError { .. } => "CalledTemplateError",
92 Error::CalledFormatterError { .. } => "CalledFormatterError",
93 Error::__NonExhaustive => unreachable!(),
94 }
95 }
96}
97
98pub type Result<T> = ::std::result::Result<T, Error>;
99
100pub(crate) fn lookup_error(source: &str, step: &str, path: PathSlice, current: &Value) -> Error {
101 let avail_str = if let Value::Object(object_map) = current {
102 let mut avail_str = " Available values at this level are ".to_string();
103 for (i, key) in object_map.keys().enumerate() {
104 if i > 0 {
105 avail_str.push_str(", ");
106 }
107 avail_str.push('\'');
108 avail_str.push_str(key);
109 avail_str.push('\'');
110 }
111 avail_str
112 } else {
113 "".to_string()
114 };
115
116 let (line, column) = get_offset(source, step);
117
118 Error::RenderError {
119 msg: format!(
120 "Failed to find value '{}' from path '{}'.{}",
121 step,
122 path_to_str(path),
123 avail_str
124 ),
125 line,
126 column,
127 }
128}
129
130pub(crate) fn truthiness_error(source: &str, path: PathSlice) -> Error {
131 let (line, column) = get_offset(source, path.last().unwrap());
132 Error::RenderError {
133 msg: format!(
134 "Path '{}' produced a value which could not be checked for truthiness.",
135 path_to_str(path)
136 ),
137 line,
138 column,
139 }
140}
141
142pub(crate) fn unprintable_error() -> Error {
143 Error::GenericError {
144 msg: "Expected a printable value but found array or object.".to_string(),
145 }
146}
147
148pub(crate) fn not_iterable_error(source: &str, path: PathSlice) -> Error {
149 let (line, column) = get_offset(source, path.last().unwrap());
150 Error::RenderError {
151 msg: format!(
152 "Expected an array for path '{}' but found a non-iterable value.",
153 path_to_str(path)
154 ),
155 line,
156 column,
157 }
158}
159
160pub(crate) fn unknown_template(source: &str, name: &str) -> Error {
161 let (line, column) = get_offset(source, name);
162 Error::RenderError {
163 msg: format!("Tried to call an unknown template '{}'", name),
164 line,
165 column,
166 }
167}
168
169pub(crate) fn unknown_formatter(source: &str, name: &str) -> Error {
170 let (line, column) = get_offset(source, name);
171 Error::RenderError {
172 msg: format!("Tried to call an unknown formatter '{}'", name),
173 line,
174 column,
175 }
176}
177
178pub(crate) fn called_template_error(source: &str, template_name: &str, err: Error) -> Error {
179 let (line, column) = get_offset(source, template_name);
180 Error::CalledTemplateError {
181 name: template_name.to_string(),
182 err: Box::new(err),
183 line,
184 column,
185 }
186}
187
188pub(crate) fn called_formatter_error(source: &str, formatter_name: &str, err: Error) -> Error {
189 let (line, column) = get_offset(source, formatter_name);
190 Error::CalledFormatterError {
191 name: formatter_name.to_string(),
192 err: Box::new(err),
193 line,
194 column,
195 }
196}
197
198pub(crate) fn get_offset(source: &str, target: &str) -> (usize, usize) {
201 let offset = target.as_ptr() as isize - source.as_ptr() as isize;
202 let to_scan = &source[0..(offset as usize)];
203
204 let mut line = 1;
205 let mut column = 0;
206
207 for byte in to_scan.bytes() {
208 match byte as char {
209 '\n' => {
210 line += 1;
211 column = 0;
212 }
213 _ => {
214 column += 1;
215 }
216 }
217 }
218
219 (line, column)
220}