use std::error;
use std::fmt;
use std::io;
use std::path::{Path, PathBuf};
use crate::{ClientState, DirEntry};
#[derive(Debug)]
pub struct Error {
depth: usize,
inner: ErrorInner,
}
#[derive(Debug)]
enum ErrorInner {
Io {
path: Option<PathBuf>,
err: io::Error,
},
Loop {
ancestor: PathBuf,
child: PathBuf,
},
ThreadpoolBusy,
}
impl Error {
pub fn path(&self) -> Option<&Path> {
match self.inner {
ErrorInner::ThreadpoolBusy => None,
ErrorInner::Io { path: None, .. } => None,
ErrorInner::Io {
path: Some(ref path),
..
} => Some(path),
ErrorInner::Loop { ref child, .. } => Some(child),
}
}
pub fn loop_ancestor(&self) -> Option<&Path> {
match self.inner {
ErrorInner::Loop { ref ancestor, .. } => Some(ancestor),
_ => None,
}
}
pub fn depth(&self) -> usize {
self.depth
}
pub fn io_error(&self) -> Option<&io::Error> {
match self.inner {
ErrorInner::Io { ref err, .. } => Some(err),
_ => None,
}
}
pub fn is_busy(&self) -> bool {
matches!(self.inner, ErrorInner::ThreadpoolBusy)
}
pub fn into_io_error(self) -> Option<io::Error> {
match self.inner {
ErrorInner::Io { err, .. } => Some(err),
_ => None,
}
}
pub(crate) fn busy() -> Self {
Error {
depth: 0,
inner: ErrorInner::ThreadpoolBusy,
}
}
pub(crate) fn from_path(depth: usize, pb: PathBuf, err: io::Error) -> Self {
Error {
depth,
inner: ErrorInner::Io {
path: Some(pb),
err,
},
}
}
pub(crate) fn from_entry<C: ClientState>(dent: &DirEntry<C>, err: io::Error) -> Self {
Error {
depth: dent.depth(),
inner: ErrorInner::Io {
path: Some(dent.path()),
err,
},
}
}
pub(crate) fn from_io(depth: usize, err: io::Error) -> Self {
Error {
depth,
inner: ErrorInner::Io { path: None, err },
}
}
pub(crate) fn from_loop(depth: usize, ancestor: &Path, child: &Path) -> Self {
Error {
depth,
inner: ErrorInner::Loop {
ancestor: ancestor.to_path_buf(),
child: child.to_path_buf(),
},
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self.inner {
ErrorInner::Io { ref err, .. } => Some(err),
ErrorInner::Loop { .. } | ErrorInner::ThreadpoolBusy => None,
}
}
#[allow(deprecated)]
fn description(&self) -> &str {
match self.inner {
ErrorInner::Io { ref err, .. } => err.description(),
ErrorInner::Loop { .. } => "file system loop found",
ErrorInner::ThreadpoolBusy => "thread-pool busy",
}
}
fn cause(&self) -> Option<&dyn error::Error> {
self.source()
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.inner {
ErrorInner::ThreadpoolBusy => f.write_str("rayon thread-pool too busy or dependency loop detected - aborting before possibility of deadlock"),
ErrorInner::Io {
path: None,
ref err,
} => err.fmt(f),
ErrorInner::Io {
path: Some(ref path),
ref err,
} => write!(f, "IO error for operation on {}: {}", path.display(), err),
ErrorInner::Loop {
ref ancestor,
ref child,
} => write!(
f,
"File system loop found: \
{} points to an ancestor {}",
child.display(),
ancestor.display()
),
}
}
}
impl From<Error> for io::Error {
fn from(walk_err: Error) -> io::Error {
let kind = match walk_err {
Error {
inner: ErrorInner::Io { ref err, .. },
..
} => err.kind(),
Error {
inner: ErrorInner::Loop { .. },
..
} => io::ErrorKind::Other,
Error {
inner: ErrorInner::ThreadpoolBusy,
..
} => io::ErrorKind::Other,
};
io::Error::new(kind, walk_err)
}
}