Skip to main content

osiris/
error.rs

1//! Utility functions and definitions for the kernel.
2#![cfg_attr(feature = "nightly", feature(likely_unlikely))]
3
4#[cfg(feature = "error-msg")]
5use core::fmt::{self, Write};
6use core::fmt::{Debug, Display};
7
8/// These two definitions are copied from https://github.com/rust-lang/hashbrown
9#[cfg(not(feature = "nightly"))]
10#[allow(unused_imports)]
11pub(crate) use core::convert::{identity as likely, identity as unlikely};
12
13#[cfg(feature = "nightly")]
14pub(crate) use core::hint::{likely, unlikely};
15
16pub type Result<T> = core::result::Result<T, Error>;
17pub use hal_api::PosixError;
18/// This is a macro that is used to panic when a bug is detected.
19/// It is similar to the BUG() macro in the Linux kernel. Link: [https://www.kernel.org/]()
20#[macro_export]
21macro_rules! bug {
22    () => {
23        panic!("BUG at {}:{}", file!(), line!());
24    };
25    ($fmt:literal $(, $arg:expr)* $(,)?) => {{
26        panic!(concat!("BUG at {}:{}: ", $fmt), file!(), line!() $(, $arg)*);
27    }};
28}
29
30#[macro_export]
31macro_rules! warn {
32    () => {
33        $crate::kprintln!("WARN at {}:{}", file!(), line!());
34    };
35    ($fmt:literal $(, $arg:expr)* $(,)?) => {{
36        $crate::kprintln!(concat!("WARN at {}:{}: ", $fmt), file!(), line!() $(, $arg)*);
37    }};
38}
39
40/// This is a macro that is used to panic when a condition is true.
41/// It is similar to the BUG_ON() macro in the Linux kernel.  Link: [https://www.kernel.org/]()
42macro_rules! bug_on {
43    ($cond:expr) => {{
44        let cond = $cond;
45        #[allow(unused_unsafe)]
46        if unsafe { $crate::error::unlikely(cond) } {
47            panic!("BUG({}) at {}:{}", stringify!($cond), file!(), line!());
48        }
49    }};
50    ($cond:expr, $fmt:literal $(, $arg:expr)* $(,)?) => {{
51        let cond = $cond;
52        #[allow(unused_unsafe)]
53        if unsafe { $crate::error::unlikely(cond) } {
54            panic!(concat!("BUG({}) at {}:{}: ", $fmt), stringify!($cond), file!(), line!() $(, $arg)*);
55        }
56    }};
57}
58
59#[allow(unused_macros)]
60macro_rules! warn_on {
61    ($cond:expr) => {{
62        let cond = $cond;
63        #[allow(unused_unsafe)]
64        if unsafe { $crate::error::unlikely(cond) } {
65            kprintln!("WARN({}) at {}:{}", stringify!($cond), file!(), line!());
66        }
67    }};
68    ($cond:expr, $fmt:literal $(, $arg:expr)* $(,)?) => {{
69        let cond = $cond;
70        #[allow(unused_unsafe)]
71        if unsafe { $crate::error::unlikely(cond) } {
72            kprintln!(concat!("WARN({}) at {}:{}: ", $fmt), stringify!($cond), file!(), line!() $(, $arg)*);
73        }
74    }};
75}
76
77macro_rules! kerr {
78    ($posix:ident) => {
79        $crate::error::Error::new($crate::error::PosixError::$posix)
80    };
81    ($posix:ident, $msg:expr) => {{
82        #[cfg(feature = "error-msg")]
83        {
84            $crate::error::Error::new($crate::error::PosixError::$posix).with_msg($msg)
85        }
86        #[cfg(not(feature = "error-msg"))]
87        {
88            $crate::error::Error::new($crate::error::PosixError::$posix)
89        }
90    }};
91}
92
93#[derive(Clone, Eq)]
94pub struct Error {
95    pub kind: PosixError,
96    #[cfg(feature = "error-msg")]
97    msg: Option<Msg>,
98}
99
100#[cfg(feature = "error-msg")]
101struct Msg {
102    buf: [u8; 128],
103    len: usize,
104}
105
106#[cfg(feature = "error-msg")]
107impl Msg {
108    fn new(args: fmt::Arguments<'_>) -> Self {
109        let mut msg = Self {
110            buf: [0; 128],
111            len: 0,
112        };
113        let _ = msg.write_fmt(args);
114        msg
115    }
116
117    fn as_str(&self) -> &str {
118        // Safety: `Msg` is only written through `fmt::Write::write_str`, which
119        // copies from valid UTF-8 string slices and only truncates at char boundaries.
120        unsafe { core::str::from_utf8_unchecked(&self.buf[..self.len]) }
121    }
122}
123
124#[cfg(feature = "error-msg")]
125impl Write for Msg {
126    fn write_str(&mut self, s: &str) -> fmt::Result {
127        let remaining = self.buf.len() - self.len;
128        let mut write_len = remaining.min(s.len());
129
130        while !s.is_char_boundary(write_len) {
131            write_len -= 1;
132        }
133
134        self.buf[self.len..self.len + write_len].copy_from_slice(&s.as_bytes()[..write_len]);
135        self.len += write_len;
136        Ok(())
137    }
138}
139
140impl Error {
141    pub fn new(kind: PosixError) -> Self {
142        #[cfg(feature = "error-msg")]
143        {
144            Self { kind, msg: None }
145        }
146        #[cfg(not(feature = "error-msg"))]
147        {
148            Self { kind }
149        }
150    }
151
152    #[cfg(feature = "error-msg")]
153    pub fn with_msg(mut self, msg: fmt::Arguments<'_>) -> Self {
154        self.msg = Some(Msg::new(msg));
155        self
156    }
157}
158
159impl Debug for Error {
160    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
161        #[cfg(feature = "error-msg")]
162        {
163            match &self.msg {
164                Some(msg) => write!(f, "{}: {}", self.kind, msg.as_str()),
165                None => write!(f, "{}", self.kind),
166            }
167        }
168        #[cfg(not(feature = "error-msg"))]
169        {
170            write!(f, "{}", self.kind)
171        }
172    }
173}
174
175impl Display for Error {
176    #[cfg(not(feature = "error-msg"))]
177    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
178        write!(f, "{}", self.kind)
179    }
180
181    #[cfg(feature = "error-msg")]
182    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
183        match &self.msg {
184            Some(msg) => write!(f, "{}: {}", self.kind, msg.as_str()),
185            None => write!(f, "{}", self.kind),
186        }
187    }
188}
189
190impl From<PosixError> for Error {
191    fn from(e: PosixError) -> Self {
192        Self::new(e)
193    }
194}
195
196impl PartialEq for Error {
197    fn eq(&self, other: &Self) -> bool {
198        self.kind == other.kind
199    }
200}
201
202#[cfg(all(test, feature = "error-msg"))]
203mod tests {
204    use super::*;
205
206    #[test]
207    fn kerr_formats_captured_message() {
208        let compatible = "sensor";
209        let ordinal = 2usize;
210        let err = kerr!(
211            NotFound,
212            "i2c device not found: compatible={compatible}, ordinal={ordinal}"
213        );
214
215        assert_eq!(
216            format!("{err}"),
217            "Not found: i2c device not found: compatible=sensor, ordinal=2"
218        );
219    }
220}