1#![no_std]
18
19#[cfg(feature = "std")]
20extern crate std;
21
22use num_enum::{IntoPrimitive, TryFromPrimitive, TryFromPrimitiveError};
23
24#[derive(Default, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
32#[repr(transparent)]
33pub struct Error(u32);
34
35impl Error {
36 pub fn new(space: impl SpaceParam, code: impl CodeParam) -> Self {
38 Error::new_const(space.into(), code.into())
39 }
40
41 pub const fn new_const(space: u8, code: u16) -> Self {
43 Error(((space as u32) << 16) | code as u32)
44 }
45
46 pub const fn space(self) -> u8 {
48 (self.0 >> 16) as u8
49 }
50
51 pub const fn code(self) -> u16 {
53 self.0 as u16
54 }
55
56 pub fn user(code: impl CodeParam) -> Self {
58 Error::new(Space::User, code)
59 }
60
61 pub fn internal(code: impl CodeParam) -> Self {
63 Error::new(Space::Internal, code)
64 }
65
66 pub fn world(code: impl CodeParam) -> Self {
68 Error::new(Space::World, code)
69 }
70
71 pub fn pop(self) -> Self {
75 let mut space = self.space();
76 match Space::try_from_primitive(space) {
77 Ok(Space::User) => space = Space::Internal as u8,
78 Ok(Space::Internal) => space = Space::World as u8,
79 _ => (),
80 }
81 let code = self.code();
82 Error::new_const(space, code)
83 }
84
85 pub fn decode(result: i32) -> Result<u32, Self> {
91 if result < 0 {
92 let error = !result as u32;
93 assert!(error & 0xff000000 == 0);
94 Err(Error(error))
95 } else {
96 Ok(result as u32)
97 }
98 }
99
100 pub fn encode(result: Result<u32, Self>) -> i32 {
106 match result {
107 Ok(value) => {
108 let value = value as i32;
109 assert!(0 <= value);
110 value
111 }
112 Err(Error(error)) => !error as i32,
113 }
114 }
115
116 pub fn check(self, cond: bool) -> Result<(), Self> {
118 match cond {
119 true => Ok(()),
120 false => Err(self),
121 }
122 }
123}
124
125pub trait SpaceParam: Into<u8> {}
126impl SpaceParam for Space {}
127impl SpaceParam for u8 {}
128
129pub trait CodeParam: Into<u16> {}
130impl CodeParam for Code {}
131impl CodeParam for u16 {}
132
133#[derive(Debug, Copy, Clone, TryFromPrimitive, IntoPrimitive)]
138#[cfg_attr(feature = "defmt", derive(defmt::Format))]
139#[non_exhaustive]
140#[repr(u8)]
141pub enum Space {
142 Generic = 0,
143
144 User = 1,
146
147 Internal = 2,
149
150 World = 3,
152}
153
154#[derive(Debug, Copy, Clone, TryFromPrimitive, IntoPrimitive)]
160#[cfg_attr(feature = "defmt", derive(defmt::Format))]
161#[non_exhaustive]
162#[repr(u16)]
163pub enum Code {
164 Generic = 0,
165
166 NotImplemented = 1,
168
169 NotFound = 2,
171
172 InvalidLength = 3,
174
175 InvalidAlign = 4,
177
178 NoPermission = 5,
180
181 NotEnough = 6,
183
184 InvalidState = 7,
189
190 InvalidArgument = 8,
195
196 OutOfBounds = 9,
198}
199
200impl core::fmt::Debug for Error {
201 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
202 write!(f, "{self}")
203 }
204}
205
206impl core::fmt::Display for Error {
207 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
208 match Space::try_from_primitive(self.space()) {
210 Ok(x) => write!(f, "{x:?}")?,
211 Err(TryFromPrimitiveError { number: x }) => write!(f, "[{x:02x}]")?,
212 }
213 write!(f, ":")?;
214 match Code::try_from_primitive(self.code()) {
215 Ok(x) => write!(f, "{x:?}"),
216 Err(TryFromPrimitiveError { number: x }) => write!(f, "[{x:04x}]"),
217 }
218 }
219}
220
221#[cfg(feature = "defmt")]
222impl defmt::Format for Error {
223 fn format(&self, fmt: defmt::Formatter) {
224 use defmt::write;
226 match Space::try_from_primitive(self.space()) {
227 Ok(x) => write!(fmt, "{:?}", x),
228 Err(TryFromPrimitiveError { number: x }) => write!(fmt, "[{:02x}]", x),
229 }
230 write!(fmt, ":");
231 match Code::try_from_primitive(self.code()) {
232 Ok(x) => write!(fmt, "{:?}", x),
233 Err(TryFromPrimitiveError { number: x }) => write!(fmt, "[{:04x}]", x),
234 }
235 }
236}
237
238impl core::error::Error for Error {}
239
240#[cfg(feature = "std")]
241impl From<std::io::Error> for Error {
242 fn from(_: std::io::Error) -> Self {
243 Error::world(Code::Generic)
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250
251 #[test]
252 fn new_ok() {
253 assert_eq!(Error::new(Space::User, Code::InvalidLength), Error(0x010003));
254 assert_eq!(Error::new(0xab, 0x1234u16), Error(0xab1234));
255 }
256}