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