use core::{array::TryFromSliceError, fmt, str::Utf8Error};
#[cfg(all(feature = "alloc", feature = "backtrace"))]
use alloc::{boxed::Box, string::ToString};
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ErrorCode {
AttributeNotFound,
AttributeIsCustom,
BufferTooSmall,
ClusterNotFound,
CommandNotFound,
Duplicate,
NodeNotFound,
EndpointNotFound,
EventNotFound,
InvalidAction,
InvalidCommand,
FailSafeRequired,
NeedsTimedInteraction,
ConstraintError,
DynamicConstraintError,
InvalidDataType,
UnsupportedAccess,
ResourceExhausted,
Busy,
DataVersionMismatch,
BtpError,
MdnsError,
NoCommand,
NoEndpoint,
NoExchange,
NoFabricId,
NoHandler,
NoNetworkInterface,
DBusError,
NoNodeId,
NoMemory,
NoSession,
NoSpace,
NoSpaceExchanges,
NoSpaceSessions,
TxTimeout,
RxTimeout,
NoTagFound,
NotFound,
PacketPoolExhaust,
StdIoError,
SysTimeFail,
Invalid,
InvalidAAD,
InvalidData,
InvalidKeyLength,
InvalidOpcode,
InvalidProto,
InvalidPeerAddr,
InvalidAuthKey,
InvalidSignature,
InvalidState,
InvalidTime,
InvalidArgument,
RwLock,
TLVNotFound,
TLVTypeMismatch,
TruncatedPacket,
Utf8Fail,
GennCommInvalidAuthentication,
NocInvalidNoc,
NocInvalidPublicKey,
NocMissingCsr,
NocFabricTableFull,
NocFabricConflict,
NocLabelConflict,
NocInvalidFabricIndex,
NocInvalidAdminSubject,
Failure,
CdInvalidFormat,
CdInvalidSignature,
CdSigningKeyNotFound,
CdInvalidVendorId,
CdInvalidProductId,
CdInvalidPaa,
}
impl From<ErrorCode> for Error {
fn from(code: ErrorCode) -> Self {
Self::new(code)
}
}
pub struct Error {
code: ErrorCode,
#[cfg(all(feature = "std", feature = "backtrace"))]
backtrace: std::backtrace::Backtrace,
#[cfg(all(feature = "alloc", feature = "backtrace"))]
inner: Option<Box<dyn core::error::Error + Send + Sync>>,
}
impl Error {
pub fn new(code: ErrorCode) -> Self {
Self {
code,
#[cfg(all(feature = "std", feature = "backtrace"))]
backtrace: std::backtrace::Backtrace::capture(),
#[cfg(all(feature = "alloc", feature = "backtrace"))]
inner: None,
}
}
#[cfg(all(feature = "alloc", feature = "backtrace"))]
pub fn new_with_details(
code: ErrorCode,
detailed_err: Box<dyn core::error::Error + Send + Sync>,
) -> Self {
Self {
code,
#[cfg(feature = "std")]
backtrace: std::backtrace::Backtrace::capture(),
inner: Some(detailed_err),
}
}
pub const fn code(&self) -> ErrorCode {
self.code
}
#[cfg(all(feature = "std", feature = "backtrace"))]
pub const fn backtrace(&self) -> &std::backtrace::Backtrace {
&self.backtrace
}
#[cfg(all(feature = "alloc", feature = "backtrace"))]
pub fn details(&self) -> Option<&(dyn core::error::Error + Send + Sync)> {
self.inner.as_ref().map(|err| err.as_ref())
}
}
#[cfg(all(feature = "std", feature = "backtrace"))]
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Self::new_with_details(ErrorCode::StdIoError, Box::new(e))
}
}
#[cfg(all(feature = "std", not(feature = "backtrace")))]
impl From<std::io::Error> for Error {
fn from(_e: std::io::Error) -> Self {
Self::new(ErrorCode::StdIoError)
}
}
#[cfg(feature = "std")]
impl<T> From<std::sync::PoisonError<T>> for Error {
fn from(_e: std::sync::PoisonError<T>) -> Self {
Self::new(ErrorCode::RwLock)
}
}
#[cfg(all(
feature = "os",
target_os = "linux",
feature = "bluer",
not(feature = "backtrace")
))]
impl From<bluer::Error> for Error {
fn from(e: bluer::Error) -> Self {
error!("Error in BTP: {}", display2format!(e));
Self::new(ErrorCode::BtpError)
}
}
#[cfg(all(
feature = "os",
target_os = "linux",
feature = "bluer",
feature = "backtrace"
))]
impl From<bluer::Error> for Error {
fn from(e: bluer::Error) -> Self {
Self::new_with_details(ErrorCode::BtpError, Box::new(e))
}
}
#[cfg(feature = "std")]
impl From<std::time::SystemTimeError> for Error {
fn from(_e: std::time::SystemTimeError) -> Self {
Error::new(ErrorCode::SysTimeFail)
}
}
impl From<TryFromSliceError> for Error {
fn from(_e: TryFromSliceError) -> Self {
Self::new(ErrorCode::Invalid)
}
}
impl From<Utf8Error> for Error {
fn from(_e: Utf8Error) -> Self {
Self::new(ErrorCode::Utf8Fail)
}
}
impl<T: num_enum::TryFromPrimitive> From<num_enum::TryFromPrimitiveError<T>> for Error {
fn from(_e: num_enum::TryFromPrimitiveError<T>) -> Self {
Self::new(ErrorCode::Invalid)
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(not(all(feature = "std", feature = "backtrace")))]
{
write!(f, "Error::{}", self)?;
}
#[cfg(all(feature = "std", feature = "backtrace"))]
{
writeln!(f, "Error::{} {{", self)?;
write!(f, "{}", self.backtrace())?;
writeln!(f, "}}")?;
}
Ok(())
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(all(feature = "alloc", feature = "backtrace"))]
{
let err_msg = self
.inner
.as_ref()
.map_or(Default::default(), |err| err.to_string());
if err_msg.is_empty() {
write!(f, "{:?}", self.code())
} else {
write!(f, "{:?}: {}", self.code(), err_msg)
}
}
#[cfg(not(all(feature = "alloc", feature = "backtrace")))]
{
write!(f, "{:?}", self.code())
}
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for Error {
fn format(&self, f: defmt::Formatter<'_>) {
defmt::write!(f, "{:?}", self.code())
}
}
impl core::error::Error for Error {
#[cfg(all(feature = "alloc", feature = "backtrace"))]
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
self.inner
.as_ref()
.map(|e| e.as_ref() as &(dyn core::error::Error + 'static))
}
}
impl embedded_io_async::Error for Error {
fn kind(&self) -> embedded_io_async::ErrorKind {
embedded_io_async::ErrorKind::Other
}
}