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}