1use std::io;
5
6#[derive(Debug, thiserror::Error)]
8pub enum FolderError {
9 #[error("absolute paths are not allowed, use a path relative to the root folder: {0}")]
11 Absolute(String),
12 #[error("path leaves the root folder and is refused: {0}")]
14 Escape(String),
15 #[error("not found: {0}")]
17 NotFound(String),
18 #[error("{0}")]
20 Exists(String),
21 #[error(
23 "the server is read-only: write and move are disabled (the operator starts it with --writable to allow them)"
24 )]
25 ReadOnly,
26 #[error("{path} is {size} bytes, over the {limit}-byte limit{hint}")]
28 TooLarge {
29 path: String,
31 size: u64,
33 limit: u64,
35 hint: &'static str,
37 },
38 #[error("{0}")]
40 Invalid(String),
41 #[error("{path}: {source}")]
43 Io {
44 path: String,
46 source: io::Error,
48 },
49}
50
51impl FolderError {
52 pub fn from_io(err: io::Error, path: &str) -> Self {
54 if err.kind() == io::ErrorKind::PermissionDenied
57 && err.to_string().contains("outside of the filesystem")
58 {
59 return Self::Escape(path.to_string());
60 }
61 match err.kind() {
62 io::ErrorKind::NotFound => Self::NotFound(path.to_string()),
63 io::ErrorKind::AlreadyExists => Self::Exists(format!("{path} already exists")),
64 _ => Self::Io {
65 path: path.to_string(),
66 source: err,
67 },
68 }
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn cap_std_escape_maps_to_escape() {
78 let e = io::Error::new(
79 io::ErrorKind::PermissionDenied,
80 "a path led outside of the filesystem",
81 );
82 assert!(matches!(
83 FolderError::from_io(e, "link"),
84 FolderError::Escape(_)
85 ));
86 }
87
88 #[test]
89 fn not_found_and_exists_are_classified() {
90 let nf = io::Error::from(io::ErrorKind::NotFound);
91 assert!(matches!(
92 FolderError::from_io(nf, "x"),
93 FolderError::NotFound(_)
94 ));
95 let ex = io::Error::from(io::ErrorKind::AlreadyExists);
96 assert!(
97 FolderError::from_io(ex, "x")
98 .to_string()
99 .contains("already exists")
100 );
101 }
102}