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}