Skip to main content

bitcoin_io/
error.rs

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