#![no_std]
#[cfg(feature = "std")]
extern crate std;
use num_enum::{IntoPrimitive, TryFromPrimitive, TryFromPrimitiveError};
#[derive(Default, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(from = "ParsedError", into = "ParsedError"))]
#[repr(transparent)]
pub struct Error(u32);
impl Error {
pub fn new(space: impl SpaceParam, code: impl CodeParam) -> Self {
Error::new_const(space.into(), code.into())
}
pub const fn new_const(space: u8, code: u16) -> Self {
Error(((space as u32) << 16) | code as u32)
}
pub const fn space(self) -> u8 {
(self.0 >> 16) as u8
}
pub const fn code(self) -> u16 {
self.0 as u16
}
pub fn user(code: impl CodeParam) -> Self {
Error::new(Space::User, code)
}
pub fn internal(code: impl CodeParam) -> Self {
Error::new(Space::Internal, code)
}
pub fn world(code: impl CodeParam) -> Self {
Error::new(Space::World, code)
}
pub fn pop(self) -> Self {
let mut space = self.space();
match Space::try_from_primitive(space) {
Ok(Space::User) => space = Space::Internal as u8,
Ok(Space::Internal) => space = Space::World as u8,
_ => (),
}
let code = self.code();
Error::new_const(space, code)
}
pub fn decode(result: i32) -> Result<u32, Self> {
if result < 0 {
let error = !result as u32;
assert!(error & 0xff000000 == 0);
Err(Error(error))
} else {
Ok(result as u32)
}
}
pub fn encode(result: Result<u32, Self>) -> i32 {
match result {
Ok(value) => {
let value = value as i32;
assert!(0 <= value);
value
}
Err(Error(error)) => !error as i32,
}
}
pub fn check(self, cond: bool) -> Result<(), Self> {
match cond {
true => Ok(()),
false => Err(self),
}
}
}
pub trait SpaceParam: Into<u8> {}
impl SpaceParam for Space {}
impl SpaceParam for u8 {}
pub trait CodeParam: Into<u16> {}
impl CodeParam for Code {}
impl CodeParam for u16 {}
#[derive(Debug, Copy, Clone, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
#[repr(u8)]
pub enum Space {
Generic = 0,
User = 1,
Internal = 2,
World = 3,
}
#[derive(Debug, Copy, Clone, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
#[repr(u16)]
pub enum Code {
Generic = 0,
NotImplemented = 1,
NotFound = 2,
InvalidLength = 3,
InvalidAlign = 4,
NoPermission = 5,
NotEnough = 6,
InvalidState = 7,
InvalidArgument = 8,
OutOfBounds = 9,
}
impl core::fmt::Debug for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{self}")
}
}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match Space::try_from_primitive(self.space()) {
Ok(x) => write!(f, "{x:?}")?,
Err(TryFromPrimitiveError { number: x }) => write!(f, "[{x:02x}]")?,
}
write!(f, ":")?;
match Code::try_from_primitive(self.code()) {
Ok(x) => write!(f, "{x:?}"),
Err(TryFromPrimitiveError { number: x }) => write!(f, "[{x:04x}]"),
}
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for Error {
fn format(&self, fmt: defmt::Formatter) {
use defmt::write;
match Space::try_from_primitive(self.space()) {
Ok(x) => write!(fmt, "{:?}", x),
Err(TryFromPrimitiveError { number: x }) => write!(fmt, "[{:02x}]", x),
}
write!(fmt, ":");
match Code::try_from_primitive(self.code()) {
Ok(x) => write!(fmt, "{:?}", x),
Err(TryFromPrimitiveError { number: x }) => write!(fmt, "[{:04x}]", x),
}
}
}
impl core::error::Error for Error {}
impl From<&Error> for Error {
fn from(x: &Error) -> Self {
*x
}
}
#[cfg(feature = "std")]
impl From<std::io::Error> for Error {
fn from(_: std::io::Error) -> Self {
Error::world(Code::Generic)
}
}
#[cfg(feature = "serde")]
#[derive(serde::Serialize, serde::Deserialize)]
struct ParsedError {
space: u8,
code: u16,
}
#[cfg(feature = "serde")]
impl From<ParsedError> for Error {
fn from(x: ParsedError) -> Self {
Error::new(x.space, x.code)
}
}
#[cfg(feature = "serde")]
impl From<Error> for ParsedError {
fn from(x: Error) -> Self {
ParsedError { space: x.space(), code: x.code() }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_ok() {
assert_eq!(Error::new(Space::User, Code::InvalidLength), Error(0x010003));
assert_eq!(Error::new(0xab, 0x1234u16), Error(0xab1234));
}
fn _as_ref_ok() -> Result<(), Error> {
let &() = Err(Error::new(0, 0)).as_ref()?;
Ok(())
}
}