1use ninep_client::ClientError;
2
3#[derive(Debug, Clone)]
15pub enum ZeroFsError {
16 NotFound {
18 path: String,
20 },
21 PermissionDenied {
23 path: String,
25 },
26 NotPermitted {
29 path: String,
31 },
32 AlreadyExists {
34 path: String,
36 },
37 NotADirectory {
39 path: String,
41 },
42 IsADirectory {
44 path: String,
46 },
47 DirectoryNotEmpty {
49 path: String,
51 },
52 NameTooLong {
54 name: String,
56 },
57 InvalidArgument {
60 message: String,
62 },
63 TooManySymlinks {
65 path: String,
67 },
68 Closed,
70 ConnectFailed {
74 message: String,
76 },
77 Io {
79 errno: i32,
81 path: String,
83 message: String,
85 },
86 Protocol {
88 message: String,
90 },
91}
92
93impl std::fmt::Display for ZeroFsError {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 match self {
96 Self::NotFound { path } => write!(f, "not found: {path}"),
97 Self::PermissionDenied { path } => write!(f, "permission denied: {path}"),
98 Self::NotPermitted { path } => write!(f, "operation not permitted: {path}"),
99 Self::AlreadyExists { path } => write!(f, "already exists: {path}"),
100 Self::NotADirectory { path } => write!(f, "not a directory: {path}"),
101 Self::IsADirectory { path } => write!(f, "is a directory: {path}"),
102 Self::DirectoryNotEmpty { path } => write!(f, "directory not empty: {path}"),
103 Self::NameTooLong { name } => write!(f, "name too long: {name}"),
104 Self::InvalidArgument { message } => write!(f, "invalid argument: {message}"),
105 Self::TooManySymlinks { path } => {
106 write!(f, "too many levels of symbolic links: {path}")
107 }
108 Self::Closed => write!(f, "handle is closed"),
109 Self::ConnectFailed { message } => write!(f, "connection failed: {message}"),
110 Self::Io {
111 errno,
112 path,
113 message,
114 } => write!(f, "i/o error (errno {errno}): {path}: {message}"),
115 Self::Protocol { message } => write!(f, "protocol error: {message}"),
116 }
117 }
118}
119
120impl std::error::Error for ZeroFsError {}
121
122impl From<ZeroFsError> for std::io::Error {
126 fn from(e: ZeroFsError) -> Self {
127 let kind = std::io::Error::from_raw_os_error(e.to_errno()).kind();
128 std::io::Error::new(kind, e)
129 }
130}
131
132impl ZeroFsError {
133 pub fn to_errno(&self) -> i32 {
135 match self {
136 Self::NotFound { .. } => libc::ENOENT,
137 Self::PermissionDenied { .. } => libc::EACCES,
138 Self::NotPermitted { .. } => libc::EPERM,
139 Self::AlreadyExists { .. } => libc::EEXIST,
140 Self::NotADirectory { .. } => libc::ENOTDIR,
141 Self::IsADirectory { .. } => libc::EISDIR,
142 Self::DirectoryNotEmpty { .. } => libc::ENOTEMPTY,
143 Self::NameTooLong { .. } => libc::ENAMETOOLONG,
144 Self::InvalidArgument { .. } => libc::EINVAL,
145 Self::TooManySymlinks { .. } => libc::ELOOP,
146 Self::Closed => libc::EBADF,
147 Self::ConnectFailed { .. } => libc::EIO,
148 Self::Io { errno, .. } => *errno,
149 Self::Protocol { .. } => libc::EIO,
150 }
151 }
152
153 pub(crate) fn from_errno(errno: i32, path: &str) -> Self {
155 let path = path.to_string();
156 match errno {
157 libc::ENOENT => Self::NotFound { path },
158 libc::EACCES => Self::PermissionDenied { path },
159 libc::EPERM => Self::NotPermitted { path },
160 libc::EEXIST => Self::AlreadyExists { path },
161 libc::ENOTDIR => Self::NotADirectory { path },
162 libc::EISDIR => Self::IsADirectory { path },
163 libc::ENOTEMPTY => Self::DirectoryNotEmpty { path },
164 libc::ENAMETOOLONG => Self::NameTooLong { name: path },
165 libc::EINVAL => Self::InvalidArgument {
166 message: format!("{path}: invalid argument"),
167 },
168 libc::ELOOP => Self::TooManySymlinks { path },
169 errno => Self::Io {
170 message: std::io::Error::from_raw_os_error(errno).to_string(),
171 errno,
172 path,
173 },
174 }
175 }
176
177 pub(crate) fn from_client(e: &ClientError, path: &str) -> Self {
181 match e {
182 ClientError::Errno(code) => Self::from_errno(*code as i32, path),
183 other => Self::Protocol {
184 message: format!("{path}: {other}"),
185 },
186 }
187 }
188}
189
190pub(crate) trait ClientResultExt<T> {
192 fn ctx(self, path: &str) -> Result<T, ZeroFsError>;
193}
194
195impl<T> ClientResultExt<T> for Result<T, ClientError> {
196 fn ctx(self, path: &str) -> Result<T, ZeroFsError> {
197 self.map_err(|e| ZeroFsError::from_client(&e, path))
198 }
199}