1#![cfg_attr(not(test), no_std)]
2#![doc = include_str!("../README.md")]
3
4use core::fmt;
5
6use strum::EnumCount;
7
8mod linux_errno {
9 include!(concat!(env!("OUT_DIR"), "/linux_errno.rs"));
10}
11
12pub use linux_errno::LinuxError;
13
14#[repr(i32)]
20#[non_exhaustive]
21#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, EnumCount)]
22pub enum AxErrorKind {
23 AddrInUse = 1,
25 AlreadyConnected,
27 AlreadyExists,
29 ArgumentListTooLong,
31 BadAddress,
33 BadFileDescriptor,
35 BadState,
37 BrokenPipe,
39 ConnectionRefused,
41 ConnectionReset,
43 CrossesDevices,
45 DirectoryNotEmpty,
47 FilesystemLoop,
50 IllegalBytes,
52 InProgress,
55 Interrupted,
57 InvalidData,
68 InvalidExecutable,
70 InvalidInput,
72 Io,
74 IsADirectory,
76 NameTooLong,
78 NoMemory,
80 NoSuchDevice,
82 NoSuchProcess,
84 NotADirectory,
86 NotASocket,
88 NotATty,
90 NotConnected,
92 NotFound,
94 OperationNotPermitted,
96 OperationNotSupported,
98 OutOfRange,
100 PermissionDenied,
102 ReadOnlyFilesystem,
104 ResourceBusy,
106 StorageFull,
108 TimedOut,
110 TooManyOpenFiles,
112 UnexpectedEof,
115 Unsupported,
117 WouldBlock,
120 DestAddrRequired,
123 MessageTooLong,
126 WriteZero,
129}
130
131impl AxErrorKind {
132 pub fn as_str(&self) -> &'static str {
134 use AxErrorKind::*;
135 match *self {
136 AddrInUse => "Address in use",
137 AlreadyConnected => "Already connected",
138 AlreadyExists => "Entity already exists",
139 ArgumentListTooLong => "Argument list too long",
140 BadAddress => "Bad address",
141 BadFileDescriptor => "Bad file descriptor",
142 BadState => "Bad internal state",
143 BrokenPipe => "Broken pipe",
144 ConnectionRefused => "Connection refused",
145 ConnectionReset => "Connection reset",
146 CrossesDevices => "Cross-device link or rename",
147 DirectoryNotEmpty => "Directory not empty",
148 FilesystemLoop => "Filesystem loop or indirection limit",
149 IllegalBytes => "Illegal byte sequence",
150 InProgress => "Operation in progress",
151 Interrupted => "Operation interrupted",
152 InvalidData => "Invalid data",
153 InvalidExecutable => "Invalid executable format",
154 InvalidInput => "Invalid input parameter",
155 Io => "I/O error",
156 IsADirectory => "Is a directory",
157 NameTooLong => "Filename too long",
158 NoMemory => "Out of memory",
159 NoSuchDevice => "No such device",
160 NoSuchProcess => "No such process",
161 NotADirectory => "Not a directory",
162 NotASocket => "Not a socket",
163 NotATty => "Inappropriate ioctl for device",
164 NotConnected => "Not connected",
165 NotFound => "Entity not found",
166 OperationNotPermitted => "Operation not permitted",
167 OperationNotSupported => "Operation not supported",
168 OutOfRange => "Result out of range",
169 PermissionDenied => "Permission denied",
170 ReadOnlyFilesystem => "Read-only filesystem",
171 ResourceBusy => "Resource busy",
172 StorageFull => "No storage space",
173 TimedOut => "Timed out",
174 TooManyOpenFiles => "Too many open files",
175 UnexpectedEof => "Unexpected end of file",
176 Unsupported => "Operation not supported",
177 WouldBlock => "Operation would block",
178 DestAddrRequired => "Destination address required",
179 MessageTooLong => "Message too long",
180 WriteZero => "Write zero",
181 }
182 }
183
184 pub const fn code(self) -> i32 {
186 self as i32
187 }
188}
189
190impl TryFrom<i32> for AxErrorKind {
191 type Error = i32;
192
193 #[inline]
194 fn try_from(value: i32) -> Result<Self, Self::Error> {
195 if value > 0 && value <= AxErrorKind::COUNT as i32 {
196 Ok(unsafe { core::mem::transmute::<i32, AxErrorKind>(value) })
197 } else {
198 Err(value)
199 }
200 }
201}
202
203impl fmt::Display for AxErrorKind {
204 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205 write!(f, "{}", self.as_str())
206 }
207}
208
209impl From<AxErrorKind> for LinuxError {
210 fn from(e: AxErrorKind) -> Self {
211 use AxErrorKind::*;
212 use LinuxError::*;
213 match e {
214 AddrInUse => EADDRINUSE,
215 AlreadyConnected => EISCONN,
216 AlreadyExists => EEXIST,
217 ArgumentListTooLong => E2BIG,
218 BadAddress | BadState => EFAULT,
219 BadFileDescriptor => EBADF,
220 BrokenPipe => EPIPE,
221 ConnectionRefused => ECONNREFUSED,
222 ConnectionReset => ECONNRESET,
223 CrossesDevices => EXDEV,
224 DirectoryNotEmpty => ENOTEMPTY,
225 FilesystemLoop => ELOOP,
226 IllegalBytes => EILSEQ,
227 InProgress => EINPROGRESS,
228 Interrupted => EINTR,
229 InvalidExecutable => ENOEXEC,
230 InvalidInput | InvalidData => EINVAL,
231 Io => EIO,
232 IsADirectory => EISDIR,
233 NameTooLong => ENAMETOOLONG,
234 NoMemory => ENOMEM,
235 NoSuchDevice => ENODEV,
236 NoSuchProcess => ESRCH,
237 NotADirectory => ENOTDIR,
238 NotASocket => ENOTSOCK,
239 NotATty => ENOTTY,
240 DestAddrRequired => EDESTADDRREQ,
241 MessageTooLong => EMSGSIZE,
242 NotConnected => ENOTCONN,
243 NotFound => ENOENT,
244 OperationNotPermitted => EPERM,
245 OperationNotSupported => EOPNOTSUPP,
246 OutOfRange => ERANGE,
247 PermissionDenied => EACCES,
248 ReadOnlyFilesystem => EROFS,
249 ResourceBusy => EBUSY,
250 StorageFull => ENOSPC,
251 TimedOut => ETIMEDOUT,
252 TooManyOpenFiles => EMFILE,
253 UnexpectedEof | WriteZero => EIO,
254 Unsupported => ENOSYS,
255 WouldBlock => EAGAIN,
256 }
257 }
258}
259
260impl TryFrom<LinuxError> for AxErrorKind {
261 type Error = LinuxError;
262
263 fn try_from(e: LinuxError) -> Result<Self, Self::Error> {
264 use AxErrorKind::*;
265 use LinuxError::*;
266 Ok(match e {
267 EADDRINUSE => AddrInUse,
268 EISCONN => AlreadyConnected,
269 EEXIST => AlreadyExists,
270 E2BIG => ArgumentListTooLong,
271 EFAULT => BadAddress,
272 EBADF => BadFileDescriptor,
273 EPIPE => BrokenPipe,
274 ECONNREFUSED => ConnectionRefused,
275 ECONNRESET => ConnectionReset,
276 EXDEV => CrossesDevices,
277 ENOTEMPTY => DirectoryNotEmpty,
278 ELOOP => FilesystemLoop,
279 EILSEQ => IllegalBytes,
280 EINPROGRESS => InProgress,
281 EINTR => Interrupted,
282 ENOEXEC => InvalidExecutable,
283 EINVAL => InvalidInput,
284 EIO => Io,
285 EISDIR => IsADirectory,
286 ENAMETOOLONG => NameTooLong,
287 ENOMEM => NoMemory,
288 ENODEV => NoSuchDevice,
289 ESRCH => NoSuchProcess,
290 ENOTDIR => NotADirectory,
291 ENOTSOCK => NotASocket,
292 ENOTTY => NotATty,
293 EDESTADDRREQ => DestAddrRequired,
294 EMSGSIZE => MessageTooLong,
295 ENOTCONN => NotConnected,
296 ENOENT => NotFound,
297 EPERM => OperationNotPermitted,
298 EOPNOTSUPP => OperationNotSupported,
299 ERANGE => OutOfRange,
300 EACCES => PermissionDenied,
301 EROFS => ReadOnlyFilesystem,
302 EBUSY => ResourceBusy,
303 ENOSPC => StorageFull,
304 ETIMEDOUT => TimedOut,
305 EMFILE => TooManyOpenFiles,
306 ENOSYS => Unsupported,
307 EAGAIN => WouldBlock,
308 _ => {
309 return Err(e);
310 }
311 })
312 }
313}
314
315#[repr(transparent)]
317#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
318pub struct AxError(i32);
319
320enum AxErrorData {
321 Ax(AxErrorKind),
322 Linux(LinuxError),
323}
324
325impl AxError {
326 const fn new_ax(kind: AxErrorKind) -> Self {
327 AxError(kind.code())
328 }
329
330 const fn new_linux(kind: LinuxError) -> Self {
331 AxError(-kind.code())
332 }
333
334 const fn data(&self) -> AxErrorData {
335 if self.0 < 0 {
336 AxErrorData::Linux(unsafe { core::mem::transmute::<i32, LinuxError>(-self.0) })
337 } else {
338 AxErrorData::Ax(unsafe { core::mem::transmute::<i32, AxErrorKind>(self.0) })
339 }
340 }
341
342 pub const fn code(self) -> i32 {
344 self.0
345 }
346
347 pub fn canonicalize(self) -> Self {
361 AxErrorKind::try_from(self).map_or_else(Into::into, Into::into)
362 }
363}
364
365impl<E: Into<AxErrorKind>> From<E> for AxError {
366 fn from(e: E) -> Self {
367 AxError::new_ax(e.into())
368 }
369}
370
371impl From<LinuxError> for AxError {
372 fn from(e: LinuxError) -> Self {
373 AxError::new_linux(e)
374 }
375}
376
377impl From<AxError> for LinuxError {
378 fn from(e: AxError) -> Self {
379 match e.data() {
380 AxErrorData::Ax(kind) => LinuxError::from(kind),
381 AxErrorData::Linux(kind) => kind,
382 }
383 }
384}
385
386impl TryFrom<AxError> for AxErrorKind {
387 type Error = LinuxError;
388
389 fn try_from(e: AxError) -> Result<Self, Self::Error> {
390 match e.data() {
391 AxErrorData::Ax(kind) => Ok(kind),
392 AxErrorData::Linux(e) => e.try_into(),
393 }
394 }
395}
396
397impl TryFrom<i32> for AxError {
398 type Error = i32;
399
400 fn try_from(value: i32) -> Result<Self, Self::Error> {
401 if AxErrorKind::try_from(value).is_ok() || LinuxError::try_from(-value).is_ok() {
402 Ok(AxError(value))
403 } else {
404 Err(value)
405 }
406 }
407}
408
409impl From<core::fmt::Error> for AxError {
410 fn from(_: core::fmt::Error) -> Self {
411 AxError::new_ax(AxErrorKind::InvalidInput)
412 }
413}
414
415impl fmt::Debug for AxError {
416 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
417 match self.data() {
418 AxErrorData::Ax(kind) => write!(f, "AxErrorKind::{:?}", kind),
419 AxErrorData::Linux(kind) => write!(f, "LinuxError::{:?}", kind),
420 }
421 }
422}
423
424impl fmt::Display for AxError {
425 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
426 match self.data() {
427 AxErrorData::Ax(kind) => write!(f, "{}", kind),
428 AxErrorData::Linux(kind) => write!(f, "{}", kind),
429 }
430 }
431}
432
433macro_rules! axerror_consts {
434 ($($name:ident),*) => {
435 #[allow(non_upper_case_globals)]
436 impl AxError {
437 $(
438 #[doc = concat!("An [`AxError`] with kind [`AxErrorKind::", stringify!($name), "`].")]
439 pub const $name: Self = Self::new_ax(AxErrorKind::$name);
440 )*
441 }
442 };
443}
444
445axerror_consts!(
446 AddrInUse,
447 AlreadyConnected,
448 AlreadyExists,
449 ArgumentListTooLong,
450 BadAddress,
451 BadFileDescriptor,
452 BadState,
453 BrokenPipe,
454 ConnectionRefused,
455 ConnectionReset,
456 CrossesDevices,
457 DirectoryNotEmpty,
458 FilesystemLoop,
459 IllegalBytes,
460 InProgress,
461 Interrupted,
462 InvalidData,
463 InvalidExecutable,
464 InvalidInput,
465 Io,
466 IsADirectory,
467 NameTooLong,
468 NoMemory,
469 NoSuchDevice,
470 NoSuchProcess,
471 NotADirectory,
472 NotASocket,
473 NotATty,
474 NotConnected,
475 NotFound,
476 OperationNotPermitted,
477 OperationNotSupported,
478 OutOfRange,
479 PermissionDenied,
480 ReadOnlyFilesystem,
481 ResourceBusy,
482 StorageFull,
483 TimedOut,
484 TooManyOpenFiles,
485 UnexpectedEof,
486 Unsupported,
487 WouldBlock,
488 DestAddrRequired,
489 MessageTooLong,
490 WriteZero
491);
492
493pub type AxResult<T = ()> = Result<T, AxError>;
495
496#[macro_export]
515macro_rules! ax_err_type {
516 ($err:ident) => {{
517 use $crate::AxErrorKind::*;
518 let err = $crate::AxError::from($err);
519 $crate::__priv::warn!("[{:?}]", err);
520 err
521 }};
522 ($err:ident, $msg:expr) => {{
523 use $crate::AxErrorKind::*;
524 let err = $crate::AxError::from($err);
525 $crate::__priv::warn!("[{:?}] {}", err, $msg);
526 err
527 }};
528}
529
530#[macro_export]
546macro_rules! ensure {
547 ($predicate:expr, $context_selector:expr $(,)?) => {
548 if !$predicate {
549 return $context_selector;
550 }
551 };
552}
553
554#[macro_export]
576macro_rules! ax_err {
577 ($err:ident) => {
578 Err($crate::ax_err_type!($err))
579 };
580 ($err:ident, $msg:expr) => {
581 Err($crate::ax_err_type!($err, $msg))
582 };
583}
584
585#[macro_export]
588macro_rules! ax_bail {
589 ($($t:tt)*) => {
590 return $crate::ax_err!($($t)*);
591 };
592}
593
594pub type LinuxResult<T = ()> = Result<T, LinuxError>;
596
597impl fmt::Display for LinuxError {
598 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
599 write!(f, "{}", self.as_str())
600 }
601}
602
603#[doc(hidden)]
604pub mod __priv {
605 pub use log::warn;
606}
607
608#[cfg(test)]
609mod tests {
610 use strum::EnumCount;
611
612 use crate::{AxError, AxErrorKind, LinuxError};
613
614 #[test]
615 fn test_try_from() {
616 let max_code = AxErrorKind::COUNT as i32;
617 assert_eq!(max_code, 45);
618 assert_eq!(max_code, AxError::WriteZero.code());
619
620 assert_eq!(AxError::AddrInUse.code(), 1);
621 assert_eq!(Ok(AxError::AddrInUse), AxError::try_from(1));
622 assert_eq!(Ok(AxError::AlreadyConnected), AxError::try_from(2));
623 assert_eq!(Ok(AxError::WriteZero), AxError::try_from(max_code));
624 assert_eq!(Err(max_code + 1), AxError::try_from(max_code + 1));
625 assert_eq!(Err(0), AxError::try_from(0));
626 assert_eq!(Err(i32::MAX), AxError::try_from(i32::MAX));
627 }
628
629 #[test]
630 fn test_conversion() {
631 for i in 1.. {
632 let Ok(err) = LinuxError::try_from(i) else {
633 break;
634 };
635 assert_eq!(err as i32, i);
636 let e = AxError::from(err);
637 assert_eq!(e.code(), -i);
638 assert_eq!(LinuxError::from(e), err);
639 }
640 }
641}