use std::{error, fmt, io};
#[derive(Debug)]
pub struct VfsError {
path: String,
kind: VfsErrorKind,
context: String,
cause: Option<Box<VfsError>>,
}
impl From<VfsErrorKind> for VfsError {
fn from(kind: VfsErrorKind) -> Self {
let kind = match kind {
VfsErrorKind::IoError(io) => match io.kind() {
io::ErrorKind::NotFound => VfsErrorKind::FileNotFound,
_ => VfsErrorKind::IoError(io),
},
other => other,
};
Self {
path: "PATH NOT FILLED BY VFS LAYER".into(),
kind,
context: "An error occured".into(),
cause: None,
}
}
}
impl From<io::Error> for VfsError {
fn from(err: io::Error) -> Self {
Self::from(VfsErrorKind::IoError(err))
}
}
impl VfsError {
pub(crate) fn with_path(mut self, path: impl Into<String>) -> Self {
self.path = path.into();
self
}
pub fn with_context<C, F>(mut self, context: F) -> Self
where
C: fmt::Display + Send + Sync + 'static,
F: FnOnce() -> C,
{
self.context = context().to_string();
self
}
pub fn with_cause(mut self, cause: VfsError) -> Self {
self.cause = Some(Box::new(cause));
self
}
pub fn kind(&self) -> &VfsErrorKind {
&self.kind
}
pub fn path(&self) -> &String {
&self.path
}
}
impl fmt::Display for VfsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} for '{}': {}", self.context, self.path, self.kind())
}
}
impl error::Error for VfsError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
if let Some(cause) = &self.cause {
Some(cause)
} else {
None
}
}
}
#[derive(Debug)]
pub enum VfsErrorKind {
IoError(io::Error),
FileNotFound,
InvalidPath,
Other(String),
DirectoryExists,
FileExists,
NotSupported,
}
impl fmt::Display for VfsErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
VfsErrorKind::IoError(cause) => {
write!(f, "IO error: {}", cause)
}
VfsErrorKind::FileNotFound => {
write!(f, "The file or directory could not be found")
}
VfsErrorKind::InvalidPath => {
write!(f, "The path is invalid")
}
VfsErrorKind::Other(message) => {
write!(f, "FileSystem error: {}", message)
}
VfsErrorKind::NotSupported => {
write!(f, "Functionality not supported by this filesystem")
}
VfsErrorKind::DirectoryExists => {
write!(f, "Directory already exists")
}
VfsErrorKind::FileExists => {
write!(f, "File already exists")
}
}
}
}
pub type VfsResult<T> = std::result::Result<T, VfsError>;
#[cfg(test)]
mod tests {
use crate::error::VfsErrorKind;
use crate::{VfsError, VfsResult};
fn produce_vfs_result() -> VfsResult<()> {
Err(VfsError::from(VfsErrorKind::Other("Not a file".into())).with_path("foo"))
}
fn produce_anyhow_result() -> anyhow::Result<()> {
Ok(produce_vfs_result()?)
}
#[test]
fn anyhow_compatibility() {
let result = produce_anyhow_result().unwrap_err();
assert_eq!(
result.to_string(),
"An error occured for 'foo': FileSystem error: Not a file"
)
}
}