1#[cfg(feature = "alloc")]
2use alloc::boxed::Box;
3use core::convert::Infallible;
4use core::error::Error as StdError;
5use core::fmt;
6use core::marker::PhantomData;
7#[cfg(feature = "std")]
8use std::io;
9
10pub type Result<I, E = Error> = core::result::Result<I, E>;
12
13#[non_exhaustive]
18#[derive(Debug)]
19pub enum Error {
20 Fmt,
22 ValueMissing,
24 ValueType,
26 #[cfg(feature = "alloc")]
28 Custom(Box<dyn StdError + Send + Sync>),
29 #[cfg(feature = "serde_json")]
31 Json(serde_json::Error),
32}
33
34impl Error {
35 #[inline]
37 #[cfg(feature = "alloc")]
38 pub fn custom(err: impl Into<Box<dyn StdError + Send + Sync>>) -> Self {
39 Self::Custom(err.into())
40 }
41
42 #[cfg(feature = "alloc")]
45 pub fn into_box(self) -> Box<dyn StdError + Send + Sync> {
46 match self {
47 Error::Fmt => fmt::Error.into(),
48 Error::ValueMissing => Box::new(Error::ValueMissing),
49 Error::ValueType => Box::new(Error::ValueType),
50 Error::Custom(err) => err,
51 #[cfg(feature = "serde_json")]
52 Error::Json(err) => err.into(),
53 }
54 }
55
56 #[cfg(feature = "std")]
60 pub fn into_io_error(self) -> io::Error {
61 io::Error::other(match self {
62 Error::Custom(err) => match err.downcast() {
63 Ok(err) => return *err,
64 Err(err) => err,
65 },
66 err => err.into_box(),
67 })
68 }
69}
70
71impl StdError for Error {
72 fn source(&self) -> Option<&(dyn StdError + 'static)> {
73 match self {
74 Error::Fmt => Some(&fmt::Error),
75 Error::ValueMissing => None,
76 Error::ValueType => None,
77 #[cfg(feature = "alloc")]
78 Error::Custom(err) => Some(err.as_ref()),
79 #[cfg(feature = "serde_json")]
80 Error::Json(err) => Some(err),
81 }
82 }
83}
84
85impl fmt::Display for Error {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 match self {
88 Error::Fmt => fmt::Error.fmt(f),
89 Error::ValueMissing => f.write_str("key missing in values"),
90 Error::ValueType => f.write_str("value has wrong type"),
91 #[cfg(feature = "alloc")]
92 Error::Custom(err) => err.fmt(f),
93 #[cfg(feature = "serde_json")]
94 Error::Json(err) => err.fmt(f),
95 }
96 }
97}
98
99impl From<Error> for fmt::Error {
100 #[inline]
101 fn from(_: Error) -> Self {
102 Self
103 }
104}
105
106#[cfg(feature = "std")]
107impl From<Error> for io::Error {
108 #[inline]
109 fn from(err: Error) -> Self {
110 err.into_io_error()
111 }
112}
113
114impl From<fmt::Error> for Error {
115 #[inline]
116 fn from(_: fmt::Error) -> Self {
117 Error::Fmt
118 }
119}
120
121#[cfg(feature = "alloc")]
123impl From<Box<dyn StdError + Send + Sync>> for Error {
124 #[inline]
125 fn from(err: Box<dyn StdError + Send + Sync>) -> Self {
126 error_from_stderror(err, MAX_ERROR_UNWRAP_COUNT)
127 }
128}
129
130#[cfg(feature = "std")]
132impl From<io::Error> for Error {
133 #[inline]
134 fn from(err: io::Error) -> Self {
135 error_from_io_error(err, MAX_ERROR_UNWRAP_COUNT)
136 }
137}
138
139#[cfg(feature = "alloc")]
140const MAX_ERROR_UNWRAP_COUNT: usize = 5;
141
142#[cfg(feature = "alloc")]
143fn error_from_stderror(err: Box<dyn StdError + Send + Sync>, unwraps: usize) -> Error {
144 let Some(unwraps) = unwraps.checked_sub(1) else {
145 return Error::Custom(err);
146 };
147 #[cfg(not(feature = "std"))]
148 let _ = unwraps;
149 match ErrorKind::inspect(err.as_ref()) {
150 ErrorKind::Fmt => Error::Fmt,
151 ErrorKind::Custom => Error::Custom(err),
152 #[cfg(feature = "serde_json")]
153 ErrorKind::Json => match err.downcast() {
154 Ok(err) => Error::Json(*err),
155 Err(_) => Error::Fmt, },
157 #[cfg(feature = "std")]
158 ErrorKind::Io => match err.downcast() {
159 Ok(err) => error_from_io_error(*err, unwraps),
160 Err(_) => Error::Fmt, },
162 ErrorKind::Askama => match err.downcast() {
163 Ok(err) => *err,
164 Err(_) => Error::Fmt, },
166 }
167}
168
169#[cfg(feature = "std")]
170fn error_from_io_error(err: io::Error, unwraps: usize) -> Error {
171 let Some(inner) = err.get_ref() else {
172 return Error::custom(err);
173 };
174 let Some(unwraps) = unwraps.checked_sub(1) else {
175 return match err.into_inner() {
176 Some(err) => Error::Custom(err),
177 None => Error::Fmt, };
179 };
180 match ErrorKind::inspect(inner) {
181 ErrorKind::Fmt => Error::Fmt,
182 ErrorKind::Askama => match err.downcast() {
183 Ok(err) => err,
184 Err(_) => Error::Fmt, },
186 #[cfg(feature = "serde_json")]
187 ErrorKind::Json => match err.downcast() {
188 Ok(err) => Error::Json(err),
189 Err(_) => Error::Fmt, },
191 ErrorKind::Custom => match err.into_inner() {
192 Some(err) => Error::Custom(err),
193 None => Error::Fmt, },
195 ErrorKind::Io => match err.downcast() {
196 Ok(inner) => error_from_io_error(inner, unwraps),
197 Err(_) => Error::Fmt, },
199 }
200}
201
202#[cfg(feature = "alloc")]
203enum ErrorKind {
204 Fmt,
205 Custom,
206 #[cfg(feature = "serde_json")]
207 Json,
208 #[cfg(feature = "std")]
209 Io,
210 Askama,
211}
212
213#[cfg(feature = "alloc")]
214impl ErrorKind {
215 fn inspect(err: &(dyn StdError + 'static)) -> ErrorKind {
216 if err.is::<fmt::Error>() {
217 return ErrorKind::Fmt;
218 }
219
220 #[cfg(feature = "std")]
221 if err.is::<io::Error>() {
222 return ErrorKind::Io;
223 }
224
225 if err.is::<Error>() {
226 return ErrorKind::Askama;
227 }
228
229 #[cfg(feature = "serde_json")]
230 if err.is::<serde_json::Error>() {
231 return ErrorKind::Json;
232 }
233
234 ErrorKind::Custom
235 }
236}
237
238#[cfg(feature = "serde_json")]
239impl From<serde_json::Error> for Error {
240 #[inline]
241 fn from(err: serde_json::Error) -> Self {
242 Error::Json(err)
243 }
244}
245
246impl From<Infallible> for Error {
247 #[inline]
248 fn from(value: Infallible) -> Self {
249 match value {}
250 }
251}
252
253#[cfg(test)]
254const _: () = {
255 trait AssertSendSyncStatic: Send + Sync + 'static {}
256 impl AssertSendSyncStatic for Error {}
257};
258
259pub trait ResultConverter {
261 type Value;
263 type Input;
265
266 fn askama_conv_result(self, result: Self::Input) -> Result<Self::Value, Error>;
268}
269
270#[derive(Debug, Clone, Copy)]
272pub struct ErrorMarker<T>(PhantomData<Result<T>>);
273
274impl<T> ErrorMarker<T> {
275 #[inline]
277 pub fn of(_: &T) -> Self {
278 Self(PhantomData)
279 }
280}
281
282#[cfg(feature = "alloc")]
283impl<T, E> ResultConverter for &ErrorMarker<Result<T, E>>
284where
285 E: Into<Box<dyn StdError + Send + Sync>>,
286{
287 type Value = T;
288 type Input = Result<T, E>;
289
290 #[inline]
291 fn askama_conv_result(self, result: Self::Input) -> Result<Self::Value, Error> {
292 result.map_err(Error::custom)
293 }
294}
295
296impl<T, E> ResultConverter for &&ErrorMarker<Result<T, E>>
297where
298 E: Into<Error>,
299{
300 type Value = T;
301 type Input = Result<T, E>;
302
303 #[inline]
304 fn askama_conv_result(self, result: Self::Input) -> Result<Self::Value, Error> {
305 result.map_err(Into::into)
306 }
307}