1#![no_std]
18
19extern crate alloc;
21#[cfg(feature = "std")]
22extern crate std;
23
24use num_enum::{IntoPrimitive, TryFromPrimitive, TryFromPrimitiveError};
25
26#[derive(Default, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
34#[repr(transparent)]
35pub struct Error(u32);
36
37impl Error {
38 pub fn new(space: impl SpaceParam, code: impl CodeParam) -> Self {
40 Error::new_const(space.into(), code.into())
41 }
42
43 pub const fn new_const(space: u8, code: u16) -> Self {
45 Error((space as u32) << 16 | code as u32)
46 }
47
48 pub const fn space(self) -> u8 {
50 (self.0 >> 16) as u8
51 }
52
53 pub const fn code(self) -> u16 {
55 self.0 as u16
56 }
57
58 pub fn user(code: impl CodeParam) -> Self {
60 Error::new(Space::User, code)
61 }
62
63 pub fn internal(code: impl CodeParam) -> Self {
65 Error::new(Space::Internal, code)
66 }
67
68 pub fn world(code: impl CodeParam) -> Self {
70 Error::new(Space::World, code)
71 }
72
73 pub fn pop(self) -> Self {
77 let mut space = self.space();
78 match Space::try_from_primitive(space) {
79 Ok(Space::User) => space = Space::Internal as u8,
80 Ok(Space::Internal) => space = Space::World as u8,
81 _ => (),
82 }
83 let code = self.code();
84 Error::new_const(space, code)
85 }
86
87 pub fn decode(result: i32) -> Result<u32, Self> {
93 if result < 0 {
94 let error = !result as u32;
95 assert!(error & 0xff000000 == 0);
96 Err(Error(error))
97 } else {
98 Ok(result as u32)
99 }
100 }
101
102 pub fn encode(result: Result<u32, Self>) -> i32 {
108 match result {
109 Ok(value) => {
110 let value = value as i32;
111 assert!(0 <= value);
112 value
113 }
114 Err(Error(error)) => !error as i32,
115 }
116 }
117
118 pub fn check(self, cond: bool) -> Result<(), Self> {
120 match cond {
121 true => Ok(()),
122 false => Err(self),
123 }
124 }
125}
126
127pub trait SpaceParam: Into<u8> {}
128impl SpaceParam for Space {}
129impl SpaceParam for u8 {}
130
131pub trait CodeParam: Into<u16> {}
132impl CodeParam for Code {}
133impl CodeParam for u16 {}
134
135#[derive(Debug, Copy, Clone, TryFromPrimitive, IntoPrimitive)]
140#[cfg_attr(feature = "defmt", derive(defmt::Format))]
141#[non_exhaustive]
142#[repr(u8)]
143pub enum Space {
144 Generic = 0,
145
146 User = 1,
148
149 Internal = 2,
151
152 World = 3,
154}
155
156#[derive(Debug, Copy, Clone, TryFromPrimitive, IntoPrimitive)]
162#[cfg_attr(feature = "defmt", derive(defmt::Format))]
163#[non_exhaustive]
164#[repr(u16)]
165pub enum Code {
166 Generic = 0,
167
168 NotImplemented = 1,
170
171 NotFound = 2,
173
174 InvalidLength = 3,
176
177 InvalidAlign = 4,
179
180 NoPermission = 5,
182
183 NotEnough = 6,
185
186 InvalidState = 7,
191
192 InvalidArgument = 8,
197
198 OutOfBounds = 9,
200}
201
202impl core::fmt::Debug for Error {
203 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
204 write!(f, "{self}")
205 }
206}
207
208impl core::fmt::Display for Error {
209 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
210 match Space::try_from_primitive(self.space()) {
212 Ok(x) => write!(f, "{x:?}")?,
213 Err(TryFromPrimitiveError { number: x }) => write!(f, "[{x:02x}]")?,
214 }
215 write!(f, ":")?;
216 match Code::try_from_primitive(self.code()) {
217 Ok(x) => write!(f, "{x:?}"),
218 Err(TryFromPrimitiveError { number: x }) => write!(f, "[{x:04x}]"),
219 }
220 }
221}
222
223#[cfg(feature = "defmt")]
224impl defmt::Format for Error {
225 fn format(&self, fmt: defmt::Formatter) {
226 use defmt::write;
228 match Space::try_from_primitive(self.space()) {
229 Ok(x) => write!(fmt, "{:?}", x),
230 Err(TryFromPrimitiveError { number: x }) => write!(fmt, "[{:02x}]", x),
231 }
232 write!(fmt, ":");
233 match Code::try_from_primitive(self.code()) {
234 Ok(x) => write!(fmt, "{:?}", x),
235 Err(TryFromPrimitiveError { number: x }) => write!(fmt, "[{:04x}]", x),
236 }
237 }
238}
239
240impl core::error::Error for Error {}
241
242#[cfg(feature = "std")]
243impl From<std::io::Error> for Error {
244 fn from(_: std::io::Error) -> Self {
245 Error::world(Code::Generic)
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252
253 #[test]
254 fn new_ok() {
255 assert_eq!(Error::new(Space::User, Code::InvalidLength), Error(0x010003));
256 assert_eq!(Error::new(0xab, 0x1234u16), Error(0xab1234));
257 }
258}