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