1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
use anyhow::Error;

pub fn get_error_code(err: &Error) -> Option<&'static str> {
  err
    .downcast_ref::<std::io::Error>()
    .map(|e| match e.raw_os_error() {
      Some(code) => get_os_error_code(code),
      None => get_io_error_code(e),
    })
    .and_then(|code| match code.is_empty() {
      true => None,
      false => Some(code),
    })
}

fn get_io_error_code(err: &std::io::Error) -> &'static str {
  // not exhaustive but simple and possibly sufficient once `io_error_more` is stabilized (https://github.com/rust-lang/rust/issues/86442)
  // inversion of https://github.com/rust-lang/rust/blob/dca3f1b786efd27be3b325ed1e01e247aa589c3b/library/std/src/sys/unix/mod.rs#L138-L185
  // TODO(@AaronO): revisit as `io_error_more` lands in rust stable
  use std::io::ErrorKind;
  match err.kind() {
    // ErrorKind::ArgumentListTooLong => "E2BIG",
    ErrorKind::AddrInUse => "EADDRINUSE",
    ErrorKind::AddrNotAvailable => "EADDRNOTAVAIL",
    // ErrorKind::ResourceBusy => "EBUSY",
    ErrorKind::ConnectionAborted => "ECONNABORTED",
    ErrorKind::ConnectionRefused => "ECONNREFUSED",
    ErrorKind::ConnectionReset => "ECONNRESET",
    // ErrorKind::Deadlock => "EDEADLK",
    // ErrorKind::FilesystemQuotaExceeded => "EDQUOT",
    ErrorKind::AlreadyExists => "EEXIST",
    // ErrorKind::FileTooLarge => "EFBIG",
    // ErrorKind::HostUnreachable => "EHOSTUNREACH",
    ErrorKind::Interrupted => "EINTR",
    ErrorKind::InvalidInput => "EINVAL",
    // ErrorKind::IsADirectory => "EISDIR",
    // ErrorKind::FilesystemLoop => "ELOOP",
    ErrorKind::NotFound => "ENOENT",
    ErrorKind::OutOfMemory => "ENOMEM",
    // ErrorKind::StorageFull => "ENOSPC",
    ErrorKind::Unsupported => "ENOSYS",
    // ErrorKind::TooManyLinks => "EMLINK",
    // ErrorKind::FilenameTooLong => "ENAMETOOLONG",
    // ErrorKind::NetworkDown => "ENETDOWN",
    // ErrorKind::NetworkUnreachable => "ENETUNREACH",
    ErrorKind::NotConnected => "ENOTCONN",
    // ErrorKind::NotADirectory => "ENOTDIR",
    // ErrorKind::DirectoryNotEmpty => "ENOTEMPTY",
    ErrorKind::BrokenPipe => "EPIPE",
    // ErrorKind::ReadOnlyFilesystem => "EROFS",
    // ErrorKind::NotSeekable => "ESPIPE",
    // ErrorKind::StaleNetworkFileHandle => "ESTALE",
    ErrorKind::TimedOut => "ETIMEDOUT",
    // ErrorKind::ExecutableFileBusy => "ETXTBSY",
    // ErrorKind::CrossesDevices => "EXDEV",
    ErrorKind::PermissionDenied => "EACCES", // NOTE: Collides with EPERM ...
    _ => "",
  }
}

