Skip to main content

esp_idf_sys/
error.rs

1use core::{ffi, fmt, num::NonZeroI32, str};
2
3use crate::{esp_err_t, esp_err_to_name, ESP_OK};
4
5/// A wrapped [`esp_err_t`] to check if an error occurred.
6///
7/// An [`esp_err_t`] is returned from most esp-idf APIs as a status code. If it is equal
8/// to [`ESP_OK`] it means **no** error occurred.
9#[repr(transparent)]
10#[derive(Copy, Clone, Eq, PartialEq, Hash)]
11pub struct EspError(NonZeroI32);
12
13const _: () = if ESP_OK != 0 {
14    panic!("ESP_OK *has* to be 0")
15};
16
17impl EspError {
18    /// Wrap an [`esp_err_t`], return [`Some`] if `error` is **not** [`ESP_OK`].
19    pub const fn from(error: esp_err_t) -> Option<Self> {
20        match NonZeroI32::new(error) {
21            None => None,
22            Some(err) => Some(Self(err)),
23        }
24    }
25
26    /// Wrap a [`NonZeroI32`]. Since [`ESP_OK`] is 0, this can never fail;
27    pub const fn from_non_zero(error: NonZeroI32) -> Self {
28        Self(error)
29    }
30
31    /// Wrap an [`esp_err_t`], throw a compile time error if `error` is [`ESP_OK`].
32    pub const fn from_infallible<const E: esp_err_t>() -> Self {
33        // workaround until feature(inline_const) is stabilized: https://github.com/rust-lang/rust/pull/104087
34        struct Dummy<const D: esp_err_t>;
35        impl<const D: esp_err_t> Dummy<D> {
36            pub const ERR: EspError = match EspError::from(D) {
37                Some(err) => err,
38                None => panic!("ESP_OK can't be an error"),
39            };
40        }
41        Dummy::<E>::ERR
42    }
43
44    /// Convert `error` into a [`Result`] with `Ok(value)` if no error occurred.
45    ///
46    /// If `error` is [`ESP_OK`] return [`Ok`] of `value` otherwise return [`Err`] of
47    /// wrapped `error`.
48    pub fn check_and_return<T>(error: esp_err_t, value: T) -> Result<T, Self> {
49        match NonZeroI32::new(error) {
50            None => Ok(value),
51            Some(err) => Err(Self(err)),
52        }
53    }
54
55    /// Convert `error` into a [`Result`] with `Ok(())` if not error occurred..
56    ///
57    /// If `error` equals to [`ESP_OK`] return [`Ok`], otherwise return [`Err`] with the
58    /// wrapped [`esp_err_t`].
59    pub fn convert(error: esp_err_t) -> Result<(), Self> {
60        EspError::check_and_return(error, ())
61    }
62
63    /// Panic with a specific error message of the contained [`esp_err_t`].
64    #[track_caller]
65    pub fn panic(&self) {
66        panic!("ESP-IDF ERROR: {self}");
67    }
68
69    /// Get the wrapped [`esp_err_t`].
70    pub fn code(&self) -> esp_err_t {
71        self.0.get()
72    }
73}
74
75impl core::error::Error for EspError {}
76
77impl fmt::Display for EspError {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        unsafe {
80            let s = ffi::CStr::from_ptr(esp_err_to_name(self.code()));
81            core::fmt::Display::fmt(&str::from_utf8_unchecked(s.to_bytes()), f)
82        }
83    }
84}
85
86impl fmt::Debug for EspError {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        write!(f, "{} (error code {})", self, self.code())
89    }
90}
91
92/// Convert an [`esp_err_t`] into a [`Result<(), EspError>`](Result).
93///
94/// See [`EspError::convert`].
95#[macro_export]
96macro_rules! esp {
97    ($err:expr) => {{
98        $crate::EspError::convert($err as $crate::esp_err_t)
99    }};
100}
101
102/// Convert an [`esp_err_t`] into a [`Result<T, EspError>`](Result).
103///
104/// See [`EspError::check_and_return`].
105#[macro_export]
106macro_rules! esp_result {
107    ($err:expr, $value:expr) => {{
108        $crate::EspError::check_and_return($err as $crate::esp_err_t, $value)
109    }};
110}
111
112/// Panic with an error-specific message if `err` is not [`ESP_OK`].
113///
114/// See [`EspError::from`] and [`EspError::panic`].
115#[macro_export]
116macro_rules! esp_nofail {
117    ($err:expr) => {{
118        if let ::core::option::Option::Some(error) =
119            $crate::EspError::from($err as $crate::esp_err_t)
120        {
121            error.panic();
122        }
123    }};
124}