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    /// An error returned when an operation could not be completed because a
121    /// call to `write()` returned [`Ok(0)`](Ok).
122    WriteZero,
123}
124
125impl AxErrorKind {
126    /// Returns the error description.
127    pub fn as_str(&self) -> &'static str {
128        use AxErrorKind::*;
129        match *self {
130            AddrInUse => "Address in use",
131            AlreadyConnected => "Already connected",
132            AlreadyExists => "Entity already exists",
133            ArgumentListTooLong => "Argument list too long",
134            BadAddress => "Bad address",
135            BadFileDescriptor => "Bad file descriptor",
136            BadState => "Bad internal state",
137            BrokenPipe => "Broken pipe",
138            ConnectionRefused => "Connection refused",
139            ConnectionReset => "Connection reset",
140            CrossesDevices => "Cross-device link or rename",
141            DirectoryNotEmpty => "Directory not empty",
142            FilesystemLoop => "Filesystem loop or indirection limit",
143            IllegalBytes => "Illegal byte sequence",
144            InProgress => "Operation in progress",
145            Interrupted => "Operation interrupted",
146            InvalidData => "Invalid data",
147            InvalidExecutable => "Invalid executable format",
148            InvalidInput => "Invalid input parameter",
149            Io => "I/O error",
150            IsADirectory => "Is a directory",
151            NameTooLong => "Filename too long",
152            NoMemory => "Out of memory",
153            NoSuchDevice => "No such device",
154            NoSuchProcess => "No such process",
155            NotADirectory => "Not a directory",
156            NotASocket => "Not a socket",
157            NotATty => "Inappropriate ioctl for device",
158            NotConnected => "Not connected",
159            NotFound => "Entity not found",
160            OperationNotPermitted => "Operation not permitted",
161            OperationNotSupported => "Operation not supported",
162            OutOfRange => "Result out of range",
163            PermissionDenied => "Permission denied",
164            ReadOnlyFilesystem => "Read-only filesystem",
165            ResourceBusy => "Resource busy",
166            StorageFull => "No storage space",
167            TimedOut => "Timed out",
168            TooManyOpenFiles => "Too many open files",
169            UnexpectedEof => "Unexpected end of file",
170            Unsupported => "Operation not supported",
171            WouldBlock => "Operation would block",
172            WriteZero => "Write zero",
173        }
174    }
175
176    /// Returns the error code value in `i32`.
177    pub const fn code(self) -> i32 {
178        self as i32
179    }
180}
181
182impl TryFrom<i32> for AxErrorKind {
183    type Error = i32;
184
185    #[inline]
186    fn try_from(value: i32) -> Result<Self, Self::Error> {
187        if value > 0 && value <= AxErrorKind::COUNT as i32 {
188            Ok(unsafe { core::mem::transmute::<i32, AxErrorKind>(value) })
189        } else {
190            Err(value)
191        }
192    }
193}
194
195impl fmt::Display for AxErrorKind {
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197        write!(f, "{}", self.as_str())
198    }
199}
200
201impl From<AxErrorKind> for LinuxError {
202    fn from(e: AxErrorKind) -> Self {
203        use AxErrorKind::*;
204        use LinuxError::*;
205        match e {
206            AddrInUse => EADDRINUSE,
207            AlreadyConnected => EISCONN,
208            AlreadyExists => EEXIST,
209            ArgumentListTooLong => E2BIG,
210            BadAddress | BadState => EFAULT,
211            BadFileDescriptor => EBADF,
212            BrokenPipe => EPIPE,
213            ConnectionRefused => ECONNREFUSED,
214            ConnectionReset => ECONNRESET,
215            CrossesDevices => EXDEV,
216            DirectoryNotEmpty => ENOTEMPTY,
217            FilesystemLoop => ELOOP,
218            IllegalBytes => EILSEQ,
219            InProgress => EINPROGRESS,
220            Interrupted => EINTR,
221            InvalidExecutable => ENOEXEC,
222            InvalidInput | InvalidData => EINVAL,
223            Io => EIO,
224            IsADirectory => EISDIR,
225            NameTooLong => ENAMETOOLONG,
226            NoMemory => ENOMEM,
227            NoSuchDevice => ENODEV,
228            NoSuchProcess => ESRCH,
229            NotADirectory => ENOTDIR,
230            NotASocket => ENOTSOCK,
231            NotATty => ENOTTY,
232            NotConnected => ENOTCONN,
233            NotFound => ENOENT,
234            OperationNotPermitted => EPERM,
235            OperationNotSupported => EOPNOTSUPP,
236            OutOfRange => ERANGE,
237            PermissionDenied => EACCES,
238            ReadOnlyFilesystem => EROFS,
239            ResourceBusy => EBUSY,
240            StorageFull => ENOSPC,
241            TimedOut => ETIMEDOUT,
242            TooManyOpenFiles => EMFILE,
243            UnexpectedEof | WriteZero => EIO,
244            Unsupported => ENOSYS,
245            WouldBlock => EAGAIN,
246        }
247    }
248}
249
250impl TryFrom<LinuxError> for AxErrorKind {
251    type Error = LinuxError;
252
253    fn try_from(e: LinuxError) -> Result<Self, Self::Error> {
254        use AxErrorKind::*;
255        use LinuxError::*;
256        Ok(match e {
257            EADDRINUSE => AddrInUse,
258            EISCONN => AlreadyConnected,
259            EEXIST => AlreadyExists,
260            E2BIG => ArgumentListTooLong,
261            EFAULT => BadAddress,
262            EBADF => BadFileDescriptor,
263            EPIPE => BrokenPipe,
264            ECONNREFUSED => ConnectionRefused,
265            ECONNRESET => ConnectionReset,
266            EXDEV => CrossesDevices,
267            ENOTEMPTY => DirectoryNotEmpty,
268            ELOOP => FilesystemLoop,
269            EILSEQ => IllegalBytes,
270            EINPROGRESS => InProgress,
271            EINTR => Interrupted,
272            ENOEXEC => InvalidExecutable,
273            EINVAL => InvalidInput,
274            EIO => Io,
275            EISDIR => IsADirectory,
276            ENAMETOOLONG => NameTooLong,
277            ENOMEM => NoMemory,
278            ENODEV => NoSuchDevice,
279            ESRCH => NoSuchProcess,
280            ENOTDIR => NotADirectory,
281            ENOTSOCK => NotASocket,
282            ENOTTY => NotATty,
283            ENOTCONN => NotConnected,
284            ENOENT => NotFound,
285            EPERM => OperationNotPermitted,
286            EOPNOTSUPP => OperationNotSupported,
287            ERANGE => OutOfRange,
288            EACCES => PermissionDenied,
289            EROFS => ReadOnlyFilesystem,
290            EBUSY => ResourceBusy,
291            ENOSPC => StorageFull,
292            ETIMEDOUT => TimedOut,
293            EMFILE => TooManyOpenFiles,
294            ENOSYS => Unsupported,
295            EAGAIN => WouldBlock,
296            _ => {
297                return Err(e);
298            }
299        })
300    }
301}
302
303/// The error type used by ArceOS.
304#[repr(transparent)]
305#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
306pub struct AxError(i32);
307
308enum AxErrorData {
309    Ax(AxErrorKind),
310    Linux(LinuxError),
311}
312
313impl AxError {
314    const fn new_ax(kind: AxErrorKind) -> Self {
315        AxError(kind.code())
316    }
317
318    const fn new_linux(kind: LinuxError) -> Self {
319        AxError(-kind.code())
320    }
321
322    const fn data(&self) -> AxErrorData {
323        if self.0 < 0 {
324            AxErrorData::Linux(unsafe { core::mem::transmute::<i32, LinuxError>(-self.0) })
325        } else {
326            AxErrorData::Ax(unsafe { core::mem::transmute::<i32, AxErrorKind>(self.0) })
327        }
328    }
329
330    /// Returns the error code value in `i32`.
331    pub const fn code(self) -> i32 {
332        self.0
333    }
334
335    /// Returns a canonicalized version of this error.
336    ///
337    /// This method tries to convert [`LinuxError`] variants into their
338    /// corresponding [`AxErrorKind`] variants if possible.
339    ///
340    /// # Examples
341    ///
342    /// ```
343    /// # use ax_errno::{AxError, AxErrorKind, LinuxError};
344    /// let linux_err = AxError::from(LinuxError::EACCES);
345    /// let canonical_err = linux_err.canonicalize();
346    /// assert_eq!(canonical_err, AxError::from(AxErrorKind::PermissionDenied));
347    /// ```
348    pub fn canonicalize(self) -> Self {
349        AxErrorKind::try_from(self).map_or_else(Into::into, Into::into)
350    }
351}
352
353impl<E: Into<AxErrorKind>> From<E> for AxError {
354    fn from(e: E) -> Self {
355        AxError::new_ax(e.into())
356    }
357}
358
359impl From<LinuxError> for AxError {
360    fn from(e: LinuxError) -> Self {
361        AxError::new_linux(e)
362    }
363}
364
365impl From<AxError> for LinuxError {
366    fn from(e: AxError) -> Self {
367        match e.data() {
368            AxErrorData::Ax(kind) => LinuxError::from(kind),
369            AxErrorData::Linux(kind) => kind,
370        }
371    }
372}
373
374impl TryFrom<AxError> for AxErrorKind {
375    type Error = LinuxError;
376
377    fn try_from(e: AxError) -> Result<Self, Self::Error> {
378        match e.data() {
379            AxErrorData::Ax(kind) => Ok(kind),
380            AxErrorData::Linux(e) => e.try_into(),
381        }
382    }
383}
384
385impl TryFrom<i32> for AxError {
386    type Error = i32;
387
388    fn try_from(value: i32) -> Result<Self, Self::Error> {
389        if AxErrorKind::try_from(value).is_ok() || LinuxError::try_from(-value).is_ok() {
390            Ok(AxError(value))
391        } else {
392            Err(value)
393        }
394    }
395}
396
397impl From<core::fmt::Error> for AxError {
398    fn from(_: core::fmt::Error) -> Self {
399        AxError::new_ax(AxErrorKind::InvalidInput)
400    }
401}
402
403impl fmt::Debug for AxError {
404    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405        match self.data() {
406            AxErrorData::Ax(kind) => write!(f, "AxErrorKind::{:?}", kind),
407            AxErrorData::Linux(kind) => write!(f, "LinuxError::{:?}", kind),
408        }
409    }
410}
411
412impl fmt::Display for AxError {
413    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
414        match self.data() {
415            AxErrorData::Ax(kind) => write!(f, "{}", kind),
416            AxErrorData::Linux(kind) => write!(f, "{}", kind),
417        }
418    }
419}
420
421macro_rules! axerror_consts {
422    ($($name:ident),*) => {
423        #[allow(non_upper_case_globals)]
424        impl AxError {
425            $(
426                #[doc = concat!("An [`AxError`] with kind [`AxErrorKind::", stringify!($name), "`].")]
427                pub const $name: Self = Self::new_ax(AxErrorKind::$name);
428            )*
429        }
430    };
431}
432
433axerror_consts!(
434    AddrInUse,
435    AlreadyConnected,
436    AlreadyExists,
437    ArgumentListTooLong,
438    BadAddress,
439    BadFileDescriptor,
440    BadState,
441    BrokenPipe,
442    ConnectionRefused,
443    ConnectionReset,
444    CrossesDevices,
445    DirectoryNotEmpty,
446    FilesystemLoop,
447    IllegalBytes,
448    InProgress,
449    Interrupted,
450    InvalidData,
451    InvalidExecutable,
452    InvalidInput,
453    Io,
454    IsADirectory,
455    NameTooLong,
456    NoMemory,
457    NoSuchDevice,
458    NoSuchProcess,
459    NotADirectory,
460    NotASocket,
461    NotATty,
462    NotConnected,
463    NotFound,
464    OperationNotPermitted,
465    OperationNotSupported,
466    OutOfRange,
467    PermissionDenied,
468    ReadOnlyFilesystem,
469    ResourceBusy,
470    StorageFull,
471    TimedOut,
472    TooManyOpenFiles,
473    UnexpectedEof,
474    Unsupported,
475    WouldBlock,
476    WriteZero
477);
478
479/// A specialized [`Result`] type with [`AxError`] as the error type.
480pub type AxResult<T = ()> = Result<T, AxError>;
481
482/// Convenience method to construct an [`AxError`] type while printing a warning
483/// message.
484///
485/// # Examples
486///
487/// ```
488/// # use ax_errno::{ax_err_type, AxError};
489/// #
490/// // Also print "[AxError::AlreadyExists]" if the `log` crate is enabled.
491/// assert_eq!(ax_err_type!(AlreadyExists), AxError::AlreadyExists,);
492///
493/// // Also print "[AxError::BadAddress] the address is 0!" if the `log` crate
494/// // is enabled.
495/// assert_eq!(
496///     ax_err_type!(BadAddress, "the address is 0!"),
497///     AxError::BadAddress,
498/// );
499/// ```
500#[macro_export]
501macro_rules! ax_err_type {
502    ($err:ident) => {{
503        use $crate::AxErrorKind::*;
504        let err = $crate::AxError::from($err);
505        $crate::__priv::warn!("[{:?}]", err);
506        err
507    }};
508    ($err:ident, $msg:expr) => {{
509        use $crate::AxErrorKind::*;
510        let err = $crate::AxError::from($err);
511        $crate::__priv::warn!("[{:?}] {}", err, $msg);
512        err
513    }};
514}
515
516/// Ensure a condition is true. If it is not, return from the function
517/// with an error.
518///
519/// ## Examples
520///
521/// ```rust
522/// # use ax_errno::{ensure, ax_err, AxError, AxResult};
523///
524/// fn example(user_id: i32) -> AxResult {
525///     ensure!(user_id > 0, ax_err!(InvalidInput));
526///     // After this point, we know that `user_id` is positive.
527///     let user_id = user_id as u32;
528///     Ok(())
529/// }
530/// ```
531#[macro_export]
532macro_rules! ensure {
533    ($predicate:expr, $context_selector:expr $(,)?) => {
534        if !$predicate {
535            return $context_selector;
536        }
537    };
538}
539
540/// Convenience method to construct an [`Err(AxError)`] type while printing a
541/// warning message.
542///
543/// # Examples
544///
545/// ```
546/// # use ax_errno::{ax_err, AxResult, AxError};
547/// #
548/// // Also print "[AxError::AlreadyExists]" if the `log` crate is enabled.
549/// assert_eq!(
550///     ax_err!(AlreadyExists),
551///     AxResult::<()>::Err(AxError::AlreadyExists),
552/// );
553///
554/// // Also print "[AxError::BadAddress] the address is 0!" if the `log` crate is enabled.
555/// assert_eq!(
556///     ax_err!(BadAddress, "the address is 0!"),
557///     AxResult::<()>::Err(AxError::BadAddress),
558/// );
559/// ```
560/// [`Err(AxError)`]: Err
561#[macro_export]
562macro_rules! ax_err {
563    ($err:ident) => {
564        Err($crate::ax_err_type!($err))
565    };
566    ($err:ident, $msg:expr) => {
567        Err($crate::ax_err_type!($err, $msg))
568    };
569}
570
571/// Throws an error of type [`AxError`] with the given error code, optionally
572/// with a message.
573#[macro_export]
574macro_rules! ax_bail {
575    ($($t:tt)*) => {
576        return $crate::ax_err!($($t)*);
577    };
578}
579
580/// A specialized [`Result`] type with [`LinuxError`] as the error type.
581pub type LinuxResult<T = ()> = Result<T, LinuxError>;
582
583impl fmt::Display for LinuxError {
584    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
585        write!(f, "{}", self.as_str())
586    }
587}
588
589#[doc(hidden)]
590pub mod __priv {
591    pub use log::warn;
592}
593
594#[cfg(test)]
595mod tests {
596    use strum::EnumCount;
597
598    use crate::{AxError, AxErrorKind, LinuxError};
599
600    #[test]
601    fn test_try_from() {
602        let max_code = AxErrorKind::COUNT as i32;
603        assert_eq!(max_code, 43);
604        assert_eq!(max_code, AxError::WriteZero.code());
605
606        assert_eq!(AxError::AddrInUse.code(), 1);
607        assert_eq!(Ok(AxError::AddrInUse), AxError::try_from(1));
608        assert_eq!(Ok(AxError::AlreadyConnected), AxError::try_from(2));
609        assert_eq!(Ok(AxError::WriteZero), AxError::try_from(max_code));
610        assert_eq!(Err(max_code + 1), AxError::try_from(max_code + 1));
611        assert_eq!(Err(0), AxError::try_from(0));
612        assert_eq!(Err(i32::MAX), AxError::try_from(i32::MAX));
613    }
614
615    #[test]
616    fn test_conversion() {
617        for i in 1.. {
618            let Ok(err) = LinuxError::try_from(i) else {
619                break;
620            };
621            assert_eq!(err as i32, i);
622            let e = AxError::from(err);
623            assert_eq!(e.code(), -i);
624            assert_eq!(LinuxError::from(e), err);
625        }
626    }
627}