use std::fmt;
pub const STATUS_TRANSPORT: u32 = u32::MAX;
pub(crate) const NFS_REFERRAL_UNSUPPORTED: &str = "NFSv4 referrals and filesystem migration are unsupported; connect directly to the destination server/export";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum TransportKind {
Timeout,
Connection,
InvalidReply,
Auth,
Other,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RpcError {
pub op_index: usize,
pub status: u32,
pub message: String,
pub transport_kind: Option<TransportKind>,
}
impl RpcError {
pub fn op(op_index: usize, status: u32) -> RpcError {
RpcError {
op_index,
status,
message: String::new(),
transport_kind: None,
}
}
pub fn transport(message: impl Into<String>) -> RpcError {
Self::transport_with_kind(TransportKind::Other, message)
}
pub fn transport_with_kind(kind: TransportKind, message: impl Into<String>) -> RpcError {
RpcError {
op_index: 0,
status: STATUS_TRANSPORT,
message: message.into(),
transport_kind: Some(kind),
}
}
pub fn from_io(error: std::io::Error) -> Self {
use std::io::ErrorKind as K;
let kind = match error.kind() {
K::TimedOut => TransportKind::Timeout,
K::ConnectionRefused
| K::ConnectionReset
| K::ConnectionAborted
| K::NotConnected
| K::BrokenPipe => TransportKind::Connection,
_ => TransportKind::Other,
};
Self::transport_with_kind(kind, error.to_string())
}
pub fn is_transport(&self) -> bool {
self.status == STATUS_TRANSPORT
}
pub fn with_op_index(self, index: usize) -> RpcError {
RpcError {
op_index: index,
..self
}
}
}
impl fmt::Display for RpcError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_transport() {
write!(f, "transport error: {}", self.message)
} else {
write!(f, "op {} failed: status {}", self.op_index, self.status)?;
if self.status == 10019 {
write!(f, "; {NFS_REFERRAL_UNSUPPORTED}")?;
}
Ok(())
}
}
}
impl std::error::Error for RpcError {}
pub type RpcResult<T> = Result<T, RpcError>;