/// Maps OS errno codes to string names
/// derived from libuv: https://github.com/libuv/libuv/blob/26b2e5dbb6301756644d6e4cf6ca9c49c00513d3/include/uv/errno.h
/// generated with tools/codegen_error_codes.js
#[cfg(unix)]
fn get_os_error_code(errno: i32) -> &'static str {
  match errno {
    libc::E2BIG => "E2BIG",
    libc::EACCES => "EACCES",
    libc::EADDRINUSE => "EADDRINUSE",
    libc::EADDRNOTAVAIL => "EADDRNOTAVAIL",
    libc::EAFNOSUPPORT => "EAFNOSUPPORT",
    libc::EAGAIN => "EAGAIN",
    libc::EALREADY => "EALREADY",
    libc::EBADF => "EBADF",
    libc::EBUSY => "EBUSY",
    libc::ECANCELED => "ECANCELED",
    libc::ECONNABORTED => "ECONNABORTED",
    libc::ECONNREFUSED => "ECONNREFUSED",
    libc::ECONNRESET => "ECONNRESET",
    libc::EEXIST => "EEXIST",
    libc::EFAULT => "EFAULT",
    libc::EHOSTUNREACH => "EHOSTUNREACH",
    libc::EINVAL => "EINVAL",
    libc::EIO => "EIO",
    libc::EISCONN => "EISCONN",
    libc::EISDIR => "EISDIR",
    libc::ELOOP => "ELOOP",
    libc::EMFILE => "EMFILE",
    libc::EMSGSIZE => "EMSGSIZE",
    libc::ENAMETOOLONG => "ENAMETOOLONG",
    libc::ENETUNREACH => "ENETUNREACH",
    libc::ENOBUFS => "ENOBUFS",
    libc::ENOENT => "ENOENT",
    libc::ENOMEM => "ENOMEM",
    libc::ENOSPC => "ENOSPC",
    libc::ENOTCONN => "ENOTCONN",
    libc::ENOTDIR => "ENOTDIR",
    libc::ENOTEMPTY => "ENOTEMPTY",
    libc::ENOTSOCK => "ENOTSOCK",
    libc::ENOTSUP => "ENOTSUP",
    libc::EPERM => "EPERM",
    libc::EPIPE => "EPIPE",
    libc::EPROTONOSUPPORT => "EPROTONOSUPPORT",
    libc::EROFS => "EROFS",
    libc::ETIMEDOUT => "ETIMEDOUT",
    libc::EXDEV => "EXDEV",
    libc::ESOCKTNOSUPPORT => "ESOCKTNOSUPPORT",
    _ => "",
  }
}

