use std::io;
#[derive(Debug, thiserror::Error)]
pub enum FolderError {
#[error("absolute paths are not allowed, use a path relative to the root folder: {0}")]
Absolute(String),
#[error("path leaves the root folder and is refused: {0}")]
Escape(String),
#[error("not found: {0}")]
NotFound(String),
#[error("{0}")]
Exists(String),
#[error(
"the server is read-only: write and move are disabled (the operator starts it with --writable to allow them)"
)]
ReadOnly,
#[error("{path} is {size} bytes, over the {limit}-byte limit{hint}")]
TooLarge {
path: String,
size: u64,
limit: u64,
hint: &'static str,
},
#[error("{0}")]
Invalid(String),
#[error("{path}: {source}")]
Io {
path: String,
source: io::Error,
},
}
impl FolderError {
pub fn from_io(err: io::Error, path: &str) -> Self {
if err.kind() == io::ErrorKind::PermissionDenied
&& err.to_string().contains("outside of the filesystem")
{
return Self::Escape(path.to_string());
}
match err.kind() {
io::ErrorKind::NotFound => Self::NotFound(path.to_string()),
io::ErrorKind::AlreadyExists => Self::Exists(format!("{path} already exists")),
_ => Self::Io {
path: path.to_string(),
source: err,
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cap_std_escape_maps_to_escape() {
let e = io::Error::new(
io::ErrorKind::PermissionDenied,
"a path led outside of the filesystem",
);
assert!(matches!(
FolderError::from_io(e, "link"),
FolderError::Escape(_)
));
}
#[test]
fn not_found_and_exists_are_classified() {
let nf = io::Error::from(io::ErrorKind::NotFound);
assert!(matches!(
FolderError::from_io(nf, "x"),
FolderError::NotFound(_)
));
let ex = io::Error::from(io::ErrorKind::AlreadyExists);
assert!(
FolderError::from_io(ex, "x")
.to_string()
.contains("already exists")
);
}
}