Skip to main content

ax_errno/
lib.rs

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/// The error kind type used by ArceOS.
15///
16/// Similar to [`std::io::ErrorKind`].
17///
18/// [`std::io::ErrorKind`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html
19#[repr(i32)]
20#[non_exhaustive]
21#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, EnumCount)]
22pub enum AxErrorKind {
23    /// A socket address could not be bound because the address is already in use elsewhere.
24    AddrInUse = 1,
25    /// The socket is already connected.
26    AlreadyConnected,
27    /// An entity already exists, often a file.
28    AlreadyExists,
29    /// Program argument list too long.
30    ArgumentListTooLong,
31    /// Bad address.
32    BadAddress,
33    /// Bad file descriptor.
34    BadFileDescriptor,
35    /// Bad internal state.
36    BadState,
37    /// Broken pipe
38    BrokenPipe,
39    /// The connection was refused by the remote server.
40    ConnectionRefused,
41    /// The connection was reset by the remote server.
42    ConnectionReset,
43    /// Cross-device or cross-filesystem (hard) link or rename.
44    CrossesDevices,
45    /// A non-empty directory was specified where an empty directory was expected.
46    DirectoryNotEmpty,
47    /// Loop in the filesystem or IO subsystem; often, too many levels of
48    /// symbolic links.
49    FilesystemLoop,
50    /// Illegal byte sequence.
51    IllegalBytes,
52    /// The operation was partially successful and needs to be checked later on
53    /// due to not blocking.
54    InProgress,
55    /// This operation was interrupted.
56    Interrupted,
57    /// Data not valid for the operation were encountered.
58    ///
59    /// Unlike [`InvalidInput`], this typically means that the operation
60    /// parameters were valid, however the error was caused by malformed
61    /// input data.
62    ///
63    /// For example, a function that reads a file into a string will error with
64    /// `InvalidData` if the file's contents are not valid UTF-8.
65    ///
66    /// [`InvalidInput`]: AxErrorKind::InvalidInput
67    InvalidData,
68    /// Invalid executable format.
69    InvalidExecutable,
70    /// Invalid parameter/argument.
71    InvalidInput,
72    /// Input/output error.
73    Io,
74    /// The filesystem object is, unexpectedly, a directory.
75    IsADirectory,
76    /// Filename is too long.
77    NameTooLong,
78    /// Not enough space/cannot allocate memory.
79    NoMemory,
80    /// No such device.
81    NoSuchDevice,
82    /// No such process.
83    NoSuchProcess,
84    /// A filesystem object is, unexpectedly, not a directory.
85    NotADirectory,
86    /// The specified entity is not a socket.
87    NotASocket,
88    /// Not a typewriter.
89    NotATty,
90    /// The network operation failed because it was not connected yet.
91    NotConnected,
92    /// The requested entity is not found.
93    NotFound,
94    /// Operation not permitted.
95    OperationNotPermitted,
96    /// Operation not supported.
97    OperationNotSupported,
98    /// Result out of range.
99    OutOfRange,
100    /// The operation lacked the necessary privileges to complete.
101    PermissionDenied,
102    /// The filesystem or storage medium is read-only, but a write operation was attempted.
103    ReadOnlyFilesystem,
104    /// Device or resource is busy.
105    ResourceBusy,
106    /// The underlying storage (typically, a filesystem) is full.
107    StorageFull,
108    /// The I/O operation’s timeout expired, causing it to be canceled.
109    TimedOut,
110    /// The process has too many files open.
111    TooManyOpenFiles,
112    /// An error returned when an operation could not be completed because an
113    /// "end of file" was reached prematurely.
114    UnexpectedEof,
115    /// This operation is unsupported or unimplemented.
116    Unsupported,
117    /// The operation needs to block to complete, but the blocking operation was
118    /// requested to not occur.
119    WouldBlock,
120    /// Destination address required (sendto/sendmsg on unconnected socket
121    /// without specifying a target address).
122    DestAddrRequired,
123    /// Message too long (sendto/sendmsg with datagram exceeding socket
124    /// buffer or protocol size limit).
125    MessageTooLong,
126    /// An error returned when an operation could not be completed because a
127    /// call to `write()` returned [`Ok(0)`](Ok).
128    WriteZero,
129}
130
131impl AxErrorKind {
132    /// Returns the error description.
133    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    /// Returns the error code value in `i32`.
185    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/// The error type used by ArceOS.
316#[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    /// Returns the error code value in `i32`.
343    pub const fn code(self) -> i32 {
344        self.0
345    }
346
347    /// Returns a canonicalized version of this error.
348    ///
349    /// This method tries to convert [`LinuxError`] variants into their
350    /// corresponding [`AxErrorKind`] variants if possible.
351    ///
352    /// # Examples
353    ///
354    /// ```
355    /// # use ax_errno::{AxError, AxErrorKind, LinuxError};
356    /// let linux_err = AxError::from(LinuxError::EACCES);
357    /// let canonical_err = linux_err.canonicalize();
358    /// assert_eq!(canonical_err, AxError::from(AxErrorKind::PermissionDenied));
359    /// ```
360    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
493/// A specialized [`Result`] type with [`AxError`] as the error type.
494pub type AxResult<T = ()> = Result<T, AxError>;
495
496/// Convenience method to construct an [`AxError`] type while printing a warning
497/// message.
498///
499/// # Examples
500///
501/// ```
502/// # use ax_errno::{ax_err_type, AxError};
503/// #
504/// // Also print "[AxError::AlreadyExists]" if the `log` crate is enabled.
505/// assert_eq!(ax_err_type!(AlreadyExists), AxError::AlreadyExists,);
506///
507/// // Also print "[AxError::BadAddress] the address is 0!" if the `log` crate
508/// // is enabled.
509/// assert_eq!(
510///     ax_err_type!(BadAddress, "the address is 0!"),
511///     AxError::BadAddress,
512/// );
513/// ```
514#[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/// Ensure a condition is true. If it is not, return from the function
531/// with an error.
532///
533/// ## Examples
534///
535/// ```rust
536/// # use ax_errno::{ensure, ax_err, AxError, AxResult};
537///
538/// fn example(user_id: i32) -> AxResult {
539///     ensure!(user_id > 0, ax_err!(InvalidInput));
540///     // After this point, we know that `user_id` is positive.
541///     let user_id = user_id as u32;
542///     Ok(())
543/// }
544/// ```
545#[macro_export]
546macro_rules! ensure {
547    ($predicate:expr, $context_selector:expr $(,)?) => {
548        if !$predicate {
549            return $context_selector;
550        }
551    };
552}
553
554/// Convenience method to construct an [`Err(AxError)`] type while printing a
555/// warning message.
556///
557/// # Examples
558///
559/// ```
560/// # use ax_errno::{ax_err, AxResult, AxError};
561/// #
562/// // Also print "[AxError::AlreadyExists]" if the `log` crate is enabled.
563/// assert_eq!(
564///     ax_err!(AlreadyExists),
565///     AxResult::<()>::Err(AxError::AlreadyExists),
566/// );
567///
568/// // Also print "[AxError::BadAddress] the address is 0!" if the `log` crate is enabled.
569/// assert_eq!(
570///     ax_err!(BadAddress, "the address is 0!"),
571///     AxResult::<()>::Err(AxError::BadAddress),
572/// );
573/// ```
574/// [`Err(AxError)`]: Err
575#[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/// Throws an error of type [`AxError`] with the given error code, optionally
586/// with a message.
587#[macro_export]
588macro_rules! ax_bail {
589    ($($t:tt)*) => {
590        return $crate::ax_err!($($t)*);
591    };
592}
593
594/// A specialized [`Result`] type with [`LinuxError`] as the error type.
595pub 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}