]> git.proxmox.com Git - proxmox.git/blame - proxmox-sys/src/error.rs
move io error helpers to proxmox-lang
[proxmox.git] / proxmox-sys / src / error.rs
CommitLineData
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
16use std::io;
17
18use nix::errno::Errno;
19use nix::Error;
20
d4b41154 21use 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/// ```
51pub 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
79impl 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
91impl 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/// ```
126pub trait SysResult {
127 type Ok;
128
129 fn into_io_result(self) -> io::Result<Self::Ok>;
130}
131
132impl<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
141macro_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
154other_error!(std::char::ParseCharError);
155other_error!(std::net::AddrParseError);
156other_error!(std::num::ParseFloatError);
157other_error!(std::num::ParseIntError);
158other_error!(std::str::ParseBoolError);
159other_error!(std::str::Utf8Error);
160other_error!(std::string::FromUtf8Error);