Skip to main content

tidecoin_io/
error.rs

1// SPDX-License-Identifier: CC0-1.0
2
3#[cfg(all(not(feature = "std"), feature = "alloc"))]
4use alloc::boxed::Box;
5use core::fmt;
6#[cfg(feature = "std")]
7use std::boxed::Box;
8
9/// The `io` crate error type.
10#[derive(Debug)]
11pub struct Error {
12    kind: ErrorKind,
13    /// We want this type to be `?UnwindSafe` and `?RefUnwindSafe` - the same as `std::io::Error`.
14    ///
15    /// In `std` builds the existence of `dyn std::error:Error` prevents `UnwindSafe` and
16    /// `RefUnwindSafe` from being automatically implemented. But in `no-std` builds without the
17    /// marker nothing prevents it.
18    _not_unwind_safe: core::marker::PhantomData<NotUnwindSafe>,
19
20    #[cfg(feature = "std")]
21    error: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
22    #[cfg(all(feature = "alloc", not(feature = "std")))]
23    error: Option<Box<dyn fmt::Debug + Send + Sync + 'static>>,
24}
25
26impl Error {
27    /// Constructs a new I/O error.
28    #[cfg(feature = "std")]
29    pub fn new<E>(kind: ErrorKind, error: E) -> Self
30    where
31        E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
32    {
33        Self { kind, _not_unwind_safe: core::marker::PhantomData, error: Some(error.into()) }
34    }
35
36    /// Constructs a new I/O error.
37    #[cfg(all(feature = "alloc", not(feature = "std")))]
38    pub fn new<E: sealed::IntoBoxDynDebug>(kind: ErrorKind, error: E) -> Self {
39        Self { kind, _not_unwind_safe: core::marker::PhantomData, error: Some(error.into()) }
40    }
41
42    /// Returns the error kind for this error.
43    pub fn kind(&self) -> ErrorKind {
44        self.kind
45    }
46
47    /// Returns a reference to this error.
48    #[cfg(feature = "std")]
49    pub fn get_ref(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> {
50        self.error.as_deref()
51    }
52
53    /// Returns a reference to this error.
54    #[cfg(all(feature = "alloc", not(feature = "std")))]
55    pub fn get_ref(&self) -> Option<&(dyn fmt::Debug + Send + Sync + 'static)> {
56        self.error.as_deref()
57    }
58}
59
60impl From<ErrorKind> for Error {
61    fn from(kind: ErrorKind) -> Self {
62        Self {
63            kind,
64            _not_unwind_safe: core::marker::PhantomData,
65            #[cfg(any(feature = "std", feature = "alloc"))]
66            error: None,
67        }
68    }
69}
70
71impl fmt::Display for Error {
72    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
73        fmt.write_fmt(format_args!("I/O Error: {}", self.kind.description()))?;
74        #[cfg(any(feature = "alloc", feature = "std"))]
75        if let Some(e) = &self.error {
76            fmt.write_fmt(format_args!(". {:?}", e))?;
77        }
78        Ok(())
79    }
80}
81
82#[cfg(feature = "std")]
83impl std::error::Error for Error {
84    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
85        self.error.as_ref().and_then(|e| e.as_ref().source())
86    }
87}
88
89#[cfg(feature = "std")]
90impl From<std::io::Error> for Error {
91    fn from(o: std::io::Error) -> Self {
92        Self {
93            kind: ErrorKind::from_std(o.kind()),
94            _not_unwind_safe: core::marker::PhantomData,
95            error: o.into_inner(),
96        }
97    }
98}
99
100#[cfg(feature = "std")]
101impl From<Error> for std::io::Error {
102    fn from(o: Error) -> Self {
103        if let Some(err) = o.error {
104            Self::new(o.kind.to_std(), err)
105        } else {
106            o.kind.to_std().into()
107        }
108    }
109}
110
111/// Useful for preventing `UnwindSafe` and `RefUnwindSafe` from being automatically implemented.
112struct NotUnwindSafe {
113    _not_unwind_safe: core::marker::PhantomData<(&'static mut (), core::cell::UnsafeCell<()>)>,
114}
115
116unsafe impl Sync for NotUnwindSafe {}
117
118macro_rules! define_errorkind {
119    ($($(#[$($attr:tt)*])* $kind:ident),*) => {
120        #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
121        /// A minimal subset of [`std::io::ErrorKind`] which is used for [`Error`].
122        ///
123        /// Note that, as with [`std::io`], only [`Self::Interrupted`] has defined semantics in this
124        /// crate, all other variants are provided here only to provide higher-fidelity conversions
125        /// to and from [`std::io::Error`].
126        pub enum ErrorKind {
127            $(
128                $(#[$($attr)*])*
129                $kind
130            ),*
131        }
132
133        impl From<core::convert::Infallible> for ErrorKind {
134            fn from(never: core::convert::Infallible) -> Self { match never {} }
135        }
136
137        impl ErrorKind {
138            fn description(&self) -> &'static str {
139                match self {
140                    $(Self::$kind => stringify!($kind)),*
141                }
142            }
143
144            #[cfg(feature = "std")]
145            fn to_std(self) -> std::io::ErrorKind {
146                match self {
147                    $(Self::$kind => std::io::ErrorKind::$kind),*
148                }
149            }
150
151            #[cfg(feature = "std")]
152            fn from_std(o: std::io::ErrorKind) -> ErrorKind {
153                match o {
154                    $(std::io::ErrorKind::$kind => ErrorKind::$kind),*,
155                    _ => ErrorKind::Other
156                }
157            }
158        }
159    }
160}
161
162define_errorkind!(
163    /// An entity was not found, often a file.
164    NotFound,
165    /// The operation lacked the necessary privileges to complete.
166    PermissionDenied,
167    /// The connection was refused by the remote server.
168    ConnectionRefused,
169    /// The connection was reset by the remote server.
170    ConnectionReset,
171    /// The connection was aborted (terminated) by the remote server.
172    ConnectionAborted,
173    /// The network operation failed because it was not connected yet.
174    NotConnected,
175    /// A socket address could not be bound because the address is already in use elsewhere.
176    AddrInUse,
177    /// A nonexistent interface was requested or the requested address was not local.
178    AddrNotAvailable,
179    /// The operation failed because a pipe was closed.
180    BrokenPipe,
181    /// An entity already exists, often a file.
182    AlreadyExists,
183    /// The operation needs to block to complete, but the blocking operation was requested to not occur.
184    WouldBlock,
185    /// A parameter was incorrect.
186    InvalidInput,
187    /// Data not valid for the operation were encountered.
188    InvalidData,
189    /// The I/O operation’s timeout expired, causing it to be canceled.
190    TimedOut,
191    /// An error returned when an operation could not be completed because a call to `write` returned `Ok(0)`.
192    WriteZero,
193    /// This operation was interrupted.
194    Interrupted,
195    /// An error returned when an operation could not be completed because an "end of file" was reached prematurely.
196    UnexpectedEof,
197    // Note: Any time we bump the MSRV any new error kinds should be added here!
198    /// A custom error that does not fall under any other I/O error kind
199    Other
200);
201
202#[cfg(all(feature = "alloc", not(feature = "std")))]
203mod sealed {
204    use alloc::boxed::Box;
205    use alloc::string::String;
206    use core::fmt;
207
208    pub trait IntoBoxDynDebug {
209        fn into(self) -> Box<dyn fmt::Debug + Send + Sync + 'static>;
210    }
211
212    impl IntoBoxDynDebug for &str {
213        fn into(self) -> Box<dyn fmt::Debug + Send + Sync + 'static> {
214            Box::new(String::from(self))
215        }
216    }
217
218    impl IntoBoxDynDebug for String {
219        fn into(self) -> Box<dyn fmt::Debug + Send + Sync + 'static> {
220            Box::new(self)
221        }
222    }
223}