use core::{ffi, fmt, num::NonZeroI32, str};
use crate::{esp_err_t, esp_err_to_name, ESP_OK};
#[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 {
pub const fn from(error: esp_err_t) -> Option<Self> {
match NonZeroI32::new(error) {
None => None,
Some(err) => Some(Self(err)),
}
}
pub const fn from_non_zero(error: NonZeroI32) -> Self {
Self(error)
}
pub const fn from_infallible<const E: esp_err_t>() -> Self {
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
}
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)),
}
}
pub fn convert(error: esp_err_t) -> Result<(), Self> {
EspError::check_and_return(error, ())
}
#[track_caller]
pub fn panic(&self) {
panic!("ESP-IDF ERROR: {self}");
}
pub fn code(&self) -> esp_err_t {
self.0.get()
}
}
impl core::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())
}
}
#[macro_export]
macro_rules! esp {
($err:expr) => {{
$crate::EspError::convert($err as $crate::esp_err_t)
}};
}
#[macro_export]
macro_rules! esp_result {
($err:expr, $value:expr) => {{
$crate::EspError::check_and_return($err as $crate::esp_err_t, $value)
}};
}
#[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();
}
}};
}