mycelium_util/
error.rs

1//! An [`Error`] trait based on [`std::error::Error`].
2//!
3//! This is a modified version of [`std::error::Error`] that doesn't require the
4//! Rust standard library.
5use core::{any::TypeId, fmt};
6
7/// `Error` is a trait representing the basic expectations for error values,
8/// i.e., values of type `E` in `Result<T, E>`.
9///
10/// This is essentially a re-implementation of the Rust standard library's
11/// `std::error::Error` trait. Because we cannot use `std` in the kernel, we
12/// must reimplement that trait in `mycelium_util`.
13///
14/// Errors must describe themselves through the `Display` and `Debug` traits,
15/// and may provide cause chain information:
16///
17/// The [`source`] method is generally used when errors cross "abstraction
18/// boundaries". If one module must report an error that is caused by an error
19/// from a lower-level module, it can allow access to that error via the
20/// [`source`] method. This makes it possible for the high-level module to
21/// provide its own errors while also revealing some of the implementation for
22/// debugging via [`source`] chains.
23///
24/// [`source`]: trait.Error.html#method.source
25pub trait Error: fmt::Debug + fmt::Display {
26    /// The lower-level source of this error, if any.
27    ///
28    /// This is equivalent to `std::error::Error`'s `source` method.
29    fn source(&self) -> Option<&(dyn Error + 'static)> {
30        None
31    }
32
33    /// Gets the `TypeId` of `self`.
34    fn type_id(&self, _: private::Internal) -> TypeId
35    where
36        Self: 'static,
37    {
38        TypeId::of::<Self>()
39    }
40}
41
42impl dyn Error + 'static + Send {
43    /// Forwards to the method defined on the type `dyn Error`.
44    #[inline]
45    pub fn is<T: Error + 'static>(&self) -> bool {
46        <dyn Error + 'static>::is::<T>(self)
47    }
48
49    /// Forwards to the method defined on the type `dyn Error`.
50    #[inline]
51    pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> {
52        <dyn Error + 'static>::downcast_ref::<T>(self)
53    }
54
55    /// Forwards to the method defined on the type `dyn Error`.
56    #[inline]
57    pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T> {
58        <dyn Error + 'static>::downcast_mut::<T>(self)
59    }
60}
61
62impl dyn Error + 'static + Send + Sync {
63    /// Forwards to the method defined on the type `dyn Error`.
64    #[inline]
65    pub fn is<T: Error + 'static>(&self) -> bool {
66        <dyn Error + 'static>::is::<T>(self)
67    }
68
69    /// Forwards to the method defined on the type `dyn Error`.
70    #[inline]
71    pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> {
72        <dyn Error + 'static>::downcast_ref::<T>(self)
73    }
74
75    /// Forwards to the method defined on the type `dyn Error`.
76    #[inline]
77    pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T> {
78        <dyn Error + 'static>::downcast_mut::<T>(self)
79    }
80}
81
82impl dyn Error {
83    /// Returns an iterator starting with the current error and continuing with
84    /// recursively calling [`source`].
85    ///
86    /// [`source`]: trait.Error.html#method.source
87    #[inline]
88    pub fn iter_chain(&self) -> ErrorIter<'_> {
89        ErrorIter {
90            current: Some(self),
91        }
92    }
93
94    /// Returns an iterator starting with the [`source`] of this error
95    /// and continuing with recursively calling [`source`].
96    ///
97    /// [`source`]: trait.Error.html#method.source
98    #[inline]
99    pub fn iter_sources(&self) -> ErrorIter<'_> {
100        ErrorIter {
101            current: self.source(),
102        }
103    }
104}
105
106impl dyn Error + 'static {
107    /// Returns `true` if the boxed type is the same as `T`
108    #[inline]
109    pub fn is<T: Error + 'static>(&self) -> bool {
110        // Get `TypeId` of the type this function is instantiated with.
111        let t = TypeId::of::<T>();
112
113        // Get `TypeId` of the type in the trait object.
114        let obj = self.type_id(private::Internal);
115
116        // Compare both `TypeId`s on equality.
117        t == obj
118    }
119
120    /// Returns some reference to the boxed value if it is of type `T`, or
121    /// `None` if it isn't.
122    #[inline]
123    pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> {
124        if self.is::<T>() {
125            unsafe { Some(&*(self as *const dyn Error as *const T)) }
126        } else {
127            None
128        }
129    }
130
131    /// Returns some mutable reference to the boxed value if it is of type `T`, or
132    /// `None` if it isn't.
133    #[inline]
134    pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T> {
135        if self.is::<T>() {
136            unsafe { Some(&mut *(self as *mut dyn Error as *mut T)) }
137        } else {
138            None
139        }
140    }
141}
142
143/// An iterator over [`Error`]
144///
145/// [`Error`]: trait.Error.html
146#[derive(Copy, Clone, Debug)]
147pub struct ErrorIter<'a> {
148    current: Option<&'a (dyn Error + 'static)>,
149}
150
151impl<'a> Iterator for ErrorIter<'a> {
152    type Item = &'a (dyn Error + 'static);
153
154    fn next(&mut self) -> Option<Self::Item> {
155        let current = self.current;
156        self.current = self.current.and_then(Error::source);
157        current
158    }
159}
160
161impl Error for &'static str {}
162
163mod private {
164    // This is a hack to prevent `type_id` from being overridden by `Error`
165    // implementations, since that can enable unsound downcasting.
166    #[derive(Debug)]
167    pub struct Internal;
168}