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