#[cfg(windows)]
fn get_os_error_code(errno: i32) -> &'static str {
  match errno {
    998 => "EACCES",            // ERROR_NOACCESS
    10013 => "EACCES",          // WSAEACCES
    1920 => "EACCES",           // ERROR_CANT_ACCESS_FILE
    1227 => "EADDRINUSE",       // ERROR_ADDRESS_ALREADY_ASSOCIATED
    10048 => "EADDRINUSE",      // WSAEADDRINUSE
    10049 => "EADDRNOTAVAIL",   // WSAEADDRNOTAVAIL
    10047 => "EAFNOSUPPORT",    // WSAEAFNOSUPPORT
    10035 => "EAGAIN",          // WSAEWOULDBLOCK
    10037 => "EALREADY",        // WSAEALREADY
    1004 => "EBADF",            // ERROR_INVALID_FLAGS
    6 => "EBADF",               // ERROR_INVALID_HANDLE
    33 => "EBUSY",              // ERROR_LOCK_VIOLATION
    231 => "EBUSY",             // ERROR_PIPE_BUSY
    32 => "EBUSY",              // ERROR_SHARING_VIOLATION
    995 => "ECANCELED",         // ERROR_OPERATION_ABORTED
    10004 => "ECANCELED",       // WSAEINTR
    1236 => "ECONNABORTED",     // ERROR_CONNECTION_ABORTED
    10053 => "ECONNABORTED",    // WSAECONNABORTED
    1225 => "ECONNREFUSED",     // ERROR_CONNECTION_REFUSED
    10061 => "ECONNREFUSED",    // WSAECONNREFUSED
    64 => "ECONNRESET",         // ERROR_NETNAME_DELETED
    10054 => "ECONNRESET",      // WSAECONNRESET
    183 => "EEXIST",            // ERROR_ALREADY_EXISTS
    80 => "EEXIST",             // ERROR_FILE_EXISTS
    111 => "EFAULT",            // ERROR_BUFFER_OVERFLOW
    10014 => "EFAULT",          // WSAEFAULT
    1232 => "EHOSTUNREACH",     // ERROR_HOST_UNREACHABLE
    10065 => "EHOSTUNREACH",    // WSAEHOSTUNREACH
    122 => "EINVAL",            // ERROR_INSUFFICIENT_BUFFER
    13 => "EINVAL",             // ERROR_INVALID_DATA
    87 => "EINVAL",             // ERROR_INVALID_PARAMETER
    1464 => "EINVAL",           // ERROR_SYMLINK_NOT_SUPPORTED
    10022 => "EINVAL",          // WSAEINVAL
    10046 => "EINVAL",          // WSAEPFNOSUPPORT
    1102 => "EIO",              // ERROR_BEGINNING_OF_MEDIA
    1111 => "EIO",              // ERROR_BUS_RESET
    23 => "EIO",                // ERROR_CRC
    1166 => "EIO",              // ERROR_DEVICE_DOOR_OPEN
    1165 => "EIO",              // ERROR_DEVICE_REQUIRES_CLEANING
    1393 => "EIO",              // ERROR_DISK_CORRUPT
    1129 => "EIO",              // ERROR_EOM_OVERFLOW
    1101 => "EIO",              // ERROR_FILEMARK_DETECTED
    31 => "EIO",                // ERROR_GEN_FAILURE
    1106 => "EIO",              // ERROR_INVALID_BLOCK_LENGTH
    1117 => "EIO",              // ERROR_IO_DEVICE
    1104 => "EIO",              // ERROR_NO_DATA_DETECTED
    205 => "EIO",               // ERROR_NO_SIGNAL_SENT
    110 => "EIO",               // ERROR_OPEN_FAILED
    1103 => "EIO",              // ERROR_SETMARK_DETECTED
    156 => "EIO",               // ERROR_SIGNAL_REFUSED
    10056 => "EISCONN",         // WSAEISCONN
    1921 => "ELOOP",            // ERROR_CANT_RESOLVE_FILENAME
    4 => "EMFILE",              // ERROR_TOO_MANY_OPEN_FILES
    10024 => "EMFILE",          // WSAEMFILE
    10040 => "EMSGSIZE",        // WSAEMSGSIZE
    206 => "ENAMETOOLONG",      // ERROR_FILENAME_EXCED_RANGE
    1231 => "ENETUNREACH",      // ERROR_NETWORK_UNREACHABLE
    10051 => "ENETUNREACH",     // WSAENETUNREACH
    10055 => "ENOBUFS",         // WSAENOBUFS
    161 => "ENOENT",            // ERROR_BAD_PATHNAME
    267 => "ENOENT",            // ERROR_DIRECTORY
    203 => "ENOENT",            // ERROR_ENVVAR_NOT_FOUND
    2 => "ENOENT",              // ERROR_FILE_NOT_FOUND
    123 => "ENOENT",            // ERROR_INVALID_NAME
    15 => "ENOENT",             // ERROR_INVALID_DRIVE
    4392 => "ENOENT",           // ERROR_INVALID_REPARSE_DATA
    126 => "ENOENT",            // ERROR_MOD_NOT_FOUND
    3 => "ENOENT",              // ERROR_PATH_NOT_FOUND
    11001 => "ENOENT",          // WSAHOST_NOT_FOUND
    11004 => "ENOENT",          // WSANO_DATA
    8 => "ENOMEM",              // ERROR_NOT_ENOUGH_MEMORY
    14 => "ENOMEM",             // ERROR_OUTOFMEMORY
    82 => "ENOSPC",             // ERROR_CANNOT_MAKE
    112 => "ENOSPC",            // ERROR_DISK_FULL
    277 => "ENOSPC",            // ERROR_EA_TABLE_FULL
    1100 => "ENOSPC",           // ERROR_END_OF_MEDIA
    39 => "ENOSPC",             // ERROR_HANDLE_DISK_FULL
    2250 => "ENOTCONN",         // ERROR_NOT_CONNECTED
    10057 => "ENOTCONN",        // WSAENOTCONN
    145 => "ENOTEMPTY",         // ERROR_DIR_NOT_EMPTY
    10038 => "ENOTSOCK",        // WSAENOTSOCK
    50 => "ENOTSUP",            // ERROR_NOT_SUPPORTED
    5 => "EPERM",               // ERROR_ACCESS_DENIED
    1314 => "EPERM",            // ERROR_PRIVILEGE_NOT_HELD
    230 => "EPIPE",             // ERROR_BAD_PIPE
    232 => "EPIPE",             // ERROR_NO_DATA
    233 => "EPIPE",             // ERROR_PIPE_NOT_CONNECTED
    10058 => "EPIPE",           // WSAESHUTDOWN
    10043 => "EPROTONOSUPPORT", // WSAEPROTONOSUPPORT
    19 => "EROFS",              // ERROR_WRITE_PROTECT
    121 => "ETIMEDOUT",         // ERROR_SEM_TIMEOUT
    10060 => "ETIMEDOUT",       // WSAETIMEDOUT
    17 => "EXDEV",              // ERROR_NOT_SAME_DEVICE
    1 => "EISDIR",              // ERROR_INVALID_FUNCTION
    208 => "E2BIG",             // ERROR_META_EXPANSION_TOO_LONG
    10044 => "ESOCKTNOSUPPORT", // WSAESOCKTNOSUPPORT
    _ => "",
  }
}