uni_error/
error.rs

1use alloc::{borrow::Cow, boxed::Box, sync::Arc};
2use core::{
3    any::{Any, TypeId, type_name},
4    error::Error,
5    fmt::{Debug, Display},
6    ops::Deref,
7};
8
9use crate::cause::{Cause, CauseInner, Chain, UniDisplay};
10
11// *** Type aliases ***
12
13/// A result type that specifies a customkind.
14pub type UniResult<T, U> = Result<T, UniError<U>>;
15
16/// A result type that specifies no kind.
17pub type SimpleResult<T> = Result<T, SimpleError>;
18
19/// A result type that specifies a trait object kind.
20pub type DynResult<T> = Result<T, DynError>;
21
22/// An error type that is used when there is no kind.
23pub type SimpleError = UniError<()>;
24
25// *** DynError ***
26
27/// An error type with an erased kind type. It can be used when you
28/// don't know `T` or wish to propagate multiple `T` types. `UniError<T>`
29/// can be recovered via downcasting if `T` is known.
30#[derive(Debug)]
31pub struct DynError(Box<dyn UniErrorOps + Send + Sync>);
32
33impl DynError {
34    pub(crate) fn new<E: UniErrorOps>(err: E) -> Self {
35        Self(Box::new(err))
36    }
37
38    /// Attempts to downcast a `DynError` to a `UniError<T>`.
39    pub fn downcast<T: UniKind>(self) -> Option<UniError<T>> {
40        let err: Box<dyn Any> = self.0;
41        err.downcast().ok().map(|err| *err)
42    }
43}
44
45impl Deref for DynError {
46    type Target = dyn UniErrorOps + Send + Sync;
47
48    fn deref(&self) -> &Self::Target {
49        &*self.0
50    }
51}
52
53// *** UniKind trait ***
54
55/// A trait that specifies a custom error kind. Any specified to facilitate downcasting.
56pub trait UniKind: Debug + Any + Send + Sync {
57    /// The string value of the kind, if any. This is useful for programmatic evaluation
58    /// when the type is boxed in the error chain and the type is not known. Defaults to `""`.
59    fn value(&self) -> Cow<'static, str> {
60        Cow::Borrowed("")
61    }
62
63    /// Returns additional context for this specific kind, if any. Defaults to `None`.
64    fn context(&self) -> Option<Cow<'static, str>> {
65        None
66    }
67
68    /// Returns the code (typically for FFI) for this specific kind. Defaults to -1.
69    fn code(&self) -> i32 {
70        -1
71    }
72
73    /// Returns the concrete type name.
74    fn type_name(&self) -> &'static str {
75        type_name::<Self>()
76    }
77}
78
79impl dyn UniKind {
80    /// Attempts to downcast a `UniKind` to a specific concrete type.
81    pub fn downcast_ref<T: UniKind>(&self) -> Option<&T> {
82        let err: &dyn Any = self;
83        err.downcast_ref()
84    }
85}
86
87impl UniKind for () {}
88
89// *** UniErrorInner ***
90
91#[derive(Debug)]
92pub(crate) struct UniErrorInner<T> {
93    kind: T,
94    context: Option<Cow<'static, str>>,
95    cause: Option<CauseInner>,
96}
97
98impl<T: UniKind> UniErrorInner<T> {
99    pub fn prev_cause<'e>(&'e self) -> Option<Cause<'e>> {
100        self.cause.as_ref().map(|inner| Cause::from_inner(inner))
101    }
102}
103
104impl<T: UniKind> Display for UniErrorInner<T> {
105    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
106        if let Some(context) = &self.context {
107            write!(f, "{}", context)?;
108        }
109
110        if let Some(context) = self.kind.context() {
111            if self.context.is_some() {
112                write!(f, ": ")?;
113            }
114            write!(f, "{}", context)?;
115        }
116
117        if let Some(cause) = &self.prev_cause() {
118            if self.context.is_some() || self.kind.context().is_some() {
119                write!(f, ": ")?;
120            }
121            write!(f, "{}", cause)?;
122        }
123
124        Ok(())
125    }
126}
127
128impl<T: UniKind> Error for UniErrorInner<T> {
129    fn source(&self) -> Option<&(dyn Error + 'static)> {
130        match self.prev_cause() {
131            Some(Cause::UniError(err)) => Some(&***err),
132            Some(Cause::UniStdError(err)) => Some(err),
133            Some(Cause::StdError(err)) => Some(err),
134            Some(Cause::UniDisplay(_)) | None => None,
135        }
136    }
137}
138
139// *** UniError ***
140
141/// A custom error type that can be used to return an error with a custom error kind.
142#[derive(Debug)]
143pub struct UniError<T> {
144    inner: Arc<UniErrorInner<T>>,
145}
146
147impl<T: UniKind + Default> UniError<T> {
148    /// Creates a new `UniError` with a default kind, the provided context, and no cause.
149    pub fn from_context(context: impl Into<Cow<'static, str>>) -> Self {
150        Self::new(Default::default(), Some(context.into()), None)
151    }
152
153    pub fn from_boxed(error: Box<dyn Error + Send + Sync>) -> Self {
154        Self::new(
155            Default::default(),
156            None,
157            Some(CauseInner::from_boxed_error(error)),
158        )
159    }
160
161    pub fn from_kind_boxed(kind: T, error: Box<dyn Error + Send + Sync>) -> Self {
162        Self::new(kind, None, Some(CauseInner::from_boxed_error(error)))
163    }
164
165    pub fn from_kind_context_boxed(
166        kind: T,
167        context: impl Into<Cow<'static, str>>,
168        error: Box<dyn Error + Send + Sync>,
169    ) -> Self {
170        Self::new(
171            kind,
172            Some(context.into()),
173            Some(CauseInner::from_boxed_error(error)),
174        )
175    }
176}
177
178impl<T: UniKind> UniError<T> {
179    pub(crate) fn new(
180        kind: T,
181        context: Option<Cow<'static, str>>,
182        cause: Option<CauseInner>,
183    ) -> Self {
184        Self {
185            inner: Arc::new(UniErrorInner {
186                kind,
187                context,
188                cause,
189            }),
190        }
191    }
192
193    /// Creates a new `UniError` with the provided kind and no context or cause.
194    pub fn from_kind(kind: T) -> Self {
195        Self::new(kind, None, None)
196    }
197
198    /// Creates a new `UniError` with the provided kind, the provided context, and no cause.
199    pub fn from_kind_context(kind: T, context: impl Into<Cow<'static, str>>) -> Self {
200        Self::new(kind, Some(context.into()), None)
201    }
202
203    /// Returns a reference to the custom kind.
204    pub fn kind_ref(&self) -> &T {
205        &self.inner.kind
206    }
207}
208
209impl<T: Copy> UniError<T> {
210    /// Returns a copy of the custom kind.
211    pub fn kind(&self) -> T {
212        self.inner.kind
213    }
214}
215
216/// A trait that specifies the operations that can be performed on a `UniError`.
217pub trait UniErrorOps: UniDisplay + Deref<Target = dyn Error + Send + Sync + 'static> {
218    /// Return a trait object reference to the custom kind.
219    fn kind_dyn_ref(&self) -> &dyn UniKind;
220
221    /// Returns true if the error is a `SimpleError`.
222    fn is_simple(&self) -> bool {
223        self.type_id() == TypeId::of::<SimpleError>()
224    }
225
226    /// Returns a reference to the first entry in the cause chain.
227    fn prev_cause<'e>(&'e self) -> Option<Cause<'e>>;
228
229    /// Returns an iterator over the cause chain.
230    fn chain(&self) -> Chain<'_>;
231
232    // TODO: Remove Option and make 'self' a possible candidate
233    /// Returns the root cause of this error. If `None` is returned then this error is the root cause.
234    fn root_cause(&self) -> Option<Cause<'_>>;
235}
236
237impl dyn UniErrorOps + Send + Sync {
238    /// Attempts to downcast a `DynError` to a reference to a `UniError<T>`.
239    pub fn downcast_ref<T: UniKind>(&self) -> Option<&UniError<T>> {
240        let err: &dyn Any = self;
241        err.downcast_ref()
242    }
243}
244
245impl<T: UniKind> UniErrorOps for UniError<T> {
246    fn kind_dyn_ref(&self) -> &dyn UniKind {
247        self.kind_ref()
248    }
249
250    fn prev_cause<'e>(&'e self) -> Option<Cause<'e>> {
251        self.inner.prev_cause()
252    }
253
254    fn chain(&self) -> Chain<'_> {
255        Chain::new(self.prev_cause())
256    }
257
258    fn root_cause(&self) -> Option<Cause<'_>> {
259        let mut chain = self.chain();
260        let mut root = chain.next();
261
262        while let Some(next) = chain.next() {
263            root = Some(next);
264        }
265        root
266    }
267}
268
269impl<T: UniKind> Display for UniError<T> {
270    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
271        <UniErrorInner<T> as Display>::fmt(&self.inner, f)
272    }
273}
274
275// Manually implement as derive requires T: Clone
276impl<T: UniKind> Clone for UniError<T> {
277    fn clone(&self) -> Self {
278        Self {
279            inner: Arc::clone(&self.inner),
280        }
281    }
282}
283
284impl<T: UniKind + PartialEq + 'static> PartialEq for UniError<T> {
285    fn eq(&self, other: &Self) -> bool {
286        // Kind values must be equal at minimum
287        if self.inner.kind == other.inner.kind {
288            // If the kind is `()`, then the context must be equal, otherwise
289            // kind equality alone is sufficient
290            if self.is_simple() {
291                self.inner.context == other.inner.context
292            } else {
293                true
294            }
295        } else {
296            false
297        }
298    }
299}
300
301impl<T: UniKind> Deref for UniError<T> {
302    type Target = dyn Error + Sync + Send + 'static;
303
304    fn deref(&self) -> &Self::Target {
305        &self.inner
306    }
307}
308
309impl<T: UniKind> AsRef<dyn Error + Sync + Send> for UniError<T> {
310    fn as_ref(&self) -> &(dyn Error + Sync + Send + 'static) {
311        &**self
312    }
313}