n0_error/
ext.rs

1use std::fmt;
2
3use crate::{AnyError, StackError, StackErrorExt, meta, stack_error};
4
5/// Provides extension methods to add context to [`StackError`]s.
6pub trait StackResultExt<T, E> {
7    /// Converts the result's error value to [`AnyError`] while providing additional context.
8    #[track_caller]
9    fn context(self, context: impl fmt::Display) -> Result<T, AnyError>;
10
11    /// Converts the result's error value to [`AnyError`] while providing lazily-evaluated additional context.
12    ///
13    /// The `context` closure is only invoked if an error occurs.
14    #[track_caller]
15    fn with_context<F, C>(self, context: F) -> Result<T, AnyError>
16    where
17        F: FnOnce(&E) -> C,
18        C: fmt::Display;
19}
20
21impl<T, E: StackError + 'static> StackResultExt<T, E> for Result<T, E> {
22    fn context(self, context: impl fmt::Display) -> Result<T, AnyError> {
23        match self {
24            Ok(v) => Ok(v),
25            Err(e) => Err(e.into_any().context(context)),
26        }
27    }
28
29    fn with_context<F, C>(self, context: F) -> Result<T, AnyError>
30    where
31        F: FnOnce(&E) -> C,
32        C: fmt::Display,
33    {
34        match self {
35            Ok(v) => Ok(v),
36            Err(e) => {
37                let context = context(&e);
38                Err(e.into_any().context(context))
39            }
40        }
41    }
42}
43
44/// Provides extension methods to add context to std errors.
45///
46/// You should only call these methods on results that contain errors which do not implement [`StackError`].
47/// For results with `StackError`s, instead use the methods from [`StackResultExt`]. The latter will
48/// preserve call-site metadata, whereas using the methods from the [`StdResultExt`] will lose the
49/// call-site metadata when called on a `StackError` result.
50pub trait StdResultExt<T, E> {
51    /// Converts the result's error value to [`AnyError`] while providing additional context.
52    #[track_caller]
53    fn std_context(self, context: impl fmt::Display) -> Result<T, AnyError>;
54
55    /// Converts the result's error value to [`AnyError`] while providing lazily-evaluated additional context.
56    ///
57    /// The `context` closure is only invoked if an error occurs.
58    #[track_caller]
59    fn with_std_context<F, C>(self, context: F) -> Result<T, AnyError>
60    where
61        F: FnOnce(&E) -> C,
62        C: fmt::Display;
63
64    /// Converts the result's error into [`AnyError`].
65    ///
66    /// You should make sure to only call this on results that contain an error which does *not*
67    /// implement [`StackError`]. If it *does* implement [`StackError`] you can simply convert
68    /// the result's error to [`AnyError`] with the `?` operator. If you use `anyerr` on
69    /// `StackError`s, you will lose access to the error's call-site metadata.
70    #[track_caller]
71    fn anyerr(self) -> Result<T, AnyError>;
72}
73
74impl<T, E: std::error::Error + Send + Sync + 'static> StdResultExt<T, E> for Result<T, E> {
75    fn std_context(self, context: impl fmt::Display) -> Result<T, AnyError> {
76        match self {
77            Ok(v) => Ok(v),
78            Err(e) => Err(AnyError::from_std(e).context(context)),
79        }
80    }
81
82    fn with_std_context<F, C>(self, context: F) -> Result<T, AnyError>
83    where
84        F: FnOnce(&E) -> C,
85        C: fmt::Display,
86    {
87        match self {
88            Ok(v) => Ok(v),
89            Err(e) => {
90                let context = context(&e);
91                Err(AnyError::from_std(e).context(context))
92            }
93        }
94    }
95
96    fn anyerr(self) -> Result<T, AnyError> {
97        match self {
98            Ok(v) => Ok(v),
99            Err(err) => Err(AnyError::from_std(err)),
100        }
101    }
102}
103
104impl<T> StdResultExt<T, NoneError> for Option<T> {
105    fn std_context(self, context: impl fmt::Display) -> Result<T, AnyError> {
106        match self {
107            Some(v) => Ok(v),
108            None => Err(NoneError { meta: meta() }.into_any().context(context)),
109        }
110    }
111
112    fn with_std_context<F, C>(self, context: F) -> Result<T, AnyError>
113    where
114        F: FnOnce(&NoneError) -> C,
115        C: fmt::Display,
116    {
117        match self {
118            Some(v) => Ok(v),
119            None => {
120                let err = NoneError { meta: meta() };
121                let context = context(&err);
122                Err(err.into_any().context(context))
123            }
124        }
125    }
126
127    fn anyerr(self) -> Result<T, AnyError> {
128        match self {
129            Some(v) => Ok(v),
130            None => Err(NoneError { meta: meta() }.into_any()),
131        }
132    }
133}
134
135impl<T> StackResultExt<T, NoneError> for Option<T> {
136    fn context(self, context: impl fmt::Display) -> Result<T, AnyError> {
137        match self {
138            Some(v) => Ok(v),
139            None => Err(NoneError { meta: meta() }.into_any().context(context)),
140        }
141    }
142
143    fn with_context<F, C>(self, context: F) -> Result<T, AnyError>
144    where
145        F: FnOnce(&NoneError) -> C,
146        C: fmt::Display,
147    {
148        match self {
149            Some(v) => Ok(v),
150            None => {
151                let err = NoneError { meta: meta() };
152                let context = context(&err);
153                Err(err.into_any().context(context))
154            }
155        }
156    }
157}
158
159/// Error returned when converting [`Option`]s to an error.
160#[stack_error(derive, add_meta)]
161#[error("Expected some, found none")]
162pub struct NoneError {}
163
164/// A simple string error, providing a message and optionally a source.
165#[stack_error(derive, add_meta)]
166pub(crate) enum FromString {
167    #[error("{message}")]
168    WithSource { message: String, source: AnyError },
169    #[error("{message}")]
170    WithoutSource { message: String },
171}