arc_io_error/
lib.rs

1//! See the type-level documentation for [`IoError`](struct.IoError.html).
2//!
3//! **Note:** [`IoErrorKind`](type.IoResult.html) is a re-export of (alias for)
4//! [`std::io::ErrorKind`](https://doc.rust-lang.org/std/io/struct.ErrorKind.html).
5//! It is not a new type, despite how it may appear in Rustdoc.
6
7#![deny(missing_docs, missing_debug_implementations)]
8#![doc(html_root_url = "https://docs.rs/arc-io-error/0.1.1")]
9
10use std::error::Error;
11use std::fmt;
12use std::io;
13use std::mem;
14use std::sync::Arc;
15
16/// A version of
17/// [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html)
18/// implemented on top of
19/// [`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html)
20/// instead of
21/// [`Box`](https://doc.rust-lang.org/std/boxed/struct.Box.html),
22/// making it cloneable.
23///
24/// The API of this type has been designed to match
25/// [`io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html), with
26/// two exceptions:
27///
28/// - [`IoError::new`](struct.IoError.html#method.new) and
29///   [`IoError::into_inner`](struct.IoError.html#method.into_inner) substitute
30///   [`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html) for
31///   [`Box`](https://doc.rust-lang.org/std/boxed/struct.Box.html), and
32/// - [`IoError`](struct.IoError.html) has no equivalent to
33///   [`io::Error::get_mut`](https://doc.rust-lang.org/std/io/struct.Error.html#method.get_mut),
34///   as the inner error instance is shared.
35///
36/// See the standard library documentation for more detailed API-level
37/// descriptions than are given here.
38///
39/// [`IoError`](struct.IoError.html) implements
40/// [`From`](https://doc.rust-lang.org/std/convert/trait.From.html)
41/// for [`io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html)
42/// and vice-versa, so the two types can easily be converted between each other.
43/// A type containing
44/// [`io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) can
45/// be made
46/// [`Clone`](https://doc.rust-lang.org/std/clone/trait.Clone.html)-compatible
47/// by instead storing [`IoError`](struct.IoError.html) internally and
48/// converting from/to
49/// [`io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) on
50/// API boundaries.
51///
52/// Clones derived from the same original [`IoError`](struct.IoError.html)
53/// instance will share a single heap-allocated error instance (if one is
54/// present) using
55/// [`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html).
56/// [`io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html)
57/// instances produced by converting those clones back with the
58/// [`From`](https://doc.rust-lang.org/std/convert/trait.From.html)
59/// implementation will also share the same single error instance.
60#[derive(Clone)]
61pub struct IoError(IoErrorRepr);
62
63/// See [`std::io::ErrorKind`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html).
64pub use io::ErrorKind as IoErrorKind;
65
66/// See [`std::io::Result`](https://doc.rust-lang.org/std/io/type.Result.html),
67/// with [`IoError`](struct.IoError.html) substituted for
68/// [`io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html).
69pub type IoResult<T> = Result<T, IoError>;
70
71#[derive(Clone)]
72enum IoErrorRepr {
73    Os(i32),
74    Kind(IoErrorKind),
75    Custom(IoErrorKind, Arc<Error + Send + Sync>),
76}
77
78impl IoError {
79    /// See
80    /// [`io::Error::new`](https://doc.rust-lang.org/std/io/struct.Error.html#method.new),
81    /// with
82    /// [`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html)
83    /// substituted for
84    /// [`Box`](https://doc.rust-lang.org/std/boxed/struct.Box.html).
85    pub fn new<E>(kind: IoErrorKind, error: E) -> Self
86        where E: Into<Arc<Error + Send + Sync>>
87    {
88        IoError(IoErrorRepr::Custom(kind, error.into()))
89    }
90
91    /// See
92    /// [`io::Error::last_os_error`](https://doc.rust-lang.org/std/io/struct.Error.html#method.last_os_error).
93    pub fn last_os_error() -> Self {
94        io::Error::last_os_error().into()
95    }
96
97    /// See
98    /// [`io::Error::from_raw_os_error`](https://doc.rust-lang.org/std/io/struct.Error.html#method.from_raw_os_error).
99    pub fn from_raw_os_error(code: i32) -> Self {
100        IoError(IoErrorRepr::Os(code))
101    }
102
103    /// See
104    /// [`io::Error::raw_os_error`](https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error).
105    pub fn raw_os_error(&self) -> Option<i32> {
106        match self.0 {
107            IoErrorRepr::Os(code) => Some(code),
108            _ => None,
109        }
110    }
111
112    /// See
113    /// [`io::Error::get_ref`](https://doc.rust-lang.org/std/io/struct.Error.html#method.get_ref).
114    pub fn get_ref(&self) -> Option<&('static + Error + Send + Sync)> {
115        match self.0 {
116            IoErrorRepr::Custom(_, ref inner) => Some(inner.as_ref()),
117            _ => None,
118        }
119    }
120
121    /// See
122    /// [`io::Error::into_inner`](https://doc.rust-lang.org/std/io/struct.Error.html#method.into_inner),
123    /// with
124    /// [`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html)
125    /// substituted for
126    /// [`Box`](https://doc.rust-lang.org/std/boxed/struct.Box.html).
127    pub fn into_inner(self) -> Option<Arc<Error + Send + Sync>> {
128        match self.0 {
129            IoErrorRepr::Custom(_, inner) => Some(inner),
130            _ => None,
131        }
132    }
133
134    /// See
135    /// [`io::Error::kind`](https://doc.rust-lang.org/std/io/struct.Error.html#method.kind).
136    pub fn kind(&self) -> IoErrorKind {
137        match self.0 {
138            IoErrorRepr::Os(code) => io::Error::from_raw_os_error(code).kind(),
139            IoErrorRepr::Kind(kind) => kind,
140            IoErrorRepr::Custom(kind, _) => kind,
141        }
142    }
143}
144
145impl From<io::Error> for IoError {
146    fn from(src: io::Error) -> Self {
147        if let Some(code) = src.raw_os_error() {
148            return IoError(IoErrorRepr::Os(code));
149        }
150
151        let kind = src.kind();
152        match src.into_inner() {
153            None => IoError(IoErrorRepr::Kind(kind)),
154            Some(inner) => {
155                let shared = Arc::new(BoxError(inner));
156                IoError(IoErrorRepr::Custom(kind, shared))
157            }
158        }
159    }
160}
161
162impl From<IoError> for io::Error {
163    fn from(src: IoError) -> Self {
164        match src.0 {
165            IoErrorRepr::Os(code) => io::Error::from_raw_os_error(code),
166            IoErrorRepr::Kind(kind) => kind.into(),
167            IoErrorRepr::Custom(kind, inner) => io::Error::new(kind, ArcError(inner)),
168        }
169    }
170}
171
172impl From<IoErrorKind> for IoError {
173    fn from(src: IoErrorKind) -> IoError {
174        IoError(IoErrorRepr::Kind(src))
175    }
176}
177
178impl fmt::Debug for IoError {
179    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
180        match self.0 {
181            IoErrorRepr::Os(code) => fmt::Debug::fmt(&io::Error::from_raw_os_error(code), fmt),
182            IoErrorRepr::Kind(kind) => fmt::Debug::fmt(&io::Error::from(kind), fmt),
183            IoErrorRepr::Custom(ref kind, ref inner) => {
184                fmt.debug_struct("Error").field("repr", &(kind, inner)).finish()
185            }
186        }
187    }
188}
189
190impl fmt::Display for IoError {
191    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
192        match self.0 {
193            IoErrorRepr::Os(code) => fmt::Display::fmt(&io::Error::from_raw_os_error(code), fmt),
194            IoErrorRepr::Kind(kind) => fmt::Display::fmt(&io::Error::from(kind), fmt),
195            IoErrorRepr::Custom(_, ref inner) => fmt::Display::fmt(inner, fmt),
196        }
197    }
198}
199
200impl Error for IoError {
201    fn description(&self) -> &str {
202        match self.0 {
203            // Here we need to access io::ErrorKind::as_str
204            // (https://github.com/rust-lang/rust/blob/13d94d5fa8129a34f5c77a1bcd76983f5aed2434/src/libstd/io/error.rs#L182)
205            // which produces a &'static str, but it's private.
206            //
207            // io::Error::description returns the result of a call to this
208            // function intact in the case of an OS or kind-only error
209            // (https://github.com/rust-lang/rust/blob/13d94d5fa8129a34f5c77a1bcd76983f5aed2434/src/libstd/io/error.rs#L534)
210            // but with an insufficient lifetime (matching the signature of
211            // trait Error::description). So we hack around this with an unsafe
212            // transmute (gulp).
213            IoErrorRepr::Os(code) => {
214                unsafe { mem::transmute(io::Error::from_raw_os_error(code).description()) }
215            }
216            IoErrorRepr::Kind(kind) => {
217                unsafe { mem::transmute(io::Error::from(kind).description()) }
218            }
219            IoErrorRepr::Custom(_, ref inner) => inner.description(),
220        }
221    }
222
223    fn cause(&self) -> Option<&Error> {
224        match self.0 {
225            IoErrorRepr::Custom(_, ref inner) => inner.cause(),
226            _ => None,
227        }
228    }
229}
230
231struct ArcError(Arc<Error + Send + Sync>);
232
233impl fmt::Debug for ArcError {
234    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
235        fmt::Debug::fmt(&self.0, fmt)
236    }
237}
238
239impl fmt::Display for ArcError {
240    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
241        fmt::Display::fmt(&self.0, fmt)
242    }
243}
244
245impl Error for ArcError {
246    fn description(&self) -> &str {
247        self.0.description()
248    }
249
250    fn cause(&self) -> Option<&Error> {
251        self.0.cause()
252    }
253}
254
255struct BoxError(Box<Error + Send + Sync>);
256
257impl fmt::Debug for BoxError {
258    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
259        fmt::Debug::fmt(&self.0, fmt)
260    }
261}
262
263impl fmt::Display for BoxError {
264    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
265        fmt::Display::fmt(&self.0, fmt)
266    }
267}
268
269impl Error for BoxError {
270    fn description(&self) -> &str {
271        self.0.description()
272    }
273
274    fn cause(&self) -> Option<&Error> {
275        self.0.cause()
276    }
277}