liquid_error/result_ext.rs
1use std::borrow;
2use std::error;
3use std::result;
4
5use super::CloneableError;
6use super::Error;
7use super::ErrorClone;
8use super::Result;
9
10type CowStr = borrow::Cow<'static, str>;
11
12/// `Result` extension methods for adapting third party errors to `Error`.
13pub trait ResultLiquidChainExt<T> {
14 /// Create an `Error` with `E` as the cause.
15 #[must_use]
16 fn chain<S: Into<CowStr>>(self, msg: S) -> Result<T>;
17
18 /// Create an `Error` with `E` as the cause.
19 #[must_use]
20 fn chain_with<F>(self, msg: F) -> Result<T>
21 where
22 F: FnOnce() -> CowStr;
23}
24
25/// `Result` extension methods for adapting third party errors to `Error`.
26pub trait ResultLiquidReplaceExt<T> {
27 /// Create an `Error` ignoring `E` as the cause.
28 ///
29 /// # Example
30 ///
31 /// ```rust
32 /// use std::io;
33 /// use liquid_error::Result;
34 /// use liquid_error::ResultLiquidReplaceExt;
35 ///
36 /// let error = Err(io::Error::new(io::ErrorKind::NotFound, "Oops"));
37 /// let error: Result<i32> = error.lossy_chain("Missing liquid partial");
38 /// ```
39 #[must_use]
40 fn lossy_chain<S: Into<CowStr>>(self, msg: S) -> Result<T>;
41
42 /// Create an `Error` ignoring `E` as the cause.
43 ///
44 /// # Example
45 ///
46 /// ```rust
47 /// use std::io;
48 /// use liquid_error::Result;
49 /// use liquid_error::ResultLiquidReplaceExt;
50 ///
51 /// let filename = "foo";
52 /// let error = Err(io::Error::new(io::ErrorKind::NotFound, "Oops"));
53 /// let error: Result<i32> = error
54 /// .lossy_chain_with(|| format!("Missing liquid partial: {}", filename).into());
55 /// ```
56 #[must_use]
57 fn lossy_chain_with<F>(self, msg: F) -> Result<T>
58 where
59 F: FnOnce() -> CowStr;
60
61 /// Create an `Error` ignoring `E` as the cause.
62 ///
63 /// # Example
64 ///
65 /// ```rust
66 /// use std::io;
67 /// use liquid_error::Result;
68 /// use liquid_error::ResultLiquidReplaceExt;
69 ///
70 /// let error = Err(io::Error::new(io::ErrorKind::NotFound, "Oops"));
71 /// let error: Result<i32> = error.replace("Missing liquid partial");
72 /// ```
73 #[must_use]
74 fn replace<S: Into<CowStr>>(self, msg: S) -> Result<T>;
75
76 /// Create an `Error` ignoring `E` as the cause.
77 ///
78 /// # Example
79 ///
80 /// ```rust
81 /// use std::io;
82 /// use liquid_error::Result;
83 /// use liquid_error::ResultLiquidReplaceExt;
84 ///
85 /// let filename = "foo";
86 /// let error = Err(io::Error::new(io::ErrorKind::NotFound, "Oops"));
87 /// let error: Result<i32> = error
88 /// .replace_with(|| format!("Missing liquid partial: {}", filename).into());
89 /// ```
90 #[must_use]
91 fn replace_with<F>(self, msg: F) -> Result<T>
92 where
93 F: FnOnce() -> CowStr;
94}
95
96impl<T, E> ResultLiquidChainExt<T> for result::Result<T, E>
97where
98 E: ErrorClone,
99{
100 fn chain<S: Into<CowStr>>(self, msg: S) -> Result<T> {
101 self.map_err(|err| Error::with_msg(msg).cause(err))
102 }
103
104 fn chain_with<F>(self, msg: F) -> Result<T>
105 where
106 F: FnOnce() -> CowStr,
107 {
108 self.map_err(|err| Error::with_msg(msg()).cause(err))
109 }
110}
111
112impl<T, E> ResultLiquidReplaceExt<T> for result::Result<T, E>
113where
114 E: error::Error + Send + Sync + 'static,
115{
116 fn lossy_chain<S: Into<CowStr>>(self, msg: S) -> Result<T> {
117 self.map_err(|err| Error::with_msg(msg).cause(CloneableError::new(err)))
118 }
119
120 fn lossy_chain_with<F>(self, msg: F) -> Result<T>
121 where
122 F: FnOnce() -> CowStr,
123 {
124 self.map_err(|err| Error::with_msg(msg()).cause(CloneableError::new(err)))
125 }
126
127 fn replace<S: Into<CowStr>>(self, msg: S) -> Result<T> {
128 self.map_err(|_| Error::with_msg(msg))
129 }
130
131 fn replace_with<F>(self, msg: F) -> Result<T>
132 where
133 F: FnOnce() -> CowStr,
134 {
135 self.map_err(|_| Error::with_msg(msg()))
136 }
137}
138
139/// Add context to a `liquid_error::Error`.
140pub trait ResultLiquidExt<T>
141where
142 Self: ::std::marker::Sized,
143{
144 /// Add a new stack frame to the `liquid_error::Error`.
145 ///
146 /// # Example
147 ///
148 /// ```rust
149 /// use liquid_error::Error;
150 /// use liquid_error::Result;
151 /// use liquid_error::ResultLiquidExt;
152 ///
153 /// let error: Result<i32> = Err(Error::with_msg("Oops"));
154 /// let error = error.trace("Within forloop");
155 /// ```
156 #[must_use]
157 fn trace<S>(self, trace: S) -> Result<T>
158 where
159 S: Into<CowStr>;
160
161 /// Add a new stack frame to the `liquid_error::Error`.
162 ///
163 /// # Example
164 ///
165 /// ```rust
166 /// use liquid_error::Error;
167 /// use liquid_error::Result;
168 /// use liquid_error::ResultLiquidExt;
169 ///
170 /// let for_var = "foo";
171 /// let error: Result<i32> = Err(Error::with_msg("Oops"));
172 /// let error = error.trace_with(|| format!("Within forloop with {}", for_var).into());
173 /// ```
174 #[must_use]
175 fn trace_with<F>(self, trace: F) -> Result<T>
176 where
177 F: FnOnce() -> CowStr;
178
179 /// Add state the current stack frame.
180 ///
181 /// # Example
182 ///
183 /// ```rust
184 /// use liquid_error::Error;
185 /// use liquid_error::Result;
186 /// use liquid_error::ResultLiquidExt;
187 ///
188 /// let for_var = "foo";
189 /// let error: Result<i32> = Err(Error::with_msg("Oops"));
190 /// let error = error
191 /// .context_key("foo")
192 /// .value("10");
193 /// let error = error
194 /// .context_key("foo")
195 /// .value_with(|| format!("{}", for_var).into());
196 /// ```
197 #[must_use]
198 fn context_key<S>(self, key: S) -> Key<T>
199 where
200 S: Into<CowStr>;
201
202 /// Add state the current stack frame.
203 ///
204 /// # Example
205 ///
206 /// ```rust
207 /// use liquid_error::Error;
208 /// use liquid_error::Result;
209 /// use liquid_error::ResultLiquidExt;
210 ///
211 /// let for_var = "foo";
212 /// let error: Result<i32> = Err(Error::with_msg("Oops"));
213 /// let error = error
214 /// .context_key_with(|| format!("{}", 10).into())
215 /// .value("10");
216 /// let error = error
217 /// .context_key_with(|| format!("{}", 10).into())
218 /// .value_with(|| format!("{}", for_var).into());
219 /// ```
220 #[must_use]
221 fn context_key_with<F>(self, key: F) -> FnKey<T, F>
222 where
223 F: FnOnce() -> CowStr;
224}
225
226impl<T> ResultLiquidExt<T> for Result<T> {
227 fn trace<S>(self, trace: S) -> Result<T>
228 where
229 S: Into<CowStr>,
230 {
231 self.map_err(|err| err.trace(trace))
232 }
233
234 fn trace_with<F>(self, trace: F) -> Result<T>
235 where
236 F: FnOnce() -> CowStr,
237 {
238 self.map_err(|err| err.trace(trace()))
239 }
240
241 fn context_key<S>(self, key: S) -> Key<T>
242 where
243 S: Into<CowStr>,
244 {
245 Key::new(self, key)
246 }
247
248 fn context_key_with<F>(self, key: F) -> FnKey<T, F>
249 where
250 F: FnOnce() -> CowStr,
251 {
252 FnKey::new(self, key)
253 }
254}
255
256/// Partially constructed context (missing value) for `Result<T>`.
257#[allow(missing_debug_implementations)]
258pub struct Key<T> {
259 builder: Result<T>,
260 key: CowStr,
261}
262
263impl<T> Key<T> {
264 /// Save off a key for a context that will be added to `builder`.
265 #[must_use]
266 pub fn new<S>(builder: Result<T>, key: S) -> Self
267 where
268 S: Into<CowStr>,
269 {
270 Self {
271 builder,
272 key: key.into(),
273 }
274 }
275
276 /// Finish creating context and add it to `Result<T>`.
277 #[must_use]
278 pub fn value<S>(self, value: S) -> Result<T>
279 where
280 S: Into<CowStr>,
281 {
282 let builder = self.builder;
283 let key = self.key;
284 builder.map_err(|err| err.context(key, value.into()))
285 }
286
287 /// Finish creating context and add it to `Result<T>`.
288 #[must_use]
289 pub fn value_with<F>(self, value: F) -> Result<T>
290 where
291 F: FnOnce() -> CowStr,
292 {
293 let builder = self.builder;
294 let key = self.key;
295 builder.map_err(|err| err.context(key, value()))
296 }
297}
298
299/// Partially constructed context (missing value) for `Result<T>`.
300#[allow(missing_debug_implementations)]
301pub struct FnKey<T, F>
302where
303 F: FnOnce() -> CowStr,
304{
305 builder: Result<T>,
306 key: F,
307}
308
309impl<T, F> FnKey<T, F>
310where
311 F: FnOnce() -> CowStr,
312{
313 /// Save off a key for a context that will be added to `builder`.
314 #[must_use]
315 pub fn new(builder: Result<T>, key: F) -> Self {
316 Self { builder, key }
317 }
318
319 /// Finish creating context and add it to `Result<T>`.
320 #[must_use]
321 pub fn value<S>(self, value: S) -> Result<T>
322 where
323 S: Into<CowStr>,
324 {
325 let builder = self.builder;
326 let key = self.key;
327 builder.map_err(|err| err.context((key)(), value.into()))
328 }
329
330 /// Finish creating context and add it to `Result<T>`.
331 #[must_use]
332 pub fn value_with<V>(self, value: V) -> Result<T>
333 where
334 V: FnOnce() -> CowStr,
335 {
336 let builder = self.builder;
337 let key = self.key;
338 builder.map_err(|err| err.context((key)(), value()))
339 }
340}