Skip to main content

ax_errno/
lib.rs

1#![cfg_attr(not(test), no_std)]
2#![doc = include_str!("../README.md")]
3
4#[cfg(all(axtest, feature = "axtest"))]
5extern crate alloc;
6
7use core::fmt;
8
9use strum::EnumCount;
10
11#[cfg(all(axtest, feature = "axtest"))]
12/// Coverage tests for error mappings and conversions.
13pub mod axtest;
14
15mod linux_errno {
16    include!(concat!(env!("OUT_DIR"), "/linux_errno.rs"));
17}
18
19pub use linux_errno::LinuxError;
20
21/// The error kind type used by ArceOS.
22///
23/// Similar to [`std::io::ErrorKind`].
24///
25/// [`std::io::ErrorKind`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html
26#[repr(i32)]
27#[non_exhaustive]
28#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, EnumCount)]
29pub enum AxErrorKind {
30    /// A socket address could not be bound because the address is already in use elsewhere.
31    AddrInUse = 1,
32    /// The socket is already connected.
33    AlreadyConnected,
34    /// An entity already exists, often a file.
35    AlreadyExists,
36    /// Program argument list too long.
37    ArgumentListTooLong,
38    /// Bad address.
39    BadAddress,
40    /// Bad file descriptor.
41    BadFileDescriptor,
42    /// Bad internal state.
43    BadState,
44    /// Broken pipe
45    BrokenPipe,
46    /// The connection was refused by the remote server.
47    ConnectionRefused,
48    /// The connection was reset by the remote server.
49    ConnectionReset,
50    /// Cross-device or cross-filesystem (hard) link or rename.
51    CrossesDevices,
52    /// A non-empty directory was specified where an empty directory was expected.
53    DirectoryNotEmpty,
54    /// Loop in the filesystem or IO subsystem; often, too many levels of
55    /// symbolic links.
56    FilesystemLoop,
57    /// Illegal byte sequence.
58    IllegalBytes,
59    /// The operation was partially successful and needs to be checked later on
60    /// due to not blocking.
61    InProgress,
62    /// This operation was interrupted.
63    Interrupted,
64    /// Data not valid for the operation were encountered.
65    ///
66    /// Unlike [`InvalidInput`], this typically means that the operation
67    /// parameters were valid, however the error was caused by malformed
68    /// input data.
69    ///
70    /// For example, a function that reads a file into a string will error with
71    /// `InvalidData` if the file's contents are not valid UTF-8.
72    ///
73    /// [`InvalidInput`]: AxErrorKind::InvalidInput
74    InvalidData,
75    /// Invalid executable format.
76    InvalidExecutable,
77    /// Invalid parameter/argument.
78    InvalidInput,
79    /// Input/output error.
80    Io,
81    /// The filesystem object is, unexpectedly, a directory.
82    IsADirectory,
83    /// Filename is too long.
84    NameTooLong,
85    /// Not enough space/cannot allocate memory.
86    NoMemory,
87    /// No such device.
88    NoSuchDevice,
89    /// No such device or address (ENXIO). Linux uses ENXIO for: opening a
90    /// UNIX-domain-socket file, opening a FIFO O_WRONLY|O_NONBLOCK with no
91    /// reader, opening a device-special file with no underlying device.
92    NoSuchDeviceOrAddress,
93    /// No such process.
94    NoSuchProcess,
95    /// A filesystem object is, unexpectedly, not a directory.
96    NotADirectory,
97    /// The specified entity is not a socket.
98    NotASocket,
99    /// Not a typewriter.
100    NotATty,
101    /// The network operation failed because it was not connected yet.
102    NotConnected,
103    /// The requested entity is not found.
104    NotFound,
105    /// Operation not permitted.
106    OperationNotPermitted,
107    /// Operation not supported.
108    OperationNotSupported,
109    /// Result out of range.
110    OutOfRange,
111    /// The operation lacked the necessary privileges to complete.
112    PermissionDenied,
113    /// The filesystem or storage medium is read-only, but a write operation was attempted.
114    ReadOnlyFilesystem,
115    /// Device or resource is busy.
116    ResourceBusy,
117    /// The underlying storage (typically, a filesystem) is full.
118    StorageFull,
119    /// The I/O operation’s timeout expired, causing it to be canceled.
120    TimedOut,
121    /// The process has too many files open.
122    TooManyOpenFiles,
123    /// An error returned when an operation could not be completed because an
124    /// "end of file" was reached prematurely.
125    UnexpectedEof,
126    /// This operation is unsupported or unimplemented.
127    Unsupported,
128    /// The operation needs to block to complete, but the blocking operation was
129    /// requested to not occur.
130    WouldBlock,
131    /// Destination address required (sendto/sendmsg on unconnected socket
132    /// without specifying a target address).
133    DestAddrRequired,
134    /// Message too long (sendto/sendmsg with datagram exceeding socket
135    /// buffer or protocol size limit).
136    MessageTooLong,
137    /// An error returned when an operation could not be completed because a
138    /// call to `write()` returned [`Ok(0)`](Ok).
139    WriteZero,
140}
141
142impl AxErrorKind {
143    /// Returns the error description.
144    pub fn as_str(&self) -> &'static str {
145        use AxErrorKind::*;
146        match *self {
147            AddrInUse => "Address in use",
148            AlreadyConnected => "Already connected",
149            AlreadyExists => "Entity already exists",
150            ArgumentListTooLong => "Argument list too long",
151            BadAddress => "Bad address",
152            BadFileDescriptor => "Bad file descriptor",
153            BadState => "Bad internal state",
154            BrokenPipe => "Broken pipe",
155            ConnectionRefused => "Connection refused",
156            ConnectionReset => "Connection reset",
157            CrossesDevices => "Cross-device link or rename",
158            DirectoryNotEmpty => "Directory not empty",
159            FilesystemLoop => "Filesystem loop or indirection limit",
160            IllegalBytes => "Illegal byte sequence",
161            InProgress => "Operation in progress",
162            Interrupted => "Operation interrupted",
163            InvalidData => "Invalid data",
164            InvalidExecutable => "Invalid executable format",
165            InvalidInput => "Invalid input parameter",
166            Io => "I/O error",
167            IsADirectory => "Is a directory",
168            NameTooLong => "Filename too long",
169            NoMemory => "Out of memory",
170            NoSuchDevice => "No such device",
171            NoSuchDeviceOrAddress => "No such device or address",
172            NoSuchProcess => "No such process",
173            NotADirectory => "Not a directory",
174            NotASocket => "Not a socket",
175            NotATty => "Inappropriate ioctl for device",
176            NotConnected => "Not connected",
177            NotFound => "Entity not found",
178            OperationNotPermitted => "Operation not permitted",
179            OperationNotSupported => "Operation not supported",
180            OutOfRange => "Result out of range",
181            PermissionDenied => "Permission denied",
182            ReadOnlyFilesystem => "Read-only filesystem",
183            ResourceBusy => "Resource busy",
184            StorageFull => "No storage space",
185            TimedOut => "Timed out",
186            TooManyOpenFiles => "Too many open files",
187            UnexpectedEof => "Unexpected end of file",
188            Unsupported => "Operation not supported",
189            WouldBlock => "Operation would block",
190            DestAddrRequired => "Destination address required",
191            MessageTooLong => "Message too long",
192            WriteZero => "Write zero",
193        }
194    }
195
196    /// Returns the error code value in `i32`.
197    pub const fn code(self) -> i32 {
198        self as i32
199    }
200}
201
202impl TryFrom<i32> for AxErrorKind {
203    type Error = i32;
204
205    #[inline]
206    fn try_from(value: i32) -> Result<Self, Self::Error> {
207        if value > 0 && value <= AxErrorKind::COUNT as i32 {
208            Ok(unsafe { core::mem::transmute::<i32, AxErrorKind>(value) })
209        } else {
210            Err(value)
211        }
212    }
213}
214
215impl fmt::Display for AxErrorKind {
216    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217        write!(f, "{}", self.as_str())
218    }
219}
220
221impl From<AxErrorKind> for LinuxError {
222    fn from(e: AxErrorKind) -> Self {
223        use AxErrorKind::*;
224        use LinuxError::*;
225        match e {
226            AddrInUse => EADDRINUSE,
227            AlreadyConnected => EISCONN,
228            AlreadyExists => EEXIST,
229            ArgumentListTooLong => E2BIG,
230            BadAddress | BadState => EFAULT,
231            BadFileDescriptor => EBADF,
232            BrokenPipe => EPIPE,
233            ConnectionRefused => ECONNREFUSED,
234            ConnectionReset => ECONNRESET,
235            CrossesDevices => EXDEV,
236            DirectoryNotEmpty => ENOTEMPTY,
237            FilesystemLoop => ELOOP,
238            IllegalBytes => EILSEQ,
239            InProgress => EINPROGRESS,
240            Interrupted => EINTR,
241            InvalidExecutable => ENOEXEC,
242            InvalidInput | InvalidData => EINVAL,
243            Io => EIO,
244            IsADirectory => EISDIR,
245            NameTooLong => ENAMETOOLONG,
246            NoMemory => ENOMEM,
247            NoSuchDevice => ENODEV,
248            NoSuchDeviceOrAddress => ENXIO,
249            NoSuchProcess => ESRCH,
250            NotADirectory => ENOTDIR,
251            NotASocket => ENOTSOCK,
252            NotATty => ENOTTY,
253            DestAddrRequired => EDESTADDRREQ,
254            MessageTooLong => EMSGSIZE,
255            NotConnected => ENOTCONN,
256            NotFound => ENOENT,
257            OperationNotPermitted => EPERM,
258            OperationNotSupported => EOPNOTSUPP,
259            OutOfRange => ERANGE,
260            PermissionDenied => EACCES,
261            ReadOnlyFilesystem => EROFS,
262            ResourceBusy => EBUSY,
263            StorageFull => ENOSPC,
264            TimedOut => ETIMEDOUT,
265            TooManyOpenFiles => EMFILE,
266            UnexpectedEof | WriteZero => EIO,
267            Unsupported => ENOSYS,
268            WouldBlock => EAGAIN,
269        }
270    }
271}
272
273impl TryFrom<LinuxError> for AxErrorKind {
274    type Error = LinuxError;
275
276    fn try_from(e: LinuxError) -> Result<Self, Self::Error> {
277        use AxErrorKind::*;
278        use LinuxError::*;
279        Ok(match e {
280            EADDRINUSE => AddrInUse,
281            EISCONN => AlreadyConnected,
282            EEXIST => AlreadyExists,
283            E2BIG => ArgumentListTooLong,
284            EFAULT => BadAddress,
285            EBADF => BadFileDescriptor,
286            EPIPE => BrokenPipe,
287            ECONNREFUSED => ConnectionRefused,
288            ECONNRESET => ConnectionReset,
289            EXDEV => CrossesDevices,
290            ENOTEMPTY => DirectoryNotEmpty,
291            ELOOP => FilesystemLoop,
292            EILSEQ => IllegalBytes,
293            EINPROGRESS => InProgress,
294            EINTR => Interrupted,
295            ENOEXEC => InvalidExecutable,
296            EINVAL => InvalidInput,
297            EIO => Io,
298            EISDIR => IsADirectory,
299            ENAMETOOLONG => NameTooLong,
300            ENOMEM => NoMemory,
301            ENODEV => NoSuchDevice,
302            ENXIO => NoSuchDeviceOrAddress,
303            ESRCH => NoSuchProcess,
304            ENOTDIR => NotADirectory,
305            ENOTSOCK => NotASocket,
306            ENOTTY => NotATty,
307            EDESTADDRREQ => DestAddrRequired,
308            EMSGSIZE => MessageTooLong,
309            ENOTCONN => NotConnected,
310            ENOENT => NotFound,
311            EPERM => OperationNotPermitted,
312            EOPNOTSUPP => OperationNotSupported,
313            ERANGE => OutOfRange,
314            EACCES => PermissionDenied,
315            EROFS => ReadOnlyFilesystem,
316            EBUSY => ResourceBusy,
317            ENOSPC => StorageFull,
318            ETIMEDOUT => TimedOut,
319            EMFILE => TooManyOpenFiles,
320            ENOSYS => Unsupported,
321            EAGAIN => WouldBlock,
322            _ => {
323                return Err(e);
324            }
325        })
326    }
327}
328
329/// The error type used by ArceOS.
330#[repr(transparent)]
331#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
332pub struct AxError(i32);
333
334enum AxErrorData {
335    Ax(AxErrorKind),
336    Linux(LinuxError),
337}
338
339impl AxError {
340    const fn new_ax(kind: AxErrorKind) -> Self {
341        AxError(kind.code())
342    }
343
344    const fn new_linux(kind: LinuxError) -> Self {
345        AxError(-kind.code())
346    }
347
348    const fn data(&self) -> AxErrorData {
349        if self.0 < 0 {
350            AxErrorData::Linux(unsafe { core::mem::transmute::<i32, LinuxError>(-self.0) })
351        } else {
352            AxErrorData::Ax(unsafe { core::mem::transmute::<i32, AxErrorKind>(self.0) })
353        }
354    }
355
356    /// Returns the error code value in `i32`.
357    pub const fn code(self) -> i32 {
358        self.0
359    }
360
361    /// Returns a canonicalized version of this error.
362    ///
363    /// This method tries to convert [`LinuxError`] variants into their
364    /// corresponding [`AxErrorKind`] variants if possible.
365    ///
366    /// # Examples
367    ///
368    /// ```
369    /// # use ax_errno::{AxError, AxErrorKind, LinuxError};
370    /// let linux_err = AxError::from(LinuxError::EACCES);
371    /// let canonical_err = linux_err.canonicalize();
372    /// assert_eq!(canonical_err, AxError::from(AxErrorKind::PermissionDenied));
373    /// ```
374    pub fn canonicalize(self) -> Self {
375        AxErrorKind::try_from(self).map_or_else(Into::into, Into::into)
376    }
377}
378
379impl<E: Into<AxErrorKind>> From<E> for AxError {
380    fn from(e: E) -> Self {
381        AxError::new_ax(e.into())
382    }
383}
384
385impl From<LinuxError> for AxError {
386    fn from(e: LinuxError) -> Self {
387        AxError::new_linux(e)
388    }
389}
390
391impl From<AxError> for LinuxError {
392    fn from(e: AxError) -> Self {
393        match e.data() {
394            AxErrorData::Ax(kind) => LinuxError::from(kind),
395            AxErrorData::Linux(kind) => kind,
396        }
397    }
398}
399
400impl TryFrom<AxError> for AxErrorKind {
401    type Error = LinuxError;
402
403    fn try_from(e: AxError) -> Result<Self, Self::Error> {
404        match e.data() {
405            AxErrorData::Ax(kind) => Ok(kind),
406            AxErrorData::Linux(e) => e.try_into(),
407        }
408    }
409}
410
411impl TryFrom<i32> for AxError {
412    type Error = i32;
413
414    fn try_from(value: i32) -> Result<Self, Self::Error> {
415        if AxErrorKind::try_from(value).is_ok() || LinuxError::try_from(-value).is_ok() {
416            Ok(AxError(value))
417        } else {
418            Err(value)
419        }
420    }
421}
422
423impl From<core::fmt::Error> for AxError {
424    fn from(_: core::fmt::Error) -> Self {
425        AxError::new_ax(AxErrorKind::InvalidInput)
426    }
427}
428
429impl fmt::Debug for AxError {
430    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
431        match self.data() {
432            AxErrorData::Ax(kind) => write!(f, "AxErrorKind::{:?}", kind),
433            AxErrorData::Linux(kind) => write!(f, "LinuxError::{:?}", kind),
434        }
435    }
436}
437
438impl fmt::Display for AxError {
439    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
440        match self.data() {
441            AxErrorData::Ax(kind) => write!(f, "{}", kind),
442            AxErrorData::Linux(kind) => write!(f, "{}", kind),
443        }
444    }
445}
446
447macro_rules! axerror_consts {
448    ($($name:ident),*) => {
449        #[allow(non_upper_case_globals)]
450        impl AxError {
451            $(
452                #[doc = concat!("An [`AxError`] with kind [`AxErrorKind::", stringify!($name), "`].")]
453                pub const $name: Self = Self::new_ax(AxErrorKind::$name);
454            )*
455        }
456    };
457}
458
459axerror_consts!(
460    AddrInUse,
461    AlreadyConnected,
462    AlreadyExists,
463    ArgumentListTooLong,
464    BadAddress,
465    BadFileDescriptor,
466    BadState,
467    BrokenPipe,
468    ConnectionRefused,
469    ConnectionReset,
470    CrossesDevices,
471    DirectoryNotEmpty,
472    FilesystemLoop,
473    IllegalBytes,
474    InProgress,
475    Interrupted,
476    InvalidData,
477    InvalidExecutable,
478    InvalidInput,
479    Io,
480    IsADirectory,
481    NameTooLong,
482    NoMemory,
483    NoSuchDevice,
484    NoSuchDeviceOrAddress,
485    NoSuchProcess,
486    NotADirectory,
487    NotASocket,
488    NotATty,
489    NotConnected,
490    NotFound,
491    OperationNotPermitted,
492    OperationNotSupported,
493    OutOfRange,
494    PermissionDenied,
495    ReadOnlyFilesystem,
496    ResourceBusy,
497    StorageFull,
498    TimedOut,
499    TooManyOpenFiles,
500    UnexpectedEof,
501    Unsupported,
502    WouldBlock,
503    DestAddrRequired,
504    MessageTooLong,
505    WriteZero
506);
507
508/// A specialized [`Result`] type with [`AxError`] as the error type.
509pub type AxResult<T = ()> = Result<T, AxError>;
510
511/// Convenience method to construct an [`AxError`] type while printing a warning
512/// message.
513///
514/// # Examples
515///
516/// ```
517/// # use ax_errno::{ax_err_type, AxError};
518/// #
519/// // Also print "[AxError::AlreadyExists]" if the `log` crate is enabled.
520/// assert_eq!(ax_err_type!(AlreadyExists), AxError::AlreadyExists,);
521///
522/// // Also print "[AxError::BadAddress] the address is 0!" if the `log` crate
523/// // is enabled.
524/// assert_eq!(
525///     ax_err_type!(BadAddress, "the address is 0!"),
526///     AxError::BadAddress,
527/// );
528/// ```
529#[macro_export]
530macro_rules! ax_err_type {
531    ($err:ident) => {{
532        use $crate::AxErrorKind::*;
533        let err = $crate::AxError::from($err);
534        $crate::__priv::warn!("[{:?}]", err);
535        err
536    }};
537    ($err:ident, $msg:expr) => {{
538        use $crate::AxErrorKind::*;
539        let err = $crate::AxError::from($err);
540        $crate::__priv::warn!("[{:?}] {}", err, $msg);
541        err
542    }};
543}
544
545/// Ensure a condition is true. If it is not, return from the function
546/// with an error.
547///
548/// ## Examples
549///
550/// ```rust
551/// # use ax_errno::{ensure, ax_err, AxError, AxResult};
552///
553/// fn example(user_id: i32) -> AxResult {
554///     ensure!(user_id > 0, ax_err!(InvalidInput));
555///     // After this point, we know that `user_id` is positive.
556///     let user_id = user_id as u32;
557///     Ok(())
558/// }
559/// ```
560#[macro_export]
561macro_rules! ensure {
562    ($predicate:expr, $context_selector:expr $(,)?) => {
563        if !$predicate {
564            return $context_selector;
565        }
566    };
567}
568
569/// Convenience method to construct an [`Err(AxError)`] type while printing a
570/// warning message.
571///
572/// # Examples
573///
574/// ```
575/// # use ax_errno::{ax_err, AxResult, AxError};
576/// #
577/// // Also print "[AxError::AlreadyExists]" if the `log` crate is enabled.
578/// assert_eq!(
579///     ax_err!(AlreadyExists),
580///     AxResult::<()>::Err(AxError::AlreadyExists),
581/// );
582///
583/// // Also print "[AxError::BadAddress] the address is 0!" if the `log` crate is enabled.
584/// assert_eq!(
585///     ax_err!(BadAddress, "the address is 0!"),
586///     AxResult::<()>::Err(AxError::BadAddress),
587/// );
588/// ```
589/// [`Err(AxError)`]: Err
590#[macro_export]
591macro_rules! ax_err {
592    ($err:ident) => {
593        Err($crate::ax_err_type!($err))
594    };
595    ($err:ident, $msg:expr) => {
596        Err($crate::ax_err_type!($err, $msg))
597    };
598}
599
600/// Throws an error of type [`AxError`] with the given error code, optionally
601/// with a message.
602#[macro_export]
603macro_rules! ax_bail {
604    ($($t:tt)*) => {
605        return $crate::ax_err!($($t)*);
606    };
607}
608
609/// A specialized [`Result`] type with [`LinuxError`] as the error type.
610pub type LinuxResult<T = ()> = Result<T, LinuxError>;
611
612impl fmt::Display for LinuxError {
613    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
614        write!(f, "{}", self.as_str())
615    }
616}
617
618#[doc(hidden)]
619pub mod __priv {
620    pub use log::warn;
621}
622
623#[cfg(test)]
624mod tests {
625    use strum::EnumCount;
626
627    use crate::{AxError, AxErrorKind, LinuxError};
628
629    #[test]
630    fn test_try_from() {
631        let max_code = AxErrorKind::COUNT as i32;
632        // 46 = 45 (dev baseline, 含 WriteZero) + 1 (NoSuchDeviceOrAddress, ENXIO).
633        // 该 variant 由 open/openat deep-fix PR 引入(fix bug-open-unix-socket-no-enxio
634        // 与 bug-open-fifo-wronly-no-reader-no-enxio 需要 ENXIO 映射)。
635        assert_eq!(max_code, 46);
636        assert_eq!(max_code, AxError::WriteZero.code());
637
638        assert_eq!(AxError::AddrInUse.code(), 1);
639        assert_eq!(Ok(AxError::AddrInUse), AxError::try_from(1));
640        assert_eq!(Ok(AxError::AlreadyConnected), AxError::try_from(2));
641        assert_eq!(Ok(AxError::WriteZero), AxError::try_from(max_code));
642        assert_eq!(Err(max_code + 1), AxError::try_from(max_code + 1));
643        assert_eq!(Err(0), AxError::try_from(0));
644        assert_eq!(Err(i32::MAX), AxError::try_from(i32::MAX));
645    }
646
647    #[test]
648    fn test_conversion() {
649        for i in 1.. {
650            let Ok(err) = LinuxError::try_from(i) else {
651                break;
652            };
653            assert_eq!(err as i32, i);
654            let e = AxError::from(err);
655            assert_eq!(e.code(), -i);
656            assert_eq!(LinuxError::from(e), err);
657        }
658    }
659}