#![no_std]
#![warn(missing_docs)]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::style))]
#[cfg(feature = "std")]
extern crate std;
use core::{mem, hash, fmt};
#[deprecated]
pub const UNKNOWN_ERROR: &str = "Unknown error";
pub const FAIL_ERROR_FORMAT: &str = "Failed to format error into utf-8";
pub const MESSAGE_BUF_SIZE: usize = 256;
pub type MessageBuf = [mem::MaybeUninit<u8>; MESSAGE_BUF_SIZE];
pub mod defs;
pub mod types;
pub mod utils;
mod posix;
pub use posix::POSIX_CATEGORY;
mod system;
pub use system::SYSTEM_CATEGORY;
#[macro_export]
macro_rules! define_category {
(
$(#[$docs:meta])*
pub enum $name:ident {
$(
#[doc = $msg:literal]
$ident:ident = $code:literal,
)+
}
) => {
#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord)]
#[repr(i32)]
$(#[$docs])*
pub enum $name {
$(
#[doc = $msg]
$ident = $code,
)+
}
impl From<$name> for $crate::ErrorCode {
#[inline(always)]
fn from(this: $name) -> $crate::ErrorCode {
this.into_error_code()
}
}
impl core::fmt::Display for $name {
#[inline(always)]
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
core::fmt::Display::fmt(&self.into_error_code(), fmt)
}
}
impl $name {
const _ASSERT: () = {
$(
assert!($msg.len() <= $crate::MESSAGE_BUF_SIZE, "Message buffer overflow, make sure your messages are not beyond MESSAGE_BUF_SIZE");
)+
};
#[inline(always)]
pub fn map_code(code: $crate::types::c_int) -> Option<&'static str> {
match code {
$($code => Some($msg),)+
_ => None,
}
}
fn message(code: $crate::types::c_int, out: &mut $crate::MessageBuf) -> &str {
let msg = match Self::map_code(code) {
Some(msg) => msg,
None => $crate::utils::generic_map_error_code(code),
};
debug_assert!(msg.len() <= out.len());
unsafe {
core::ptr::copy_nonoverlapping(msg.as_ptr(), out.as_mut_ptr() as *mut u8, msg.len());
core::str::from_utf8_unchecked(
core::slice::from_raw_parts(out.as_ptr() as *const u8, msg.len())
)
}
}
pub fn into_error_code(self) -> $crate::ErrorCode {
let _ = Self::_ASSERT;
static CATEGORY: $crate::Category = $crate::Category {
name: core::stringify!($name),
message: $name::message,
equivalent,
is_would_block
};
fn equivalent(code: $crate::types::c_int, other: &$crate::ErrorCode) -> bool {
core::ptr::eq(&CATEGORY, other.category()) && code == other.raw_code()
}
fn is_would_block(_: $crate::types::c_int) -> bool {
false
}
$crate::ErrorCode::new(self as _, &CATEGORY)
}
}
}
}
pub struct Category {
pub name: &'static str,
pub message: fn(types::c_int, &mut MessageBuf) -> &str,
pub equivalent: fn(types::c_int, &ErrorCode) -> bool,
pub is_would_block: fn(types::c_int) -> bool,
}
#[derive(Copy, Clone)]
pub struct ErrorCode {
code: types::c_int,
category: &'static Category
}
impl ErrorCode {
#[inline]
pub const fn new(code: types::c_int, category: &'static Category) -> Self {
Self {
code,
category,
}
}
#[inline(always)]
pub fn new_posix(code: types::c_int) -> Self {
Self::new(code, &POSIX_CATEGORY)
}
#[inline(always)]
pub fn new_system(code: types::c_int) -> Self {
Self::new(code, &SYSTEM_CATEGORY)
}
#[inline]
pub fn last_posix() -> Self {
Self::new_posix(posix::get_last_error())
}
#[inline]
pub fn last_system() -> Self {
Self::new_system(system::get_last_error())
}
#[inline(always)]
pub const fn raw_code(&self) -> types::c_int {
self.code
}
#[inline(always)]
pub const fn category(&self) -> &'static Category {
self.category
}
#[inline(always)]
pub fn is_would_block(&self) -> bool {
(self.category.is_would_block)(self.code)
}
}
impl PartialEq for ErrorCode {
#[inline]
fn eq(&self, other: &Self) -> bool {
(self.category.equivalent)(self.code, other)
}
}
impl Eq for ErrorCode {}
impl hash::Hash for ErrorCode {
#[inline]
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.code.hash(state);
}
}
impl fmt::Debug for ErrorCode {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut out = [mem::MaybeUninit::uninit(); MESSAGE_BUF_SIZE];
let message = (self.category.message)(self.code, &mut out);
fmt.debug_struct(self.category.name).field("code", &self.code).field("message", &message).finish()
}
}
impl fmt::Display for ErrorCode {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut out = [mem::MaybeUninit::uninit(); MESSAGE_BUF_SIZE];
let message = (self.category.message)(self.code, &mut out);
fmt.write_fmt(format_args!("{}({}): {}", self.category.name, self.code, message))
}
}
#[cfg(feature = "std")]
impl std::error::Error for ErrorCode {}
#[cfg(feature = "std")]
impl From<std::io::Error> for ErrorCode {
#[inline]
fn from(err: std::io::Error) -> Self {
match err.raw_os_error() {
Some(err) => Self::new_posix(err),
None => Self::new_posix(-1),
}
}
}