wasefire_error/
lib.rs

1// Copyright 2023 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Applet and board API errors.
16
17#![no_std]
18
19// TODO(https://github.com/rust-lang/rust/issues/122105): Remove when fixed.
20extern crate alloc;
21#[cfg(feature = "std")]
22extern crate std;
23
24use num_enum::{IntoPrimitive, TryFromPrimitive, TryFromPrimitiveError};
25
26/// API errors.
27///
28/// Errors are equivalent to `u32` but only using the 24 least significant bits: 8 bits for the
29/// error space and 16 bits for the error code. The 8 most significant bits are zero. It is possible
30/// to encode a `Result<u32, Error>` into a `i32` as long as the success value only uses the 31
31/// least significant bits. Non-negative values encode success, while negative values encode the
32/// error by taking its bitwise complement (thus setting the 8 most significant bits to 1).
33#[derive(Default, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
34#[repr(transparent)]
35pub struct Error(u32);
36
37impl Error {
38    /// Creates a new error.
39    pub fn new(space: impl SpaceParam, code: impl CodeParam) -> Self {
40        Error::new_const(space.into(), code.into())
41    }
42
43    /// Creates a new error at compile-time.
44    pub const fn new_const(space: u8, code: u16) -> Self {
45        Error((space as u32) << 16 | code as u32)
46    }
47
48    /// Returns the error space.
49    pub const fn space(self) -> u8 {
50        (self.0 >> 16) as u8
51    }
52
53    /// Returns the error code.
54    pub const fn code(self) -> u16 {
55        self.0 as u16
56    }
57
58    /// Creates a user error.
59    pub fn user(code: impl CodeParam) -> Self {
60        Error::new(Space::User, code)
61    }
62
63    /// Creates an internal error.
64    pub fn internal(code: impl CodeParam) -> Self {
65        Error::new(Space::Internal, code)
66    }
67
68    /// Creates a world error.
69    pub fn world(code: impl CodeParam) -> Self {
70        Error::new(Space::World, code)
71    }
72
73    /// Pops the error one level up.
74    ///
75    /// User errors become internal errors. Internal errors become world errors.
76    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    /// Decodes a signed integer as a result (where errors are negative values).
88    ///
89    /// # Panics
90    ///
91    /// Panics if the signed integer is smaller than -16777216 (0xff000000).
92    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    /// Encodes a result (where errors are negative values) as a signed integer.
103    ///
104    /// # Panics
105    ///
106    /// Panics if the result is a success greater than 2147483647 (0x7fffffff).
107    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    /// Ensures a condition is true, otherwise returns the error.
119    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/// Common error spaces.
136///
137/// Values from 0 to 127 (0x7f) are reserved for common error spaces and defined by this enum.
138/// Values from 128 (0x80) to 255 (0xff) are reserved for implementation-specific error spaces.
139#[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    /// The user made an error.
147    User = 1,
148
149    /// The implementation failed.
150    Internal = 2,
151
152    /// The world returned an error.
153    World = 3,
154}
155
156/// Common error codes.
157///
158/// Values from 0 to 32767 (0x7fff) are reserved for common error codes and defined by this enum.
159/// Values from 32768 (0x8000) to 65535 (0xffff) are reserved for implementation-specific error
160/// codes.
161#[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    /// The operation is not implemented.
169    NotImplemented = 1,
170
171    /// An input was not found.
172    NotFound = 2,
173
174    /// An input has an invalid length.
175    InvalidLength = 3,
176
177    /// An input has an invalid alignment.
178    InvalidAlign = 4,
179
180    /// The caller does not have permission for the operation.
181    NoPermission = 5,
182
183    /// There is not enough resource for the operation.
184    NotEnough = 6,
185
186    /// An input is invalid for the current state.
187    ///
188    /// This could also be that the current state is invalid for all inputs and cannot progress
189    /// anymore.
190    InvalidState = 7,
191
192    /// An input is invalid.
193    ///
194    /// This is a generic error. More precise errors would be `InvalidLength`, `InvalidAlign`,
195    /// `InvalidState`, or `NotFound` for example.
196    InvalidArgument = 8,
197
198    /// An input is out of bounds.
199    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        // Keep in sync with defmt::Format.
211        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        // Keep in sync with core::fmt::Display.
227        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}