io_err/
lib.rs

1pub mod result_ext;
2
3pub use result_ext::Result;
4
5/// construct an io error rapidly
6/// ```
7/// let error = err!(other, "another error");
8/// let other = err!("another unspecified error, will be categorized as `Other`");
9/// let result = err!(("this error will be encapsulated under an Err()"));
10/// fn chosen_one<T>(ty: T) -> Result<T> {
11///     if random() {
12///         // unspecified errors will always be classified as `Other`
13///         err!(("you died in the process"))? // this will return early
14///     } else if !chosen() {
15///         err!((permission_denied, "you are not the chosen one"))?
16///     }
17///     Ok(ty)
18/// }
19/// ```
20#[macro_export]
21macro_rules! err {
22    (not_found, $e: expr) => {
23        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::NotFound, $e))
24    };
25    (permission_denied, $e: expr) => {
26        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::PermissionDenied, $e))
27    };
28    (conn_refused, $e: expr) => {
29        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::ConnectionRefused, $e))
30    };
31    (conn_reset, $e: expr) => {
32        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::ConnectionReset, $e))
33    };
34    (host_unreachable, $e: expr) => {
35        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::HostUnreachable, $e))
36    };
37    (net_unreachable, $e: expr) => {
38        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::NetworkUnreachable, $e))
39    };
40    (conn_aborted, $e: expr) => {
41        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::ConnectionAborted, $e))
42    };
43    (not_connected, $e: expr) => {
44        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::NotConnected, $e))
45    };
46    (in_use, $e: expr) => {
47        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::AddrInUse, $e))
48    };
49    (addr_not_available, $e: expr) => {
50        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::AddrNotAvailable, $e))
51    };
52    (net_down, $e: expr) => {
53        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::NetworkDown, $e))
54    };
55    (broken_pipe, $e: expr) => {
56        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::BrokenPipe, $e))
57    };
58    (already_exists, $e: expr) => {
59        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::AlreadyExists, $e))
60    };
61    (would_block, $e: expr) => {
62        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::WouldBlock, $e))
63    };
64    (not_a_dir, $e: expr) => {
65        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::NotADirectory, $e))
66    };
67    (is_a_dir, $e: expr) => {
68        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::IsADirectory, $e))
69    };
70    (dir_not_empty, $e: expr) => {
71        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::DirectoryNotEmpty, $e))
72    };
73    (read_only_fs, $e: expr) => {
74        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::ReadOnlyFilesystem, $e))
75    };
76    (fs_loop, $e: expr) => {
77        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::FilesystemLoop, $e))
78    };
79    (stale_net_filehandle, $e: expr) => {
80        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::StaleNetworkFileHandle, $e))
81    };
82    (invalid_input, $e: expr) => {
83        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::InvalidInput, $e))
84    };
85    (invalid_data, $e: expr) => {
86        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::InvalidData, $e))
87    };
88    (timeout, $e: expr) => {
89        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::TimedOut, $e))
90    };
91    (write_zero, $e: expr) => {
92        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::WriteZero, $e))
93    };
94    (storage_full, $e: expr) => {
95        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::StorageFull, $e))
96    };
97    (not_seekable, $e: expr) => {
98        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::NotSeekable, $e))
99    };
100    (fs_quota_exceeded, $e: expr) => {
101        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::FilesystemQuotaExceeded, $e))
102    };
103    (file_too_large, $e: expr) => {
104        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::FileTooLarge, $e))
105    };
106    (resource_busy, $e: expr) => {
107        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::ResourceBusy, $e))
108    };
109    (executable_busy, $e: expr) => {
110        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::ExecutableFileBusy, $e))
111    };
112    (deadlock, $e: expr) => {
113        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::Deadlock, $e))
114    };
115    (crosses_devices, $e: expr) => {
116        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::CrossesDevices, $e))
117    };
118    (too_many_links, $e: expr) => {
119        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::TooManyLinks, $e))
120    };
121    (filename_too_long, $e: expr) => {
122        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::FilenameTooLong, $e))
123    };
124    (argument_list_too_long, $e: expr) => {
125        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::ArgumentListTooLong, $e))
126    };
127    (interrupted, $e: expr) => {
128        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::Interrupted, $e))
129    };
130    (unsupported, $e: expr) => {
131        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::Unsupported, $e))
132    };
133    (unexpected_eof, $e: expr) => {
134        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::UnexpectedEof, $e))
135    };
136    (out_of_memory, $e: expr) => {
137        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::OutOfMemory, $e))
138    };
139    (other, $e: expr) => {
140        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::Other, $e))
141    };
142    (uncategorized, $e: expr) => {
143        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::Uncategorized, $e))
144    };
145    ($p: ident, $e: expr) => {
146        $crate::Error::new(::std::io::Error::new(::std::io::ErrorKind::$p, $e))
147    };
148
149    (($($t: tt)*)) => {
150        Err($crate::err!($($t)*))
151    };
152    (@$i: ident) => {
153        {
154            |e| $crate::err!($i, e)
155        }
156    };
157    ($e: expr) => {
158        $crate::err!(other, $e)
159    };
160    ($p: ident, $fmt:expr, $($arg:tt)*) => {
161        $crate::err!($p, format!($fmt, $($arg)*))
162    };
163    ($fmt:expr, $($arg:tt)*) => {
164        $crate::err!(format!($fmt, $($arg)*))
165    };
166}
167
168#[macro_export]
169macro_rules! bail {
170    ($($t: tt)*) => {
171        return Err($crate::err!($($t)*));
172    };
173}
174
175use serde::{ser::SerializeTuple, Deserialize, Serialize};
176use serde_repr::{Deserialize_repr, Serialize_repr};
177use std::{
178    fmt::{Debug, Display},
179    io::ErrorKind as StdErrorKind,
180};
181
182#[repr(transparent)]
183/// a result type equivalent to std::io::Error, but implements `Serialize` and `Deserialize`
184pub struct Error(std::io::Error);
185impl Error {
186    #[inline]
187    /// construct a new Error type from a std::io::Error
188    pub fn new(e: std::io::Error) -> Self {
189        Error(e)
190    }
191}
192
193impl std::ops::Deref for Error {
194    type Target = std::io::Error;
195
196    #[inline]
197    fn deref(&self) -> &Self::Target {
198        &self.0
199    }
200}
201
202impl std::ops::DerefMut for Error {
203    #[inline]
204    fn deref_mut(&mut self) -> &mut Self::Target {
205        &mut self.0
206    }
207}
208
209impl From<std::io::Error> for Error {
210    #[inline]
211    fn from(error: std::io::Error) -> Self {
212        Error(error)
213    }
214}
215
216impl From<Error> for std::io::Error {
217    #[inline]
218    fn from(error: Error) -> Self {
219        error.0
220    }
221}
222
223impl Display for Error {
224    #[inline]
225    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226        <std::io::Error as Display>::fmt(&self.0, f)
227    }
228}
229impl Debug for Error {
230    #[inline]
231    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
232        <std::io::Error as Debug>::fmt(&self.0, f)
233    }
234}
235
236impl std::error::Error for Error {
237    #[inline]
238    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
239        self.0.source()
240    }
241
242    #[inline]
243    fn description(&self) -> &str {
244        #[allow(deprecated)]
245        self.0.description()
246    }
247
248    #[inline]
249    fn cause(&self) -> Option<&dyn std::error::Error> {
250        #[allow(deprecated)]
251        self.0.cause()
252    }
253}
254
255impl Serialize for Error {
256    #[inline]
257    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
258    where
259        S: serde::Serializer,
260    {
261        let string = self.0.to_string();
262        let mut tuple = serializer.serialize_tuple(2)?;
263        tuple.serialize_element(&string)?;
264        let kind: ErrorKind = self.0.kind().into();
265        tuple.serialize_element(&kind)?;
266        tuple.end()
267    }
268}
269
270impl<'de> Deserialize<'de> for Error {
271    #[inline]
272    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
273    where
274        D: serde::Deserializer<'de>,
275    {
276        let (error, kind) = <(String, ErrorKind)>::deserialize(deserializer)?;
277        Ok(Error(::std::io::Error::new(kind.into(), error)))
278    }
279}
280
281#[derive(Serialize_repr, Deserialize_repr)]
282#[repr(u8)]
283/// Serializable version of `std::io::ErrorKind`
284pub enum ErrorKind {
285    /// An entity was not found, often a file.
286    NotFound,
287    /// The operation lacked the necessary privileges to complete.
288    PermissionDenied,
289    /// The connection was refused by the remote server.
290    ConnectionRefused,
291    /// The connection was reset by the remote server.
292    ConnectionReset,
293    /// The remote host is not reachable.
294    HostUnreachable,
295    /// The network containing the remote host is not reachable.
296    NetworkUnreachable,
297    /// The connection was aborted (terminated) by the remote server.
298    ConnectionAborted,
299    /// The network operation failed because it was not connected yet.
300    NotConnected,
301    /// A socket address could not be bound because the address is already in
302    /// use elsewhere.
303    AddrInUse,
304    /// A nonexistent interface was requested or the requested address was not
305    /// local.
306    AddrNotAvailable,
307    /// The system's networking is down.
308    NetworkDown,
309    /// The operation failed because a pipe was closed.
310    BrokenPipe,
311    /// An entity already exists, often a file.
312    AlreadyExists,
313    /// The operation needs to block to complete, but the blocking operation was
314    /// requested to not occur.
315    WouldBlock,
316    /// A filesystem object is, unexpectedly, not a directory.
317    ///
318    /// For example, a filesystem path was specified where one of the intermediate directory
319    /// components was, in fact, a plain file.
320    NotADirectory,
321    /// The filesystem object is, unexpectedly, a directory.
322    ///
323    /// A directory was specified when a non-directory was expected.
324    IsADirectory,
325    /// A non-empty directory was specified where an empty directory was expected.
326    DirectoryNotEmpty,
327    /// The filesystem or storage medium is read-only, but a write operation was attempted.
328    ReadOnlyFilesystem,
329    /// Loop in the filesystem or IO subsystem; often, too many levels of symbolic links.
330    ///
331    /// There was a loop (or excessively long chain) resolving a filesystem object
332    /// or file IO object.
333    ///
334    /// On Unix this is usually the result of a symbolic link loop; or, of exceeding the
335    /// system-specific limit on the depth of symlink traversal.
336    FilesystemLoop,
337    /// Stale network file handle.
338    ///
339    /// With some network filesystems, notably NFS, an open file (or directory) can be invalidated
340    /// by problems with the network or server.
341    StaleNetworkFileHandle,
342    /// A parameter was incorrect.
343    InvalidInput,
344    /// Data not valid for the operation were encountered.
345    ///
346    /// Unlike [`InvalidInput`], this typically means that the operation
347    /// parameters were valid, however the error was caused by malformed
348    /// input data.
349    ///
350    /// For example, a function that reads a file into a string will error with
351    /// `InvalidData` if the file's contents are not valid UTF-8.
352    ///
353    /// [`InvalidInput`]: ErrorKind::InvalidInput
354    InvalidData,
355    /// The I/O operation's timeout expired, causing it to be canceled.
356    TimedOut,
357    /// An error returned when an operation could not be completed because a
358    /// call to [`write`] returned [`Ok(0)`].
359    ///
360    /// This typically means that an operation could only succeed if it wrote a
361    /// particular number of bytes but only a smaller number of bytes could be
362    /// written.
363    ///
364    /// [`write`]: crate::io::Write::write
365    /// [`Ok(0)`]: Ok
366    WriteZero,
367    /// The underlying storage (typically, a filesystem) is full.
368    ///
369    /// This does not include out of quota errors.
370    StorageFull,
371    /// Seek on unseekable file.
372    ///
373    /// Seeking was attempted on an open file handle which is not suitable for seeking - for
374    /// example, on Unix, a named pipe opened with `File::open`.
375    NotSeekable,
376    /// Filesystem quota was exceeded.
377    FilesystemQuotaExceeded,
378    /// File larger than allowed or supported.
379    ///
380    /// This might arise from a hard limit of the underlying filesystem or file access API, or from
381    /// an administratively imposed resource limitation.  Simple disk full, and out of quota, have
382    /// their own errors.
383    FileTooLarge,
384    /// Resource is busy.
385    ResourceBusy,
386    /// Executable file is busy.
387    ///
388    /// An attempt was made to write to a file which is also in use as a running program.  (Not all
389    /// operating systems detect this situation.)
390    ExecutableFileBusy,
391    /// Deadlock (avoided).
392    ///
393    /// A file locking operation would result in deadlock.  This situation is typically detected, if
394    /// at all, on a best-effort basis.
395    Deadlock,
396    /// Cross-device or cross-filesystem (hard) link or rename.
397    CrossesDevices,
398    /// Too many (hard) links to the same filesystem object.
399    ///
400    /// The filesystem does not support making so many hardlinks to the same file.
401    TooManyLinks,
402    /// Filename too long.
403    ///
404    /// The limit might be from the underlying filesystem or API, or an administratively imposed
405    /// resource limit.
406    FilenameTooLong,
407    /// Program argument list too long.
408    ///
409    /// When trying to run an external program, a system or process limit on the size of the
410    /// arguments would have been exceeded.
411    ArgumentListTooLong,
412    /// This operation was interrupted.
413    ///
414    /// Interrupted operations can typically be retried.
415    Interrupted,
416    /// This operation is unsupported on this platform.
417    ///
418    /// This means that the operation can never succeed.
419    Unsupported,
420    // ErrorKinds which are primarily categorisations for OS error
421    // codes should be added above.
422    //
423    /// An error returned when an operation could not be completed because an
424    /// "end of file" was reached prematurely.
425    ///
426    /// This typically means that an operation could only succeed if it read a
427    /// particular number of bytes but only a smaller number of bytes could be
428    /// read.
429    UnexpectedEof,
430    /// An operation could not be completed, because it failed
431    /// to allocate enough memory.
432    OutOfMemory,
433    // "Unusual" error kinds which do not correspond simply to (sets
434    // of) OS error codes, should be added just above this comment.
435    // `Other` and `Uncategorised` should remain at the end:
436    //
437    /// A custom error that does not fall under any other I/O error kind.
438    ///
439    /// This can be used to construct your own [`Error`]s that do not match any
440    /// [`ErrorKind`].
441    ///
442    /// This [`ErrorKind`] is not used by the standard library.
443    ///
444    /// Errors from the standard library that do not fall under any of the I/O
445    /// error kinds cannot be `match`ed on, and will only match a wildcard (`_`) pattern.
446    /// New [`ErrorKind`]s might be added in the future for some of those.
447    Other,
448    /// Any I/O error from the standard library that's not part of this list.
449    ///
450    /// Errors that are `Uncategorized` now may move to a different or a new
451    /// [`ErrorKind`] variant in the future. It is not recommended to match
452    /// an error against `Uncategorized`; use a wildcard match (`_`) instead.
453    Uncategorized,
454}
455
456impl From<ErrorKind> for StdErrorKind {
457    #[inline(always)]
458    fn from(kind: ErrorKind) -> Self {
459        match kind {
460            ErrorKind::NotFound => StdErrorKind::NotFound,
461            ErrorKind::PermissionDenied => StdErrorKind::PermissionDenied,
462            ErrorKind::ConnectionRefused => StdErrorKind::ConnectionRefused,
463            ErrorKind::ConnectionReset => StdErrorKind::ConnectionReset,
464            ErrorKind::ConnectionAborted => StdErrorKind::ConnectionAborted,
465            ErrorKind::NotConnected => StdErrorKind::NotConnected,
466            ErrorKind::AddrInUse => StdErrorKind::AddrInUse,
467            ErrorKind::AddrNotAvailable => StdErrorKind::AddrNotAvailable,
468            ErrorKind::BrokenPipe => StdErrorKind::BrokenPipe,
469            ErrorKind::AlreadyExists => StdErrorKind::AlreadyExists,
470            ErrorKind::WouldBlock => StdErrorKind::WouldBlock,
471            ErrorKind::InvalidInput => StdErrorKind::InvalidInput,
472            ErrorKind::InvalidData => StdErrorKind::InvalidData,
473            ErrorKind::TimedOut => StdErrorKind::TimedOut,
474            ErrorKind::WriteZero => StdErrorKind::WriteZero,
475            ErrorKind::Interrupted => StdErrorKind::Interrupted,
476            ErrorKind::Unsupported => StdErrorKind::Unsupported,
477            ErrorKind::UnexpectedEof => StdErrorKind::UnexpectedEof,
478            ErrorKind::OutOfMemory => StdErrorKind::OutOfMemory,
479            ErrorKind::Other => StdErrorKind::Other,
480            _ => StdErrorKind::Other,
481        }
482    }
483}
484
485impl From<StdErrorKind> for ErrorKind {
486    #[inline(always)]
487    fn from(kind: StdErrorKind) -> Self {
488        match kind {
489            StdErrorKind::NotFound => ErrorKind::NotFound,
490            StdErrorKind::PermissionDenied => ErrorKind::PermissionDenied,
491            StdErrorKind::ConnectionRefused => ErrorKind::ConnectionRefused,
492            StdErrorKind::ConnectionReset => ErrorKind::ConnectionReset,
493            StdErrorKind::ConnectionAborted => ErrorKind::ConnectionAborted,
494            StdErrorKind::NotConnected => ErrorKind::NotConnected,
495            StdErrorKind::AddrInUse => ErrorKind::AddrInUse,
496            StdErrorKind::AddrNotAvailable => ErrorKind::AddrNotAvailable,
497            StdErrorKind::BrokenPipe => ErrorKind::BrokenPipe,
498            StdErrorKind::AlreadyExists => ErrorKind::AlreadyExists,
499            StdErrorKind::WouldBlock => ErrorKind::WouldBlock,
500            StdErrorKind::InvalidInput => ErrorKind::InvalidInput,
501            StdErrorKind::InvalidData => ErrorKind::InvalidData,
502            StdErrorKind::TimedOut => ErrorKind::TimedOut,
503            StdErrorKind::WriteZero => ErrorKind::WriteZero,
504            StdErrorKind::Interrupted => ErrorKind::Interrupted,
505            StdErrorKind::Unsupported => ErrorKind::Unsupported,
506            StdErrorKind::UnexpectedEof => ErrorKind::UnexpectedEof,
507            StdErrorKind::OutOfMemory => ErrorKind::OutOfMemory,
508            StdErrorKind::Other => ErrorKind::Other,
509            _ => ErrorKind::Other,
510        }
511    }
512}