generic_err/
lib.rs

1#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
2//!
3//! ## Usage
4//! ```rust
5#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/examples/simple.rs"))]
6//! ```
7//!
8//! You can also use the [`Untyped`] variant of [`GenericError`] if that suits your needs better.
9//!
10//! There are also extension traits for convenience:
11//! ```rust
12#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/examples/ext-traits.rs"))]
13//! ```
14//!
15//! Also, for types that don't implement [`Debug`](std::fmt::Debug),
16//! there are methods to suit your needs, e.g. [`GenericError::from_non_err`].
17
18pub mod prelude {
19  //! Only exports extension traits without polluting your namespace
20  pub use crate::{GenericErrorExt as _, GenericErrorRefExt as _};
21}
22
23use extension_traits::extension;
24use std::{convert::Infallible, fmt::Display, marker::PhantomData, sync::Arc};
25
26/// A marker for [`GenericError`] that indicates it doesn't have
27/// knowledge of the type it is wrapping
28pub type Untyped = Infallible;
29
30/// Wrapper around any error T such that it is `Clone`,
31/// [`serde::Serialize`], and [`serde::Deserialize`] by storing
32/// its initial `Debug` and `Display` representations as `String`s.
33// TODO: Make an enum that maintains source from [core::error::Error]
34// when not over a serialization/deserialization boundary
35#[derive(serde::Serialize, serde::Deserialize)]
36pub struct GenericError<T>
37where
38  T: 'static,
39{
40  display: String,
41  debug: String,
42  source: Option<Arc<GenericError<Untyped>>>,
43  _phantom: PhantomData<T>,
44}
45
46#[extension(pub trait GenericErrorExt)]
47impl<T, E> Result<T, E>
48where
49  E: std::fmt::Display + std::fmt::Debug,
50{
51  fn make_generic(self) -> Result<T, GenericError<E>>
52  where
53    E: core::error::Error,
54  {
55    self.map_err(GenericError::from)
56  }
57
58  fn make_generic_untyped(self) -> Result<T, GenericError<Untyped>>
59  where
60    E: core::error::Error + 'static,
61  {
62    self.make_generic().map_err(GenericError::untyped)
63    // self.map_err(|err: E| GenericError::from_ref(&err).untyped())
64  }
65}
66
67#[extension(pub trait GenericErrorRefExt)]
68impl<T, E> Result<T, &E>
69where
70  E: Display + std::fmt::Debug + core::error::Error,
71{
72  fn make_generic_ref(self) -> Result<T, GenericError<E>> {
73    self.map_err(|e| GenericError::from_ref(e))
74  }
75
76  fn make_generic_ref_untyped(self) -> Result<T, GenericError<Untyped>>
77  where
78    E: 'static,
79  {
80    self.make_generic_ref().map_err(GenericError::untyped)
81  }
82}
83
84impl<T> core::error::Error for GenericError<T> {}
85
86impl<T> std::fmt::Debug for GenericError<T> {
87  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88    write!(f, "{}", self.debug)
89  }
90}
91
92impl<T> std::fmt::Display for GenericError<T> {
93  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94    write!(f, "{}", self.display)
95  }
96}
97
98impl<T> Clone for GenericError<T> {
99  fn clone(&self) -> Self {
100    GenericError {
101      display: self.display.clone(),
102      debug: self.debug.clone(),
103      source: self.source.clone(),
104      _phantom: PhantomData,
105    }
106  }
107}
108
109impl<T> GenericError<T> {
110  /// Preserves source
111  pub fn untyped(self) -> GenericError<Untyped> {
112    GenericError {
113      display: self.display,
114      debug: self.debug,
115      source: self.source,
116      _phantom: PhantomData,
117    }
118  }
119}
120
121impl GenericError<Untyped> {
122  fn from_dyn(err: &dyn core::error::Error) -> GenericError<Untyped> {
123    GenericError {
124      display: err.to_string(),
125      debug: format!("{:?}", err),
126      source: err
127        .source()
128        .map(|source| Arc::new(GenericError::from_dyn(source))),
129      _phantom: PhantomData,
130    }
131  }
132}
133
134impl<E> GenericError<E>
135where
136  E: std::fmt::Display + std::fmt::Debug + core::error::Error,
137{
138  /// Preserves source
139  pub fn from(err: E) -> GenericError<E> {
140    Self {
141      debug: format!("{:?}", err),
142      display: err.to_string(),
143      source: err
144        .source()
145        .map(|source: &dyn core::error::Error| Arc::new(GenericError::from_dyn(source).untyped())),
146      _phantom: PhantomData,
147    }
148  }
149
150  /// Preserves source
151  pub fn from_ref(err: &E) -> GenericError<E> {
152    Self {
153      debug: format!("{:?}", err),
154      display: err.to_string(),
155      source: err
156        .source()
157        .map(|err| Arc::new(GenericError::from_dyn(err))),
158      _phantom: PhantomData,
159    }
160  }
161}
162
163impl<E> GenericError<E>
164where
165  E: std::fmt::Display + std::fmt::Debug,
166{
167  pub fn from_non_err(err: E) -> GenericError<E> {
168    Self {
169      debug: format!("{:?}", err),
170      display: err.to_string(),
171      source: None,
172      _phantom: PhantomData,
173    }
174  }
175
176  pub fn from_non_err_ref(err: &E) -> GenericError<E> {
177    Self {
178      debug: format!("{:?}", err),
179      display: err.to_string(),
180      source: None,
181      _phantom: PhantomData,
182    }
183  }
184}