error-code 0.1.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
//!
//! # Requirements
//!
//! - `alloc` - Crate uses allocator to create dynamic message, when necessary (Only [System](struct.SystemCategory.html) category uses heap on Windows).
//!
//! # Features
//!
//! - `std` - enables `std::error::Error` implementation
//! - `ufmt` - enables `ufmt` formatting 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)]

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

const UNKNOWN_ERROR: &str = "Unknown error";

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

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<'a>(code: i32) -> alloc::borrow::Cow<'a, str>;
}

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

    #[inline(always)]
    fn message<'a>(_: i32) -> alloc::borrow::Cow<'a, str> {
        alloc::borrow::Cow::Borrowed("")
    }
}

///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 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())
    }
}

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

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

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> {
    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> {
    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, C::message(self.code))
    }
}

impl<C: Category> fmt::Display for ErrorCode<C> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        <Self as  core::fmt::Debug>::fmt(self, f)
    }
}

#[cfg(feature = "ufmt")]
impl<C: Category> ufmt::uDebug for ErrorCode<C> {
    fn fmt<W: ufmt::uWrite + ?Sized>(&self, f: &mut ufmt::Formatter<'_, W>) -> Result<(), W::Error> {
        f.write_str(C::NAME)?;

        {
            match self.code {
                0 => f.write_str("0: "),
                mut code => {
                    let is_neg = if code < 0 {
                        code = -code;
                        true
                    } else {
                        false
                    };

                    let mut buffer = unsafe {
                        core::mem::MaybeUninit::<[u8; 12]>::uninit().assume_init()
                    };
                    let mut buffer_idx = 0;
                    while code != 0 {
                        let rem = code % 10;
                        unsafe {
                            *buffer.get_unchecked_mut(buffer_idx) = rem as u8 + b'0';
                        }
                        buffer_idx += 1;
                        code = code / 10;
                    }

                    if is_neg {
                        unsafe {
                            *buffer.get_unchecked_mut(buffer_idx) = b'-';
                        }
                        buffer_idx += 1;
                    }

                    f.write_str(unsafe {
                        let buffer = &mut buffer[..buffer_idx];
                        buffer.reverse();
                        core::str::from_utf8_unchecked(buffer)
                    })?;

                    f.write_str(": ")
                },
            }?
        }

        f.write_str(&C::message(self.code))
    }
}

#[cfg(feature = "ufmt")]
impl<C: Category> ufmt::uDisplay for ErrorCode<C> {
    #[inline]
    fn fmt<W: ufmt::uWrite + ?Sized>(&self, f: &mut ufmt::Formatter<'_, W>) -> Result<(), W::Error> {
        <Self as ufmt::uDebug>::fmt(self, f)
    }
}

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