use ninep_client::ClientError;
#[derive(Debug, Clone)]
pub enum ZeroFsError {
NotFound {
path: String,
},
PermissionDenied {
path: String,
},
NotPermitted {
path: String,
},
AlreadyExists {
path: String,
},
NotADirectory {
path: String,
},
IsADirectory {
path: String,
},
DirectoryNotEmpty {
path: String,
},
NameTooLong {
name: String,
},
InvalidArgument {
message: String,
},
TooManySymlinks {
path: String,
},
Closed,
ConnectFailed {
message: String,
},
Io {
errno: i32,
path: String,
message: String,
},
Protocol {
message: String,
},
}
impl std::fmt::Display for ZeroFsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NotFound { path } => write!(f, "not found: {path}"),
Self::PermissionDenied { path } => write!(f, "permission denied: {path}"),
Self::NotPermitted { path } => write!(f, "operation not permitted: {path}"),
Self::AlreadyExists { path } => write!(f, "already exists: {path}"),
Self::NotADirectory { path } => write!(f, "not a directory: {path}"),
Self::IsADirectory { path } => write!(f, "is a directory: {path}"),
Self::DirectoryNotEmpty { path } => write!(f, "directory not empty: {path}"),
Self::NameTooLong { name } => write!(f, "name too long: {name}"),
Self::InvalidArgument { message } => write!(f, "invalid argument: {message}"),
Self::TooManySymlinks { path } => {
write!(f, "too many levels of symbolic links: {path}")
}
Self::Closed => write!(f, "handle is closed"),
Self::ConnectFailed { message } => write!(f, "connection failed: {message}"),
Self::Io {
errno,
path,
message,
} => write!(f, "i/o error (errno {errno}): {path}: {message}"),
Self::Protocol { message } => write!(f, "protocol error: {message}"),
}
}
}
impl std::error::Error for ZeroFsError {}
impl From<ZeroFsError> for std::io::Error {
fn from(e: ZeroFsError) -> Self {
let kind = std::io::Error::from_raw_os_error(e.to_errno()).kind();
std::io::Error::new(kind, e)
}
}
impl ZeroFsError {
pub fn to_errno(&self) -> i32 {
match self {
Self::NotFound { .. } => libc::ENOENT,
Self::PermissionDenied { .. } => libc::EACCES,
Self::NotPermitted { .. } => libc::EPERM,
Self::AlreadyExists { .. } => libc::EEXIST,
Self::NotADirectory { .. } => libc::ENOTDIR,
Self::IsADirectory { .. } => libc::EISDIR,
Self::DirectoryNotEmpty { .. } => libc::ENOTEMPTY,
Self::NameTooLong { .. } => libc::ENAMETOOLONG,
Self::InvalidArgument { .. } => libc::EINVAL,
Self::TooManySymlinks { .. } => libc::ELOOP,
Self::Closed => libc::EBADF,
Self::ConnectFailed { .. } => libc::EIO,
Self::Io { errno, .. } => *errno,
Self::Protocol { .. } => libc::EIO,
}
}
pub(crate) fn from_errno(errno: i32, path: &str) -> Self {
let path = path.to_string();
match errno {
libc::ENOENT => Self::NotFound { path },
libc::EACCES => Self::PermissionDenied { path },
libc::EPERM => Self::NotPermitted { path },
libc::EEXIST => Self::AlreadyExists { path },
libc::ENOTDIR => Self::NotADirectory { path },
libc::EISDIR => Self::IsADirectory { path },
libc::ENOTEMPTY => Self::DirectoryNotEmpty { path },
libc::ENAMETOOLONG => Self::NameTooLong { name: path },
libc::EINVAL => Self::InvalidArgument {
message: format!("{path}: invalid argument"),
},
libc::ELOOP => Self::TooManySymlinks { path },
errno => Self::Io {
message: std::io::Error::from_raw_os_error(errno).to_string(),
errno,
path,
},
}
}
pub(crate) fn from_client(e: &ClientError, path: &str) -> Self {
match e {
ClientError::Errno(code) => Self::from_errno(*code as i32, path),
other => Self::Protocol {
message: format!("{path}: {other}"),
},
}
}
}
pub(crate) trait ClientResultExt<T> {
fn ctx(self, path: &str) -> Result<T, ZeroFsError>;
}
impl<T> ClientResultExt<T> for Result<T, ClientError> {
fn ctx(self, path: &str) -> Result<T, ZeroFsError> {
self.map_err(|e| ZeroFsError::from_client(&e, path))
}
}