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#[cfg(feature = "std")]
20extern crate std;
21
22use num_enum::{IntoPrimitive, TryFromPrimitive, TryFromPrimitiveError};
23
24/// API errors.
25///
26/// Errors are equivalent to `u32` but only using the 24 least significant bits: 8 bits for the
27/// error space and 16 bits for the error code. The 8 most significant bits are zero. It is possible
28/// to encode a `Result<u32, Error>` into a `i32` as long as the success value only uses the 31
29/// least significant bits. Non-negative values encode success, while negative values encode the
30/// error by taking its bitwise complement (thus setting the 8 most significant bits to 1).
31#[derive(Default, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
32#[repr(transparent)]
33pub struct Error(u32);
34
35impl Error {
36    /// Creates a new error.
37    pub fn new(space: impl SpaceParam, code: impl CodeParam) -> Self {
38        Error::new_const(space.into(), code.into())
39    }
40
41    /// Creates a new error at compile-time.
42    pub const fn new_const(space: u8, code: u16) -> Self {
43        Error(((space as u32) << 16) | code as u32)
44    }
45
46    /// Returns the error space.
47    pub const fn space(self) -> u8 {
48        (self.0 >> 16) as u8
49    }
50
51    /// Returns the error code.
52    pub const fn code(self) -> u16 {
53        self.0 as u16
54    }
55
56    /// Creates a user error.
57    pub fn user(code: impl CodeParam) -> Self {
58        Error::new(Space::User, code)
59    }
60
61    /// Creates an internal error.
62    pub fn internal(code: impl CodeParam) -> Self {
63        Error::new(Space::Internal, code)
64    }
65
66    /// Creates a world error.
67    pub fn world(code: impl CodeParam) -> Self {
68        Error::new(Space::World, code)
69    }
70
71    /// Pops the error one level up.
72    ///
73    /// User errors become internal errors. Internal errors become world errors.
74    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    /// Decodes a signed integer as a result (where errors are negative values).
86    ///
87    /// # Panics
88    ///
89    /// Panics if the signed integer is smaller than -16777216 (0xff000000).
90    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    /// Encodes a result (where errors are negative values) as a signed integer.
101    ///
102    /// # Panics
103    ///
104    /// Panics if the result is a success greater than 2147483647 (0x7fffffff).
105    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    /// Ensures a condition is true, otherwise returns the error.
117    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/// Common error spaces.
134///
135/// Values from 0 to 127 (0x7f) are reserved for common error spaces and defined by this enum.
136/// Values from 128 (0x80) to 255 (0xff) are reserved for implementation-specific error spaces.
137#[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    /// The user made an error.
145    User = 1,
146
147    /// The implementation failed.
148    Internal = 2,
149
150    /// The world returned an error.
151    World = 3,
152}
153
154/// Common error codes.
155///
156/// Values from 0 to 32767 (0x7fff) are reserved for common error codes and defined by this enum.
157/// Values from 32768 (0x8000) to 65535 (0xffff) are reserved for implementation-specific error
158/// codes.
159#[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    /// The operation is not implemented.
167    NotImplemented = 1,
168
169    /// An input was not found.
170    NotFound = 2,
171
172    /// An input has an invalid length.
173    InvalidLength = 3,
174
175    /// An input has an invalid alignment.
176    InvalidAlign = 4,
177
178    /// The caller does not have permission for the operation.
179    NoPermission = 5,
180
181    /// There is not enough resource for the operation.
182    NotEnough = 6,
183
184    /// An input is invalid for the current state.
185    ///
186    /// This could also be that the current state is invalid for all inputs and cannot progress
187    /// anymore.
188    InvalidState = 7,
189
190    /// An input is invalid.
191    ///
192    /// This is a generic error. More precise errors would be `InvalidLength`, `InvalidAlign`,
193    /// `InvalidState`, or `NotFound` for example.
194    InvalidArgument = 8,
195
196    /// An input is out of bounds.
197    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        // Keep in sync with defmt::Format.
209        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        // Keep in sync with core::fmt::Display.
225        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}