error-code 2.3.1

Alternative Error for Rust
Documentation
//! Alternative `Error`
//!
//! It's goal is to be able to provide simplified `Error` which would work in `no_std` environment
//!
//! # Features
//!
//! - `std` - enables `std::error::Error` implementation
//!
//! # Categories
//!
//! Library introduces the concept of categories, similar to that of C++ `std::error_category`.
//! Each category can be used to describe set of integral error codes.
//!
//! Following implementations are builtin:
//!
//! - [Posix](struct.PosixCategory.html) - POSIX category. To access integer constants use [libc](https://crates.io/crates/libc)
//! - [System](struct.SystemCategory.html) - System category. To access integer constants use [libc](https://crates.io/crates/libc) on unix, and [winapi](https://crates.io/crates/winapi) on Windows
//! - [Plain](type.PlainError.html) - Plain errors without any category.

#![no_std]
#![warn(missing_docs)]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::style))]

#[cfg(feature = "std")]
extern crate std;

const UNKNOWN_ERROR: &str = "Unknown error";
#[allow(unused)]
const FAIL_FORMAT: &str = "Failed to format OS Error";

use core::fmt;
use core::marker::PhantomData;

///Static string used to store error message.
///
///Limited to 128 characters, truncates on overflow.
pub type Str = str_buf::StrBuf::<[u8; 128]>;

mod posix;
pub use posix::PosixCategory;
mod system;
pub use system::SystemCategory;

///Describes category of Error, defining it's semantics.
pub trait Category {
    /// Category's name, used in formatting to identify type of error code.
    const NAME: &'static str;

    /// Returns the explanatory text for the code.
    fn message(code: i32) -> Str;
}

impl Category for () {
    const NAME: &'static str = "Error code";

    #[inline(always)]
    fn message<'a>(_: i32) -> Str {
        Str::new()
    }
}

///Alias to Posix error code
pub type PosixError = ErrorCode<PosixCategory>;
///Alias to System error code
pub type SystemError = ErrorCode<SystemCategory>;
///Alias to Plain error code, without any extra category
pub type PlainError = ErrorCode<()>;

///Describes way to convert from one `Category` into another.
pub trait IntoCategory<C: Category>: Category + Sized {
    ///Converts error from category into own category.
    fn map_code(code: ErrorCode<Self>) -> ErrorCode<C>;
}

impl IntoCategory<SystemCategory> for PosixCategory {
    #[inline]
    fn map_code(code: ErrorCode<Self>) -> ErrorCode<SystemCategory> {
        ErrorCode::<SystemCategory>::new(code.raw_code())
    }
}

impl<T: Category> IntoCategory<T> for T {
    #[inline(always)]
    fn map_code(code: ErrorCode<Self>) -> ErrorCode<Self> {
        code
    }
}

///Identifies object as error code, allowing for it to be converted with right [Category](trait.CateCategory.html)
pub trait ErrorCodeEnum: Into<i32> {
    ///Specifies category of error code.
    type Category: Category;

    #[inline]
    ///Converts self into [ErrorCode](struct.ErrorCode.html)
    fn error_code(self) -> ErrorCode<Self::Category> {
        self.into().into()
    }
}

#[repr(transparent)]
///Describes error code in particular category.
pub struct ErrorCode<C> {
    code: i32,
    _category: PhantomData<C>,
}

impl ErrorCode<PosixCategory> {
    #[inline]
    ///Retrieves last error, generated by runtime
    pub fn last() -> Self {
        Self::new(posix::get_last_error())
    }

    #[inline]
    ///Creates `unimplemented` error
    pub const fn unimplemented() -> Self {
        Self::new(posix::get_unimplemented_error())
    }

    #[inline]
    ///Returns whether underlying error means to try again.
    ///
    ///Under POSIX, it means either `EWOULDBLOCK` or `EAGAIN`, in some cases it can be the same
    ///error code.
    pub const fn is_would_block(self) -> bool {
        posix::is_would_block(self.code)
    }
}

impl ErrorCode<SystemCategory> {
    #[inline]
    ///Retrieves last error, generated by OS
    pub fn last() -> Self {
        Self::new(system::get_last_error())
    }

    #[inline]
    ///Creates `unimplemented` error
    pub const fn unimplemented() -> Self {
        Self::new(system::get_unimplemented_error())
    }

