#[cfg(feature = "use_libc")]
use libc;
use std::fmt::Display;
use std::num::NonZeroI32;
use strum;
#[cfg(feature = "use_libc")]
mod compat_errno {
use libc::c_int;
macro_rules! linux_errno {
($($name:ident = $sentinel:literal,)*) => {$(
#[cfg(any(target_os = "linux", target_os = "android"))]
pub const $name: c_int = libc::$name;
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub const $name: c_int = -$sentinel;
)*};
}
macro_rules! unix_errno {
($($name:ident = $sentinel:literal,)*) => {$(
#[cfg(unix)]
pub const $name: c_int = libc::$name;
#[cfg(not(unix))]
pub const $name: c_int = -$sentinel;
)*};
}
linux_errno! {
ENOMEDIUM = 1001,
ENOTUNIQ = 1002,
ENOKEY = 1003,
EBADFD = 1004,
EISNAM = 1005,
EUNATCH = 1006,
ECHRNG = 1007,
ENOPKG = 1008,
ECOMM = 1009,
ENONET = 1010,
}
unix_errno! {
ENOTBLK = 1101,
ESHUTDOWN = 1102,
EDQUOT = 1103,
EUSERS = 1104,
EPFNOSUPPORT = 1105,
ESOCKTNOSUPPORT = 1106,
EHOSTDOWN = 1107,
ESTALE = 1108,
}
}
#[derive(
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
strum::EnumString,
strum::EnumIter,
strum::FromRepr,
strum::IntoStaticStr,
strum::VariantArray,
)]
#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
#[repr(i32)]
#[non_exhaustive]
pub enum ErrorCode {
Cancelled = 1,
Unknown = 2,
InvalidArgument = 3,
DeadlineExceeded = 4,
NotFound = 5,
AlreadyExists = 6,
PermissionDenied = 7,
ResourceExhausted = 8,
FailedPrecondition = 9,
Aborted = 10,
OutOfRange = 11,
Unimplemented = 12,
Internal = 13,
Unavailable = 14,
DataLoss = 15,
Unauthenticated = 16,
}
impl ErrorCode {
#[cfg(feature = "use_libc")]
pub fn from_errno(errno: i32) -> Option<ErrorCode> {
match errno {
0 => None,
libc::EINVAL | libc::ENAMETOOLONG | libc::E2BIG | libc::EDESTADDRREQ | libc::EDOM | libc::EFAULT | libc::EILSEQ | libc::ENOPROTOOPT | libc::ENOTSOCK | libc::ENOTTY | libc::EPROTOTYPE | libc::ESPIPE => Some(Self::InvalidArgument),
libc::ETIMEDOUT => Some(Self::DeadlineExceeded),
libc::ENODEV | libc::ENOENT | compat_errno::ENOMEDIUM | libc::ENXIO | libc::ESRCH => Some(Self::NotFound),
libc::EEXIST | libc::EADDRNOTAVAIL | libc::EALREADY | compat_errno::ENOTUNIQ => Some(Self::AlreadyExists),
libc::EPERM | libc::EACCES | compat_errno::ENOKEY | libc::EROFS => Some(Self::PermissionDenied),
libc::ENOTEMPTY | libc::EISDIR | libc::ENOTDIR | libc::EADDRINUSE | libc::EBADF | compat_errno::EBADFD | libc::EBUSY | libc::ECHILD | libc::EISCONN | compat_errno::EISNAM | compat_errno::ENOTBLK | libc::ENOTCONN | libc::EPIPE | compat_errno::ESHUTDOWN | libc::ETXTBSY | compat_errno::EUNATCH => Some(Self::FailedPrecondition),
libc::ENOSPC | compat_errno::EDQUOT | libc::EMFILE | libc::EMLINK | libc::ENFILE | libc::ENOBUFS | libc::ENOMEM | compat_errno::EUSERS => Some(Self::ResourceExhausted),
compat_errno::ECHRNG | libc::EFBIG | libc::EOVERFLOW | libc::ERANGE => Some(Self::OutOfRange),
compat_errno::ENOPKG | libc::ENOSYS | libc::ENOTSUP | libc::EAFNOSUPPORT | compat_errno::EPFNOSUPPORT | libc::EPROTONOSUPPORT | compat_errno::ESOCKTNOSUPPORT | libc::EXDEV => Some(Self::Unimplemented),
libc::EAGAIN | compat_errno::ECOMM | libc::ECONNREFUSED | libc::ECONNABORTED | libc::ECONNRESET | libc::EINTR | compat_errno::EHOSTDOWN | libc::EHOSTUNREACH | libc::ENETDOWN | libc::ENETRESET | libc::ENETUNREACH | libc::ENOLCK | libc::ENOLINK | compat_errno::ENONET => Some(Self::Unavailable),
libc::EDEADLK | compat_errno::ESTALE => Some(Self::Aborted),
libc::ECANCELED => Some(Self::Cancelled),
_ => Some(Self::Unknown),
}
}
pub fn from_error_kind(kind: std::io::ErrorKind) -> ErrorCode {
use std::io::ErrorKind::*;
match kind {
NotFound => ErrorCode::NotFound,
PermissionDenied => ErrorCode::PermissionDenied,
AlreadyExists => ErrorCode::AlreadyExists,
InvalidInput | ArgumentListTooLong => ErrorCode::InvalidArgument,
TimedOut => ErrorCode::DeadlineExceeded,
StorageFull | QuotaExceeded | OutOfMemory => ErrorCode::ResourceExhausted,
DirectoryNotEmpty | IsADirectory | NotADirectory | NotConnected | AddrInUse
| ResourceBusy | ExecutableFileBusy | ReadOnlyFilesystem | CrossesDevices | NotSeekable => ErrorCode::FailedPrecondition,
FileTooLarge => ErrorCode::OutOfRange,
Unsupported => ErrorCode::Unimplemented,
WouldBlock | Interrupted | BrokenPipe | ConnectionRefused | ConnectionReset
| ConnectionAborted | HostUnreachable | NetworkUnreachable | NetworkDown => {
ErrorCode::Unavailable
}
Deadlock | StaleNetworkFileHandle => ErrorCode::Aborted,
InvalidData | WriteZero | UnexpectedEof => ErrorCode::DataLoss,
Other => ErrorCode::Unknown,
_ => ErrorCode::Unknown,
}
}
#[cfg(feature = "use_libc")]
pub fn from_raw_os_error(err: &std::io::Error) -> Option<ErrorCode> {
err.raw_os_error().and_then(Self::from_errno)
}
}
impl From<ErrorCode> for NonZeroI32 {
fn from(code: ErrorCode) -> Self {
NonZeroI32::new(code as i32).expect(&format!(
"The enum value of an ErrorCode must be nonzero, but got '{:?}'",
code
))
}
}
impl TryFrom<i32> for ErrorCode {
type Error = ();
fn try_from(code: i32) -> Result<Self, ()> {
Self::from_repr(code).ok_or(())
}
}
impl Display for ErrorCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.alternate() {
write!(f, "{}", *self as i32)
} else {
f.write_str(self.into())
}
}
}
#[cfg(test)]
mod thin_status_tests {
use super::*;
use anyhow;
#[cfg(feature = "use_libc")]
#[test]
fn test_cloud_rpc_status_conversions() {
assert_eq!(ErrorCode::from_errno(0), None);
assert_eq!(
ErrorCode::from_errno(libc::ENOENT),
Some(ErrorCode::NotFound)
);
}
#[test]
fn test_to_string() {
assert_eq!(format!("{}", ErrorCode::NotFound), "NOT_FOUND");
assert_eq!(format!("{:#}", ErrorCode::NotFound), "5");
}
#[test]
fn test_anyhow_context() {
let err = anyhow::anyhow!("test error").context(ErrorCode::PermissionDenied);
assert_eq!(
err.downcast_ref::<ErrorCode>(),
Some(&ErrorCode::PermissionDenied)
);
}
}