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}