    #[inline]
    ///Returns whether underlying error means to try again.
    ///
    ///Under POSIX, it means either `EWOULDBLOCK` or `EAGAIN`, in some cases it can be the same
    ///error code.
    ///In case of Windows, it is also `WSAEWOULDBLOCK`
    pub const fn is_would_block(self) -> bool {
        system::is_would_block(self.code)
    }
}

impl<C> ErrorCode<C> {
    #[inline]
    ///Creates new error code in provided category.
    pub const fn new(code: i32) -> Self {
        Self {
            code,
            _category: PhantomData,
        }
    }

    #[inline]
    ///Access raw integer code
    pub const fn raw_code(self) -> i32 {
        self.code
    }

    #[inline]
    ///Returns whether error code is zero.
    ///
    ///Commonly zero is indication of no error.
    pub const fn is_zero(self) -> bool {
        self.code == 0
    }
}

impl<C: Category> ErrorCode<C> {
    #[inline(always)]
    ///Returns textual representation of the error code
    pub fn message(self) -> Str {
        C::message(self.code)
    }

    #[inline]
    ///Converts self into error code of another category.
    ///
    ///Requires self's category to implement `IntoCategory` for destination category.
    pub fn into_another<O: Category>(self) -> ErrorCode<O> where C: IntoCategory<O> {
        C::map_code(self)
    }
}

impl<C> From<i32> for ErrorCode<C> {
    #[inline]
    fn from(code: i32) -> Self {
        ErrorCode {
            code,
            _category: PhantomData
        }
    }
}

impl<C> Clone for ErrorCode<C> {
    #[inline]
    fn clone(&self) -> Self {
        self.code.into()
    }

    #[inline]
    fn clone_from(&mut self, other: &Self) {
        self.code = other.code
    }
}

impl<C> Copy for ErrorCode<C> {}

impl<C> PartialEq for ErrorCode<C> {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.code == other.code
    }
}

impl<C> PartialOrd for ErrorCode<C> {
    #[inline]
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
        self.code.partial_cmp(&other.code)
    }
}
impl<C> Ord for ErrorCode<C> {
    #[inline]
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
        self.code.cmp(&other.code)
    }
}

impl<C> Eq for ErrorCode<C> {}

impl<C> core::hash::Hash for ErrorCode<C> {
    #[inline]
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
        self.code.hash(state);
    }
}

impl<C: Category> fmt::Debug for ErrorCode<C> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} {}", C::NAME, self.code)
    }
}

impl<C: Category> fmt::Display for ErrorCode<C> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} {}: {}", C::NAME, self.code, self.message())
    }
}

unsafe impl<C> Send for ErrorCode<C> {}
unsafe impl<C> Sync for ErrorCode<C> {}
impl<C> Unpin for ErrorCode<C> {}

#[cfg(feature = "std")]
impl<C: Category> std::error::Error for ErrorCode<C> {}

#[cfg(feature = "std")]
impl From<ErrorCode<PosixCategory>> for std::io::Error {
    #[inline]
    fn from(err: ErrorCode<PosixCategory>) -> Self {
        Self::from_raw_os_error(err.raw_code())
    }
}

#[cfg(feature = "std")]
impl From<std::io::Error> for ErrorCode<PosixCategory> {
    #[inline]
    fn from(err: std::io::Error) -> Self {
        match err.raw_os_error() {
            Some(err) => Self::new(err),
            None => Self::new(-1),
        }
    }
}

#[cfg(feature = "std")]
impl PartialEq<std::io::Error> for ErrorCode<PosixCategory> {
    #[inline]
    fn eq(&self, other: &std::io::Error) -> bool {
        if let Some(other) = other.raw_os_error() {
            self.code == other
        } else {
            false
        }
    }
}

#[cfg(feature = "std")]
impl From<ErrorCode<SystemCategory>> for std::io::Error {
    #[inline]
    fn from(err: ErrorCode<SystemCategory>) -> Self {
        Self::from_raw_os_error(err.raw_code())
    }
}

#[cfg(feature = "std")]
impl From<std::io::Error> for ErrorCode<SystemCategory> {
    #[inline]
    fn from(err: std::io::Error) -> Self {
        match err.raw_os_error() {
            Some(err) => Self::new(err),
            None => Self::new(-1),
        }
    }
}

#[cfg(feature = "std")]
impl PartialEq<std::io::Error> for ErrorCode<SystemCategory> {
    #[inline]
    fn eq(&self, other: &std::io::Error) -> bool {
        if let Some(other) = other.raw_os_error() {
            self.code == other
        } else {
            false
        }
    }
}