1use crate::types::alloc_type::{Box, Cow, String};
6use crate::types::{ComposableError, ErrorContext, ErrorVec, LazyContext};
7use core::time::Duration;
8
9#[diagnostic::on_unimplemented(
15 message = "`{Self}` cannot be used as error context",
16 label = "this type does not implement `IntoErrorContext`",
17 note = "implement `IntoErrorContext` manually or use `impl_error_context!({Self})` macro",
18 note = "see: https://docs.rs/error-rail/latest/error_rail/macro.impl_error_context.html"
19)]
20pub trait IntoErrorContext {
21 fn into_error_context(self) -> ErrorContext;
23}
24
25impl IntoErrorContext for String {
26 #[inline]
27 fn into_error_context(self) -> ErrorContext {
28 ErrorContext::new(self)
29 }
30}
31
32impl IntoErrorContext for &'static str {
33 #[inline]
34 fn into_error_context(self) -> ErrorContext {
35 ErrorContext::new(self)
36 }
37}
38
39impl IntoErrorContext for Cow<'static, str> {
40 #[inline]
41 fn into_error_context(self) -> ErrorContext {
42 ErrorContext::new(self)
43 }
44}
45
46impl IntoErrorContext for ErrorContext {
47 #[inline]
48 fn into_error_context(self) -> ErrorContext {
49 self
50 }
51}
52
53pub trait TransientError {
59 fn is_transient(&self) -> bool;
61
62 #[inline]
64 fn is_permanent(&self) -> bool {
65 !self.is_transient()
66 }
67
68 #[inline]
70 fn retry_after_hint(&self) -> Option<Duration> {
71 None
72 }
73
74 #[inline]
76 fn max_retries_hint(&self) -> Option<u32> {
77 None
78 }
79}
80
81#[cfg(feature = "std")]
82impl TransientError for std::io::Error {
83 fn is_transient(&self) -> bool {
84 use std::io::ErrorKind;
85 matches!(
86 self.kind(),
87 ErrorKind::ConnectionRefused
88 | ErrorKind::ConnectionReset
89 | ErrorKind::ConnectionAborted
90 | ErrorKind::TimedOut
91 | ErrorKind::Interrupted
92 | ErrorKind::WouldBlock
93 )
94 }
95}
96
97pub trait TransientErrorExt<T, E: TransientError> {
99 fn retry_if_transient(self) -> Option<Result<T, E>>;
101}
102
103impl<T, E: TransientError> TransientErrorExt<T, E> for Result<T, E> {
104 fn retry_if_transient(self) -> Option<Result<T, E>> {
105 match &self {
106 Ok(_) => None,
107 Err(e) if e.is_transient() => Some(self),
108 Err(_) => None,
109 }
110 }
111}
112
113pub trait WithError<E> {
119 type Success;
120 type ErrorOutput<G>;
121
122 fn fmap_error<F, G>(self, f: F) -> Self::ErrorOutput<G>
124 where
125 F: Fn(E) -> G;
126
127 fn to_result_first(self) -> Result<Self::Success, E>;
129
130 fn to_result_all(self) -> Result<Self::Success, ErrorVec<E>>;
132}
133
134impl<T, E> WithError<E> for Result<T, E> {
135 type Success = T;
136 type ErrorOutput<G> = Result<T, G>;
137
138 fn fmap_error<F, G>(self, f: F) -> Self::ErrorOutput<G>
139 where
140 F: FnOnce(E) -> G,
141 {
142 match self {
143 Ok(t) => Ok(t),
144 Err(e) => Err(f(e)),
145 }
146 }
147
148 fn to_result_first(self) -> Result<Self::Success, E> {
149 self
150 }
151
152 fn to_result_all(self) -> Result<Self::Success, ErrorVec<E>> {
153 match self {
154 Ok(t) => Ok(t),
155 Err(e) => {
156 let mut error_vec = ErrorVec::new();
157 error_vec.push(e);
158 Err(error_vec)
159 }
160 }
161 }
162}
163
164pub trait ErrorOps<E>: WithError<E> {
170 fn recover<F>(self, recovery: F) -> Self
172 where
173 F: FnOnce(E) -> Self,
174 Self: Sized;
175
176 fn bimap_result<B, F, SuccessF, ErrorF>(
178 self,
179 success_f: SuccessF,
180 error_f: ErrorF,
181 ) -> Result<B, F>
182 where
183 SuccessF: FnOnce(Self::Success) -> B,
184 ErrorF: FnOnce(E) -> F,
185 Self: Sized;
186}
187
188impl<T, E> ErrorOps<E> for Result<T, E> {
189 #[inline]
190 fn recover<F>(self, recovery: F) -> Self
191 where
192 F: FnOnce(E) -> Self,
193 {
194 match self {
195 Ok(value) => Ok(value),
196 Err(error) => recovery(error),
197 }
198 }
199
200 #[inline]
201 fn bimap_result<B, F, SuccessF, ErrorF>(
202 self,
203 success_f: SuccessF,
204 error_f: ErrorF,
205 ) -> Result<B, F>
206 where
207 SuccessF: FnOnce(T) -> B,
208 ErrorF: FnOnce(E) -> F,
209 {
210 match self {
211 Ok(value) => Ok(success_f(value)),
212 Err(error) => Err(error_f(error)),
213 }
214 }
215}
216
217pub trait ResultExt<T, E> {
223 fn ctx<C: IntoErrorContext>(self, msg: C) -> Result<T, Box<ComposableError<E>>>;
225
226 fn ctx_with<F>(self, f: F) -> Result<T, Box<ComposableError<E>>>
228 where
229 F: FnOnce() -> String;
230}
231
232impl<T, E> ResultExt<T, E> for Result<T, E> {
233 #[inline]
234 fn ctx<C: IntoErrorContext>(self, msg: C) -> Result<T, Box<ComposableError<E>>> {
235 self.map_err(|e| Box::new(ComposableError::new(e).with_context(msg)))
236 }
237
238 #[inline]
239 fn ctx_with<F>(self, f: F) -> Result<T, Box<ComposableError<E>>>
240 where
241 F: FnOnce() -> String,
242 {
243 self.map_err(|e| Box::new(ComposableError::new(e).with_context(LazyContext::new(f))))
244 }
245}
246
247pub trait BoxedResultExt<T, E> {
249 fn ctx_boxed<C: IntoErrorContext>(self, msg: C) -> Self;
251
252 fn ctx_boxed_with<F>(self, f: F) -> Self
254 where
255 F: FnOnce() -> String;
256}
257
258impl<T, E> BoxedResultExt<T, E> for Result<T, Box<ComposableError<E>>> {
259 #[inline]
260 fn ctx_boxed<C: IntoErrorContext>(self, msg: C) -> Self {
261 self.map_err(|e| {
262 let inner = *e;
263 Box::new(inner.with_context(msg))
264 })
265 }
266
267 #[inline]
268 fn ctx_boxed_with<F>(self, f: F) -> Self
269 where
270 F: FnOnce() -> String,
271 {
272 self.map_err(|e| {
273 let inner = *e;
274 Box::new(inner.with_context(LazyContext::new(f)))
275 })
276 }
277}
278
279pub trait ErrorCategory<E> {
285 type ErrorFunctor<T>: WithError<E>;
286
287 fn lift<T>(value: T) -> Self::ErrorFunctor<T>;
288 fn handle_error<T>(error: E) -> Self::ErrorFunctor<T>;
289}
290
291impl<E> ErrorCategory<E> for Result<(), E> {
292 type ErrorFunctor<T> = Result<T, E>;
293
294 #[inline]
295 fn lift<T>(value: T) -> Result<T, E> {
296 Ok(value)
297 }
298
299 #[inline]
300 fn handle_error<T>(error: E) -> Result<T, E> {
301 Err(error)
302 }
303}