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 fmt::Debug for AxError {
398    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
399        match self.data() {
400            AxErrorData::Ax(kind) => write!(f, "AxErrorKind::{:?}", kind),
401            AxErrorData::Linux(kind) => write!(f, "LinuxError::{:?}", kind),
402        }
403    }
404}
405
406impl fmt::Display for AxError {
407    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
408        match self.data() {
409            AxErrorData::Ax(kind) => write!(f, "{}", kind),
410            AxErrorData::Linux(kind) => write!(f, "{}", kind),
411        }
412    }
413}
414
415macro_rules! axerror_consts {
416    ($($name:ident),*) => {
417        #[allow(non_upper_case_globals)]
418        impl AxError {
419            $(
420                #[doc = concat!("An [`AxError`] with kind [`AxErrorKind::", stringify!($name), "`].")]
421                pub const $name: Self = Self::new_ax(AxErrorKind::$name);
422            )*
423        }
424    };
425}
426
427axerror_consts!(
428    AddrInUse,
429    AlreadyConnected,
430    AlreadyExists,
431    ArgumentListTooLong,
432    BadAddress,
433    BadFileDescriptor,
434    BadState,
435    BrokenPipe,
436    ConnectionRefused,
437    ConnectionReset,
438    CrossesDevices,
439    DirectoryNotEmpty,
440    FilesystemLoop,
441    IllegalBytes,
442    InProgress,
443    Interrupted,
444    InvalidData,
445    InvalidExecutable,
446    InvalidInput,
447    Io,
448    IsADirectory,
449    NameTooLong,
450    NoMemory,
451    NoSuchDevice,
452    NoSuchProcess,
453    NotADirectory,
454    NotASocket,
455    NotATty,
456    NotConnected,
457    NotFound,
458    OperationNotPermitted,
459    OperationNotSupported,
460    OutOfRange,
461    PermissionDenied,
462    ReadOnlyFilesystem,
463    ResourceBusy,
464    StorageFull,
465    TimedOut,
466    TooManyOpenFiles,
467    UnexpectedEof,
468    Unsupported,
469    WouldBlock,
470    WriteZero
471);
472
473/// A specialized [`Result`] type with [`AxError`] as the error type.
474pub type AxResult<T = ()> = Result<T, AxError>;
475
476/// Convenience method to construct an [`AxError`] type while printing a warning
477/// message.
478///
479/// # Examples
480///
481/// ```
482/// # use ax_errno::{ax_err_type, AxError};
483/// #
484/// // Also print "[AxError::AlreadyExists]" if the `log` crate is enabled.
485/// assert_eq!(ax_err_type!(AlreadyExists), AxError::AlreadyExists,);
486///
487/// // Also print "[AxError::BadAddress] the address is 0!" if the `log` crate
488/// // is enabled.
489/// assert_eq!(
490///     ax_err_type!(BadAddress, "the address is 0!"),
491///     AxError::BadAddress,
492/// );
493/// ```
494#[macro_export]
495macro_rules! ax_err_type {
496    ($err:ident) => {{
497        use $crate::AxErrorKind::*;
498        let err = $crate::AxError::from($err);
499        $crate::__priv::warn!("[{:?}]", err);
500        err
501    }};
502    ($err:ident, $msg:expr) => {{
503        use $crate::AxErrorKind::*;
504        let err = $crate::AxError::from($err);
505        $crate::__priv::warn!("[{:?}] {}", err, $msg);
506        err
507    }};
508}
509
510/// Ensure a condition is true. If it is not, return from the function
511/// with an error.
512///
513/// ## Examples
514///
515/// ```rust
516/// # use ax_errno::{ensure, ax_err, AxError, AxResult};
517///
518/// fn example(user_id: i32) -> AxResult {
519///     ensure!(user_id > 0, ax_err!(InvalidInput));
520///     // After this point, we know that `user_id` is positive.
521///     let user_id = user_id as u32;
522///     Ok(())
523/// }
524/// ```
525#[macro_export]
526macro_rules! ensure {
527    ($predicate:expr, $context_selector:expr $(,)?) => {
528        if !$predicate {
529            return $context_selector;
530        }
531    };
532}
533
534/// Convenience method to construct an [`Err(AxError)`] type while printing a
535/// warning message.
536///
537/// # Examples
538///
539/// ```
540/// # use ax_errno::{ax_err, AxResult, AxError};
541/// #
542/// // Also print "[AxError::AlreadyExists]" if the `log` crate is enabled.
543/// assert_eq!(
544///     ax_err!(AlreadyExists),
545///     AxResult::<()>::Err(AxError::AlreadyExists),
546/// );
547///
548/// // Also print "[AxError::BadAddress] the address is 0!" if the `log` crate is enabled.
549/// assert_eq!(
550///     ax_err!(BadAddress, "the address is 0!"),
551///     AxResult::<()>::Err(AxError::BadAddress),
552/// );
553/// ```
554/// [`Err(AxError)`]: Err
555#[macro_export]
556macro_rules! ax_err {
557    ($err:ident) => {
558        Err($crate::ax_err_type!($err))
559    };
560    ($err:ident, $msg:expr) => {
561        Err($crate::ax_err_type!($err, $msg))
562    };
563}
564
565/// Throws an error of type [`AxError`] with the given error code, optionally
566/// with a message.
567#[macro_export]
568macro_rules! ax_bail {
569    ($($t:tt)*) => {
570        return $crate::ax_err!($($t)*);
571    };
572}
573
574/// A specialized [`Result`] type with [`LinuxError`] as the error type.
575pub type LinuxResult<T = ()> = Result<T, LinuxError>;
576
577impl fmt::Display for LinuxError {
578    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
579        write!(f, "{}", self.as_str())
580    }
581}
582
583#[doc(hidden)]
584pub mod __priv {
585    pub use log::warn;
586}
587
588#[cfg(test)]
589mod tests {
590    use strum::EnumCount;
591
592    use crate::{AxError, AxErrorKind, LinuxError};
593
594    #[test]
595    fn test_try_from() {
596        let max_code = AxErrorKind::COUNT as i32;
597        assert_eq!(max_code, 43);
598        assert_eq!(max_code, AxError::WriteZero.code());
599
600        assert_eq!(AxError::AddrInUse.code(), 1);
601        assert_eq!(Ok(AxError::AddrInUse), AxError::try_from(1));
602        assert_eq!(Ok(AxError::AlreadyConnected), AxError::try_from(2));
603        assert_eq!(Ok(AxError::WriteZero), AxError::try_from(max_code));
604        assert_eq!(Err(max_code + 1), AxError::try_from(max_code + 1));
605        assert_eq!(Err(0), AxError::try_from(0));
606        assert_eq!(Err(i32::MAX), AxError::try_from(i32::MAX));
607    }
608
609    #[test]
610    fn test_conversion() {
611        for i in 1.. {
612            let Ok(err) = LinuxError::try_from(i) else {
613                break;
614            };
615            assert_eq!(err as i32, i);
616            let e = AxError::from(err);
617            assert_eq!(e.code(), -i);
618            assert_eq!(LinuxError::from(e), err);
619        }
620    }
621}