1use std::{fmt, io};
2
3use serde::{Deserialize, Serialize};
4
5use crate::target::OperatingSystem;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
11#[non_exhaustive]
12pub enum ErrorKind {
13 NotFound,
14 PermissionDenied,
15 ConnectionRefused,
16 ConnectionReset,
17 HostUnreachable,
18 NetworkUnreachable,
19 ConnectionAborted,
20 NotConnected,
21 AddrInUse,
22 AddrNotAvailable,
23 NetworkDown,
24 BrokenPipe,
25 AlreadyExists,
26 WouldBlock,
27 NotADirectory,
28 IsADirectory,
29 DirectoryNotEmpty,
30 ReadOnlyFilesystem,
31 StaleNetworkFileHandle,
32 InvalidInput,
33 InvalidData,
34 TimedOut,
35 WriteZero,
36 StorageFull,
37 NotSeekable,
38 QuotaExceeded,
39 FileTooLarge,
40 ResourceBusy,
41 ExecutableFileBusy,
42 Deadlock,
43 CrossesDevices,
44 TooManyLinks,
45 InvalidFilename,
46 ArgumentListTooLong,
47 Interrupted,
48 Unsupported,
49 UnexpectedEof,
50 OutOfMemory,
51 Other,
52 Closed,
54}
55
56impl From<io::ErrorKind> for ErrorKind {
57 fn from(kind: io::ErrorKind) -> Self {
58 match kind {
59 io::ErrorKind::NotFound => Self::NotFound,
60 io::ErrorKind::PermissionDenied => Self::PermissionDenied,
61 io::ErrorKind::ConnectionRefused => Self::ConnectionRefused,
62 io::ErrorKind::ConnectionReset => Self::ConnectionReset,
63 io::ErrorKind::HostUnreachable => Self::HostUnreachable,
64 io::ErrorKind::NetworkUnreachable => Self::NetworkUnreachable,
65 io::ErrorKind::ConnectionAborted => Self::ConnectionAborted,
66 io::ErrorKind::NotConnected => Self::NotConnected,
67 io::ErrorKind::AddrInUse => Self::AddrInUse,
68 io::ErrorKind::AddrNotAvailable => Self::AddrNotAvailable,
69 io::ErrorKind::NetworkDown => Self::NetworkDown,
70 io::ErrorKind::BrokenPipe => Self::BrokenPipe,
71 io::ErrorKind::AlreadyExists => Self::AlreadyExists,
72 io::ErrorKind::WouldBlock => Self::WouldBlock,
73 io::ErrorKind::NotADirectory => Self::NotADirectory,
74 io::ErrorKind::IsADirectory => Self::IsADirectory,
75 io::ErrorKind::DirectoryNotEmpty => Self::DirectoryNotEmpty,
76 io::ErrorKind::ReadOnlyFilesystem => Self::ReadOnlyFilesystem,
77 io::ErrorKind::StaleNetworkFileHandle => Self::StaleNetworkFileHandle,
78 io::ErrorKind::InvalidInput => Self::InvalidInput,
79 io::ErrorKind::InvalidData => Self::InvalidData,
80 io::ErrorKind::TimedOut => Self::TimedOut,
81 io::ErrorKind::WriteZero => Self::WriteZero,
82 io::ErrorKind::StorageFull => Self::StorageFull,
83 io::ErrorKind::NotSeekable => Self::NotSeekable,
84 io::ErrorKind::QuotaExceeded => Self::QuotaExceeded,
85 io::ErrorKind::FileTooLarge => Self::FileTooLarge,
86 io::ErrorKind::ResourceBusy => Self::ResourceBusy,
87 io::ErrorKind::ExecutableFileBusy => Self::ExecutableFileBusy,
88 io::ErrorKind::Deadlock => Self::Deadlock,
89 io::ErrorKind::CrossesDevices => Self::CrossesDevices,
90 io::ErrorKind::TooManyLinks => Self::TooManyLinks,
91 io::ErrorKind::InvalidFilename => Self::InvalidFilename,
92 io::ErrorKind::ArgumentListTooLong => Self::ArgumentListTooLong,
93 io::ErrorKind::Interrupted => Self::Interrupted,
94 io::ErrorKind::Unsupported => Self::Unsupported,
95 io::ErrorKind::UnexpectedEof => Self::UnexpectedEof,
96 io::ErrorKind::OutOfMemory => Self::OutOfMemory,
97 _ => Self::Other,
98 }
99 }
100}
101
102impl From<ErrorKind> for io::ErrorKind {
103 fn from(kind: ErrorKind) -> Self {
104 match kind {
105 ErrorKind::NotFound => Self::NotFound,
106 ErrorKind::PermissionDenied => Self::PermissionDenied,
107 ErrorKind::ConnectionRefused => Self::ConnectionRefused,
108 ErrorKind::ConnectionReset => Self::ConnectionReset,
109 ErrorKind::HostUnreachable => Self::HostUnreachable,
110 ErrorKind::NetworkUnreachable => Self::NetworkUnreachable,
111 ErrorKind::ConnectionAborted => Self::ConnectionAborted,
112 ErrorKind::NotConnected => Self::NotConnected,
113 ErrorKind::AddrInUse => Self::AddrInUse,
114 ErrorKind::AddrNotAvailable => Self::AddrNotAvailable,
115 ErrorKind::NetworkDown => Self::NetworkDown,
116 ErrorKind::BrokenPipe => Self::BrokenPipe,
117 ErrorKind::AlreadyExists => Self::AlreadyExists,
118 ErrorKind::WouldBlock => Self::WouldBlock,
119 ErrorKind::NotADirectory => Self::NotADirectory,
120 ErrorKind::IsADirectory => Self::IsADirectory,
121 ErrorKind::DirectoryNotEmpty => Self::DirectoryNotEmpty,
122 ErrorKind::ReadOnlyFilesystem => Self::ReadOnlyFilesystem,
123 ErrorKind::StaleNetworkFileHandle => Self::StaleNetworkFileHandle,
124 ErrorKind::InvalidInput => Self::InvalidInput,
125 ErrorKind::InvalidData => Self::InvalidData,
126 ErrorKind::TimedOut => Self::TimedOut,
127 ErrorKind::WriteZero => Self::WriteZero,
128 ErrorKind::StorageFull => Self::StorageFull,
129 ErrorKind::NotSeekable => Self::NotSeekable,
130 ErrorKind::QuotaExceeded => Self::QuotaExceeded,
131 ErrorKind::FileTooLarge => Self::FileTooLarge,
132 ErrorKind::ResourceBusy => Self::ResourceBusy,
133 ErrorKind::ExecutableFileBusy => Self::ExecutableFileBusy,
134 ErrorKind::Deadlock => Self::Deadlock,
135 ErrorKind::CrossesDevices => Self::CrossesDevices,
136 ErrorKind::TooManyLinks => Self::TooManyLinks,
137 ErrorKind::InvalidFilename => Self::InvalidFilename,
138 ErrorKind::ArgumentListTooLong => Self::ArgumentListTooLong,
139 ErrorKind::Interrupted => Self::Interrupted,
140 ErrorKind::Unsupported => Self::Unsupported,
141 ErrorKind::UnexpectedEof => Self::UnexpectedEof,
142 ErrorKind::OutOfMemory => Self::OutOfMemory,
143 ErrorKind::Other | ErrorKind::Closed => Self::Other,
144 }
145 }
146}
147
148impl PartialEq<io::ErrorKind> for ErrorKind {
149 fn eq(&self, other: &io::ErrorKind) -> bool {
150 *self == Self::from(*other)
151 }
152}
153
154impl PartialEq<ErrorKind> for io::ErrorKind {
155 fn eq(&self, other: &ErrorKind) -> bool {
156 ErrorKind::from(*self) == *other
157 }
158}
159
160#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
162pub struct SystemCode {
163 operating_system: OperatingSystem,
164 raw: i32,
165}
166
167impl SystemCode {
168 pub const fn new(operating_system: OperatingSystem, raw: i32) -> Self {
170 Self {
171 operating_system,
172 raw,
173 }
174 }
175
176 pub const fn operating_system(self) -> OperatingSystem {
178 self.operating_system
179 }
180
181 pub const fn raw(self) -> i32 {
183 self.raw
184 }
185}
186
187#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct Error {
193 kind: ErrorKind,
194 message: String,
195 system_code: Option<SystemCode>,
196}
197
198impl Error {
199 pub fn new(kind: ErrorKind, message: impl ToString) -> Self {
201 Self {
202 kind,
203 message: message.to_string(),
204 system_code: None,
205 }
206 }
207
208 pub fn other(error: impl ToString) -> Self {
210 Self::new(ErrorKind::Other, error.to_string())
211 }
212
213 pub fn from_system_code(
215 kind: ErrorKind,
216 message: impl Into<String>,
217 operating_system: OperatingSystem,
218 raw: i32,
219 ) -> Self {
220 Self {
221 kind,
222 message: message.into(),
223 system_code: Some(SystemCode::new(operating_system, raw)),
224 }
225 }
226
227 pub fn from_raw_os_error(raw: i32) -> Self {
229 let error = io::Error::from_raw_os_error(raw);
230 Self::from_raw_os_error_with_message(raw, error.to_string())
231 }
232
233 pub fn last_os_error() -> Self {
235 io::Error::last_os_error().into()
236 }
237
238 pub fn from_raw_os_error_with_message(raw: i32, message: impl Into<String>) -> Self {
241 let kind = io::Error::from_raw_os_error(raw).kind().into();
242 Self::from_system_code(kind, message, OperatingSystem::current(), raw)
243 }
244
245 pub fn from_raw_os_error_with_kind(raw: i32, kind: ErrorKind) -> Self {
248 let error = io::Error::from_raw_os_error(raw);
249 Self::from_system_code(kind, error.to_string(), OperatingSystem::current(), raw)
250 }
251
252 pub const fn kind(&self) -> ErrorKind {
254 self.kind
255 }
256
257 pub fn message(&self) -> &str {
259 &self.message
260 }
261
262 pub const fn system_code(&self) -> Option<SystemCode> {
264 self.system_code
265 }
266
267 pub const fn raw_os_error(&self) -> Option<i32> {
269 match self.system_code {
270 Some(code) => Some(code.raw()),
271 None => None,
272 }
273 }
274
275 pub fn into_io_error(self) -> io::Error {
277 io::Error::new(self.kind.into(), self)
278 }
279}
280
281impl From<io::Error> for Error {
282 fn from(error: io::Error) -> Self {
283 let error = match error.downcast::<Self>() {
286 Ok(error) => return error,
287 Err(error) => error,
288 };
289 let kind = error.kind().into();
290 let message = error.to_string();
291 match error.raw_os_error() {
292 Some(raw) => Self::from_system_code(kind, message, OperatingSystem::current(), raw),
293 None => Self::new(kind, message),
294 }
295 }
296}
297
298impl From<dolang_rpc::Error> for Error {
299 fn from(error: dolang_rpc::Error) -> Self {
300 match error {
301 dolang_rpc::Error::Io(error) => error.into(),
302 dolang_rpc::Error::Serialize(_)
303 | dolang_rpc::Error::Deserialize(_)
304 | dolang_rpc::Error::Protocol(_) => Self::new(ErrorKind::InvalidData, error),
305 dolang_rpc::Error::Auth(_) => Self::new(ErrorKind::PermissionDenied, error),
306 dolang_rpc::Error::ConnectionClosed => Self::new(ErrorKind::ConnectionReset, error),
307 dolang_rpc::Error::Cancelled => Self::new(ErrorKind::Interrupted, error),
308 dolang_rpc::Error::UnsupportedCapability => Self::new(ErrorKind::Unsupported, error),
309 }
310 }
311}
312
313impl From<std::ffi::NulError> for Error {
314 fn from(error: std::ffi::NulError) -> Self {
315 io::Error::from(error).into()
316 }
317}
318
319#[cfg(unix)]
320impl From<nix::errno::Errno> for Error {
321 fn from(error: nix::errno::Errno) -> Self {
322 Self::from_raw_os_error(error as i32)
323 }
324}
325
326impl From<Error> for io::Error {
327 fn from(error: Error) -> Self {
328 error.into_io_error()
329 }
330}
331
332impl fmt::Display for Error {
333 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
334 f.write_str(&self.message)
335 }
336}
337
338impl std::error::Error for Error {}
339
340pub type Result<T> = std::result::Result<T, Error>;
342
343#[cfg(test)]
344mod tests {
345 use super::{Error, ErrorKind, OperatingSystem};
346 use std::io;
347
348 #[test]
349 fn io_error_preserves_formatted_message_and_origin() {
350 #[cfg(unix)]
351 let raw = libc::ENOENT;
352 #[cfg(windows)]
353 let raw = windows_sys::Win32::Foundation::ERROR_FILE_NOT_FOUND as i32;
354
355 let io_error = io::Error::from_raw_os_error(raw);
356 let message = io_error.to_string();
357 let error = Error::from(io_error);
358 assert_eq!(error.kind(), ErrorKind::NotFound);
359 assert_eq!(error.message(), message);
360 let code = error.system_code().unwrap();
361 assert_eq!(code.operating_system(), OperatingSystem::current());
362 assert_eq!(code.raw(), raw);
363 }
364
365 #[test]
366 fn foreign_system_code_keeps_supplied_message() {
367 let error = Error::from_system_code(
368 ErrorKind::PermissionDenied,
369 "access is denied",
370 OperatingSystem::Windows,
371 5,
372 );
373 assert_eq!(error.message(), "access is denied");
374 assert_eq!(
375 error.system_code().unwrap().operating_system(),
376 OperatingSystem::Windows
377 );
378 }
379
380 #[test]
381 fn raw_os_error_with_message_derives_kind_and_preserves_details() {
382 #[cfg(unix)]
383 let raw = libc::ENOENT;
384 #[cfg(windows)]
385 let raw = windows_sys::Win32::Foundation::ERROR_FILE_NOT_FOUND as i32;
386
387 let error = Error::from_raw_os_error_with_message(raw, "custom message");
388 assert_eq!(error.kind(), ErrorKind::NotFound);
389 assert_eq!(error.message(), "custom message");
390 let code = error.system_code().unwrap();
391 assert_eq!(code.operating_system(), OperatingSystem::current());
392 assert_eq!(code.raw(), raw);
393 }
394}