]>
Commit | Line | Data |
---|---|---|
b659584f WB |
1 | //! Helpers for `io::Error` and `nix::Error`. |
2 | //! | |
3 | //! When dealing with low level functionality, the `nix` crate contains a lot of helpful | |
4 | //! functionality. Unfortunately it also contains its own error type which doesn't mix all too well | |
5 | //! with `std::io::Error`. Some of the common cases we want to deal with is checking for `EAGAIN`, | |
6 | //! `EWOULDBLOCK` or `ENOENT` specifically. To do this easily, we add the `SysError` trait (rather | |
7 | //! than a type), which is implemented for both these errors and allows checking for the most | |
8 | //! common errors in a unified way. | |
9 | //! | |
10 | //! Another problem with the `nix` error type is that it has no equivalent to `ErrorKind::Other`. | |
11 | //! Unfortunately this is more difficult to deal with, so for now, we consider `io::Error` to be | |
12 | //! the more general error (and require an `into_io_error` implementation in `SysError`). | |
13 | //! | |
14 | //! See the `SysError` and `SysResult` traits for examples. | |
15 | ||
16 | use std::io; | |
17 | ||
18 | use nix::errno::Errno; | |
19 | use nix::Error; | |
20 | ||
d4b41154 | 21 | use proxmox_lang::error::io_err_other; |
b659584f WB |
22 | |
23 | /// This trait should be implemented for error types which can represent system errors. Note that | |
24 | /// it is discouraged to try to map non-system errors to with this trait, since users of this trait | |
25 | /// assume there to be a relation between the error code and a previous system call. For instance, | |
26 | /// `error.is_errno(Errno::EAGAIN)` can be used when implementing a reactor to drive async I/O. | |
27 | /// | |
28 | /// Usage examples: | |
29 | /// | |
30 | /// ``` | |
5dd21ee8 | 31 | /// # use anyhow::{bail, Error}; |
b659584f WB |
32 | /// use nix::{dir::Dir, fcntl::OFlag, sys::stat::Mode}; |
33 | /// | |
ec3965fd | 34 | /// use proxmox_sys::error::SysError; |
b659584f WB |
35 | /// |
36 | /// # fn test() -> Result<(), Error> { | |
37 | /// | |
38 | /// match Dir::open(".", OFlag::O_RDONLY, Mode::empty()) { | |
39 | /// Ok(_dir) => { | |
40 | /// // Do something | |
41 | /// } | |
42 | /// Err(ref err) if err.not_found() => { | |
43 | /// // Handle ENOENT specially | |
44 | /// } | |
45 | /// Err(err) => bail!("failed to open directory: {}", err), | |
46 | /// } | |
47 | /// | |
48 | /// # Ok(()) | |
49 | /// # } | |
50 | /// ``` | |
51 | pub trait SysError { | |
52 | /// Check if this error is a specific error returned from a system call. | |
341795c1 | 53 | fn is_errno(&self, value: Errno) -> bool; |
b659584f WB |
54 | |
55 | /// Convert this error into a `std::io::Error`. This must use the correct `std::io::ErrorKind`, | |
56 | /// so that for example `ErrorKind::WouldBlock` means that the previous system call failed with | |
57 | /// `EAGAIN`. | |
58 | fn into_io_error(self) -> io::Error; | |
59 | ||
60 | /// Convenience shortcut to check for `EAGAIN`. | |
61 | #[inline] | |
62 | fn would_block(&self) -> bool { | |
63 | self.is_errno(Errno::EAGAIN) | |
64 | } | |
65 | ||
66 | /// Convenience shortcut to check for `ENOENT`. | |
67 | #[inline] | |
68 | fn not_found(&self) -> bool { | |
69 | self.is_errno(Errno::ENOENT) | |
70 | } | |
71 | ||
72 | /// Convenience shortcut to check for `EEXIST`. | |
73 | #[inline] | |
74 | fn already_exists(&self) -> bool { | |
75 | self.is_errno(Errno::EEXIST) | |
76 | } | |
77 | } | |
78 | ||
79 | impl SysError for io::Error { | |
80 | #[inline] | |
341795c1 WB |
81 | fn is_errno(&self, value: Errno) -> bool { |
82 | self.raw_os_error() == Some(value as i32) | |
b659584f WB |
83 | } |
84 | ||
85 | #[inline] | |
86 | fn into_io_error(self) -> io::Error { | |
87 | self | |
88 | } | |
89 | } | |
90 | ||
91 | impl SysError for nix::Error { | |
92 | #[inline] | |
93 | fn is_errno(&self, value: Errno) -> bool { | |
94 | *self == Error::Sys(value) | |
95 | } | |
96 | ||
97 | #[inline] | |
98 | fn into_io_error(self) -> io::Error { | |
99 | match self { | |
100 | Error::Sys(raw) => io::Error::from_raw_os_error(raw as _), | |
101 | other => io::Error::new(io::ErrorKind::Other, other.to_string()), | |
102 | } | |
103 | } | |
104 | } | |
105 | ||
106 | /// Convenience helper to map a `Result<_, nix::Error>` to a `Result<_, std::io::Error>` quickly. | |
107 | /// | |
108 | /// Usage example: | |
109 | /// | |
110 | /// ```no_run | |
111 | /// # use std::os::unix::io::RawFd; | |
5dd21ee8 | 112 | /// # use anyhow::{bail, Error}; |
b659584f | 113 | /// |
ec3965fd | 114 | /// use proxmox_sys::error::SysResult; |
b659584f WB |
115 | /// |
116 | /// struct MyReader(RawFd); | |
117 | /// | |
118 | /// impl std::io::Read for MyReader { | |
119 | /// fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { | |
120 | /// nix::unistd::read(self.0, buf).into_io_result() | |
121 | /// } | |
122 | /// | |
123 | /// // ... rest | |
124 | /// } | |
125 | /// ``` | |
126 | pub trait SysResult { | |
127 | type Ok; | |
128 | ||
129 | fn into_io_result(self) -> io::Result<Self::Ok>; | |
130 | } | |
131 | ||
132 | impl<T> SysResult for Result<T, nix::Error> { | |
133 | type Ok = T; | |
134 | ||
135 | #[inline] | |
136 | fn into_io_result(self) -> io::Result<T> { | |
137 | self.map_err(|e| e.into_io_error()) | |
138 | } | |
139 | } | |
140 | ||
141 | macro_rules! other_error { | |
142 | ($err:ty) => { | |
143 | impl<T> SysResult for Result<T, $err> { | |
144 | type Ok = T; | |
145 | ||
146 | #[inline] | |
147 | fn into_io_result(self) -> io::Result<T> { | |
32b69176 | 148 | self.map_err($crate::error::io_err_other) |
b659584f WB |
149 | } |
150 | } | |
151 | }; | |
152 | } | |
153 | ||
154 | other_error!(std::char::ParseCharError); | |
155 | other_error!(std::net::AddrParseError); | |
156 | other_error!(std::num::ParseFloatError); | |
157 | other_error!(std::num::ParseIntError); | |
158 | other_error!(std::str::ParseBoolError); | |
159 | other_error!(std::str::Utf8Error); | |
160 | other_error!(std::string::FromUtf8Error); |