use core::{ffi, fmt, num::NonZeroI32, slice, str};
use crate::{esp_err_t, esp_err_to_name, ESP_OK};
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
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()
}
}
#[cfg(feature = "std")]
impl std::error::Error for EspError {}
impl fmt::Display for EspError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe fn strlen(c_s: *const ffi::c_char) -> usize {
let mut len = 0;
while *c_s.offset(len) != 0 {
len += 1;
}
len as usize
}
unsafe {
let c_s = esp_err_to_name(self.code());
str::from_utf8_unchecked(slice::from_raw_parts(c_s as *const u8, strlen(c_s))).fmt(f)
}
}
}
#[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();
}
}};
}