#[cfg(feature = "use_libc")]
use libc;
use std::fmt::Display;
use std::num::NonZeroI32;
use strum;
#[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 | libc::ENOMEDIUM | libc::ENXIO | libc::ESRCH => Some(Self::NotFound),
libc::EEXIST | libc::EADDRNOTAVAIL | libc::EALREADY | libc::ENOTUNIQ => Some(Self::AlreadyExists),
libc::EPERM | libc::EACCES | libc::ENOKEY | libc::EROFS => Some(Self::PermissionDenied),
libc::ENOTEMPTY | libc::EISDIR | libc::ENOTDIR | libc::EADDRINUSE | libc::EBADF | libc::EBADFD | libc::EBUSY | libc::ECHILD | libc::EISCONN | libc::EISNAM | libc::ENOTBLK | libc::ENOTCONN | libc::EPIPE | libc::ESHUTDOWN | libc::ETXTBSY | libc::EUNATCH => Some(Self::FailedPrecondition),
libc::ENOSPC | libc::EDQUOT | libc::EMFILE | libc::EMLINK | libc::ENFILE | libc::ENOBUFS | libc::ENOMEM | libc::EUSERS => Some(Self::ResourceExhausted),
libc::ECHRNG | libc::EFBIG | libc::EOVERFLOW | libc::ERANGE => Some(Self::OutOfRange),
libc::ENOPKG | libc::ENOSYS | libc::ENOTSUP | libc::EAFNOSUPPORT | libc::EPFNOSUPPORT | libc::EPROTONOSUPPORT | libc::ESOCKTNOSUPPORT | libc::EXDEV => Some(Self::Unimplemented),
libc::EAGAIN | libc::ECOMM | libc::ECONNREFUSED | libc::ECONNABORTED | libc::ECONNRESET | libc::EINTR | libc::EHOSTDOWN | libc::EHOSTUNREACH | libc::ENETDOWN | libc::ENETRESET | libc::ENETUNREACH | libc::ENOLCK | libc::ENOLINK | libc::ENONET => Some(Self::Unavailable),
libc::EDEADLK | libc::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)
);
}
}