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}
53
54impl From<io::ErrorKind> for ErrorKind {
55 fn from(kind: io::ErrorKind) -> Self {
56 match kind {
57 io::ErrorKind::NotFound => Self::NotFound,
58 io::ErrorKind::PermissionDenied => Self::PermissionDenied,
59 io::ErrorKind::ConnectionRefused => Self::ConnectionRefused,
60 io::ErrorKind::ConnectionReset => Self::ConnectionReset,
61 io::ErrorKind::HostUnreachable => Self::HostUnreachable,
62 io::ErrorKind::NetworkUnreachable => Self::NetworkUnreachable,
63 io::ErrorKind::ConnectionAborted => Self::ConnectionAborted,
64 io::ErrorKind::NotConnected => Self::NotConnected,
65 io::ErrorKind::AddrInUse => Self::AddrInUse,
66 io::ErrorKind::AddrNotAvailable => Self::AddrNotAvailable,
67 io::ErrorKind::NetworkDown => Self::NetworkDown,
68 io::ErrorKind::BrokenPipe => Self::BrokenPipe,
69 io::ErrorKind::AlreadyExists => Self::AlreadyExists,
70 io::ErrorKind::WouldBlock => Self::WouldBlock,
71 io::ErrorKind::NotADirectory => Self::NotADirectory,
72 io::ErrorKind::IsADirectory => Self::IsADirectory,
73 io::ErrorKind::DirectoryNotEmpty => Self::DirectoryNotEmpty,
74 io::ErrorKind::ReadOnlyFilesystem => Self::ReadOnlyFilesystem,
75 io::ErrorKind::StaleNetworkFileHandle => Self::StaleNetworkFileHandle,
76 io::ErrorKind::InvalidInput => Self::InvalidInput,
77 io::ErrorKind::InvalidData => Self::InvalidData,
78 io::ErrorKind::TimedOut => Self::TimedOut,
79 io::ErrorKind::WriteZero => Self::WriteZero,
80 io::ErrorKind::StorageFull => Self::StorageFull,
81 io::ErrorKind::NotSeekable => Self::NotSeekable,
82 io::ErrorKind::QuotaExceeded => Self::QuotaExceeded,
83 io::ErrorKind::FileTooLarge => Self::FileTooLarge,
84 io::ErrorKind::ResourceBusy => Self::ResourceBusy,
85 io::ErrorKind::ExecutableFileBusy => Self::ExecutableFileBusy,
86 io::ErrorKind::Deadlock => Self::Deadlock,
87 io::ErrorKind::CrossesDevices => Self::CrossesDevices,
88 io::ErrorKind::TooManyLinks => Self::TooManyLinks,
89 io::ErrorKind::InvalidFilename => Self::InvalidFilename,
90 io::ErrorKind::ArgumentListTooLong => Self::ArgumentListTooLong,
91 io::ErrorKind::Interrupted => Self::Interrupted,
92 io::ErrorKind::Unsupported => Self::Unsupported,
93 io::ErrorKind::UnexpectedEof => Self::UnexpectedEof,
94 io::ErrorKind::OutOfMemory => Self::OutOfMemory,
95 _ => Self::Other,
96 }
97 }
98}
99
100impl From<ErrorKind> for io::ErrorKind {
101 fn from(kind: ErrorKind) -> Self {
102 match kind {
103 ErrorKind::NotFound => Self::NotFound,
104 ErrorKind::PermissionDenied => Self::PermissionDenied,
105 ErrorKind::ConnectionRefused => Self::ConnectionRefused,
106 ErrorKind::ConnectionReset => Self::ConnectionReset,
107 ErrorKind::HostUnreachable => Self::HostUnreachable,
108 ErrorKind::NetworkUnreachable => Self::NetworkUnreachable,
109 ErrorKind::ConnectionAborted => Self::ConnectionAborted,
110 ErrorKind::NotConnected => Self::NotConnected,
111 ErrorKind::AddrInUse => Self::AddrInUse,
112 ErrorKind::AddrNotAvailable => Self::AddrNotAvailable,
113 ErrorKind::NetworkDown => Self::NetworkDown,
114 ErrorKind::BrokenPipe => Self::BrokenPipe,
115 ErrorKind::AlreadyExists => Self::AlreadyExists,
116 ErrorKind::WouldBlock => Self::WouldBlock,
117 ErrorKind::NotADirectory => Self::NotADirectory,
118 ErrorKind::IsADirectory => Self::IsADirectory,
119 ErrorKind::DirectoryNotEmpty => Self::DirectoryNotEmpty,
120 ErrorKind::ReadOnlyFilesystem => Self::ReadOnlyFilesystem,
121 ErrorKind::StaleNetworkFileHandle => Self::StaleNetworkFileHandle,
122 ErrorKind::InvalidInput => Self::InvalidInput,
123 ErrorKind::InvalidData => Self::InvalidData,
124 ErrorKind::TimedOut => Self::TimedOut,
125 ErrorKind::WriteZero => Self::WriteZero,
126 ErrorKind::StorageFull => Self::StorageFull,
127 ErrorKind::NotSeekable => Self::NotSeekable,
128 ErrorKind::QuotaExceeded => Self::QuotaExceeded,
129 ErrorKind::FileTooLarge => Self::FileTooLarge,
130 ErrorKind::ResourceBusy => Self::ResourceBusy,
131 ErrorKind::ExecutableFileBusy => Self::ExecutableFileBusy,
132 ErrorKind::Deadlock => Self::Deadlock,
133 ErrorKind::CrossesDevices => Self::CrossesDevices,
134 ErrorKind::TooManyLinks => Self::TooManyLinks,
135 ErrorKind::InvalidFilename => Self::InvalidFilename,
136 ErrorKind::ArgumentListTooLong => Self::ArgumentListTooLong,
137 ErrorKind::Interrupted => Self::Interrupted,
138 ErrorKind::Unsupported => Self::Unsupported,
139 ErrorKind::UnexpectedEof => Self::UnexpectedEof,
140 ErrorKind::OutOfMemory => Self::OutOfMemory,
141 ErrorKind::Other => Self::Other,
142 }
143 }
144}
145
146impl PartialEq<io::ErrorKind> for ErrorKind {
147 fn eq(&self, other: &io::ErrorKind) -> bool {
148 *self == Self::from(*other)
149 }
150}
151
152impl PartialEq<ErrorKind> for io::ErrorKind {
153 fn eq(&self, other: &ErrorKind) -> bool {
154 ErrorKind::from(*self) == *other
155 }
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
160pub struct SystemCode {
161 operating_system: OperatingSystem,
162 raw: i32,
163}
164
165impl SystemCode {
166 pub const fn new(operating_system: OperatingSystem, raw: i32) -> Self {
168 Self {
169 operating_system,
170 raw,
171 }
172 }
173
174 pub const fn operating_system(self) -> OperatingSystem {
176 self.operating_system
177 }
178
179 pub const fn raw(self) -> i32 {
181 self.raw
182 }
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize)]
190pub struct Error {
191 kind: ErrorKind,
192 message: String,
193 system_code: Option<SystemCode>,
194}
195
196impl Error {
197 pub fn new(kind: ErrorKind, message: impl ToString) -> Self {
199 Self {
200 kind,
201 message: message.to_string(),
202 system_code: None,
203 }
204 }
205
206 pub fn other(error: impl ToString) -> Self {
208 Self::new(ErrorKind::Other, error.to_string())
209 }
210
211 pub fn from_system_code(
213 kind: ErrorKind,
214 message: impl Into<String>,
215 operating_system: OperatingSystem,
216 raw: i32,
217 ) -> Self {
218 Self {
219 kind,
220 message: message.into(),
221 system_code: Some(SystemCode::new(operating_system, raw)),
222 }
223 }
224
225 pub fn from_raw_os_error(raw: i32) -> Self {
227 let error = io::Error::from_raw_os_error(raw);
228 Self::from_raw_os_error_with_message(raw, error.to_string())
229 }
230
231 pub fn last_os_error() -> Self {
233 io::Error::last_os_error().into()
234 }
235
236 pub fn from_raw_os_error_with_message(raw: i32, message: impl Into<String>) -> Self {
239 let kind = io::Error::from_raw_os_error(raw).kind().into();
240 Self::from_system_code(kind, message, OperatingSystem::current(), raw)
241 }
242
243 pub fn from_raw_os_error_with_kind(raw: i32, kind: ErrorKind) -> Self {
246 let error = io::Error::from_raw_os_error(raw);
247 Self::from_system_code(kind, error.to_string(), OperatingSystem::current(), raw)
248 }
249
250 pub const fn kind(&self) -> ErrorKind {
252 self.kind
253 }
254
255 pub fn message(&self) -> &str {
257 &self.message
258 }
259
260 pub const fn system_code(&self) -> Option<SystemCode> {
262 self.system_code
263 }
264
265 pub const fn raw_os_error(&self) -> Option<i32> {
267 match self.system_code {
268 Some(code) => Some(code.raw()),
269 None => None,
270 }
271 }
272
273 pub fn into_io_error(self) -> io::Error {
275 io::Error::new(self.kind.into(), self)
276 }
277}
278
279impl From<io::Error> for Error {
280 fn from(error: io::Error) -> Self {
281 let kind = error.kind().into();
282 let message = error.to_string();
283 match error.raw_os_error() {
284 Some(raw) => Self::from_system_code(kind, message, OperatingSystem::current(), raw),
285 None => Self::new(kind, message),
286 }
287 }
288}
289
290impl From<dolang_rpc::Error> for Error {
291 fn from(error: dolang_rpc::Error) -> Self {
292 match error {
293 dolang_rpc::Error::Io(error) => error.into(),
294 dolang_rpc::Error::Serialize(_)
295 | dolang_rpc::Error::Deserialize(_)
296 | dolang_rpc::Error::Protocol(_) => Self::new(ErrorKind::InvalidData, error),
297 dolang_rpc::Error::Auth(_) => Self::new(ErrorKind::PermissionDenied, error),
298 dolang_rpc::Error::ConnectionClosed => Self::new(ErrorKind::ConnectionReset, error),
299 dolang_rpc::Error::Cancelled => Self::new(ErrorKind::Interrupted, error),
300 dolang_rpc::Error::UnsupportedCapability => Self::new(ErrorKind::Unsupported, error),
301 }
302 }
303}
304
305impl From<std::ffi::NulError> for Error {
306 fn from(error: std::ffi::NulError) -> Self {
307 io::Error::from(error).into()
308 }
309}
310
311#[cfg(unix)]
312impl From<nix::errno::Errno> for Error {
313 fn from(error: nix::errno::Errno) -> Self {
314 Self::from_raw_os_error(error as i32)
315 }
316}
317
318impl From<Error> for io::Error {
319 fn from(error: Error) -> Self {
320 error.into_io_error()
321 }
322}
323
324impl fmt::Display for Error {
325 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
326 f.write_str(&self.message)
327 }
328}
329
330impl std::error::Error for Error {}
331
332pub struct HandoffError<H> {
345 handle: H,
346 error: Error,
347}
348
349impl<H> HandoffError<H> {
350 pub fn new(handle: H, error: impl Into<Error>) -> Self {
352 Self {
353 handle,
354 error: error.into(),
355 }
356 }
357
358 pub fn error(&self) -> &Error {
360 &self.error
361 }
362
363 pub fn into_handle(self) -> H {
365 self.handle
366 }
367
368 pub fn into_error(self) -> Error {
370 self.error
371 }
372
373 pub fn into_parts(self) -> (H, Error) {
375 (self.handle, self.error)
376 }
377}
378
379impl<H> fmt::Debug for HandoffError<H> {
380 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
381 f.debug_struct("HandoffError")
382 .field("error", &self.error)
383 .finish_non_exhaustive()
384 }
385}
386
387impl<H> fmt::Display for HandoffError<H> {
388 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
389 fmt::Display::fmt(&self.error, f)
390 }
391}
392
393impl<H> std::error::Error for HandoffError<H> {}
394
395impl<H> From<HandoffError<H>> for Error {
396 fn from(error: HandoffError<H>) -> Self {
397 error.error
398 }
399}
400
401pub type Result<T> = std::result::Result<T, Error>;
403
404#[cfg(test)]
405mod tests {
406 use super::{Error, ErrorKind, OperatingSystem};
407 use std::io;
408
409 #[test]
410 fn io_error_preserves_formatted_message_and_origin() {
411 #[cfg(unix)]
412 let raw = libc::ENOENT;
413 #[cfg(windows)]
414 let raw = windows_sys::Win32::Foundation::ERROR_FILE_NOT_FOUND as i32;
415
416 let io_error = io::Error::from_raw_os_error(raw);
417 let message = io_error.to_string();
418 let error = Error::from(io_error);
419 assert_eq!(error.kind(), ErrorKind::NotFound);
420 assert_eq!(error.message(), message);
421 let code = error.system_code().unwrap();
422 assert_eq!(code.operating_system(), OperatingSystem::current());
423 assert_eq!(code.raw(), raw);
424 }
425
426 #[test]
427 fn foreign_system_code_keeps_supplied_message() {
428 let error = Error::from_system_code(
429 ErrorKind::PermissionDenied,
430 "access is denied",
431 OperatingSystem::Windows,
432 5,
433 );
434 assert_eq!(error.message(), "access is denied");
435 assert_eq!(
436 error.system_code().unwrap().operating_system(),
437 OperatingSystem::Windows
438 );
439 }
440
441 #[test]
442 fn raw_os_error_with_message_derives_kind_and_preserves_details() {
443 #[cfg(unix)]
444 let raw = libc::ENOENT;
445 #[cfg(windows)]
446 let raw = windows_sys::Win32::Foundation::ERROR_FILE_NOT_FOUND as i32;
447
448 let error = Error::from_raw_os_error_with_message(raw, "custom message");
449 assert_eq!(error.kind(), ErrorKind::NotFound);
450 assert_eq!(error.message(), "custom message");
451 let code = error.system_code().unwrap();
452 assert_eq!(code.operating_system(), OperatingSystem::current());
453 assert_eq!(code.raw(), raw);
454 }
455}