use core::fmt;
use smol_str::SmolStr;
use crate::path::PropertyPath;
pub type IotResult<T> = core::result::Result<T, IotError>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TransportError {
NotConnected,
AddressInvalid(SmolStr),
ConnectFailed(SmolStr),
Io(SmolStr),
Timeout,
Tls(SmolStr),
SerialFraming(SmolStr),
Closed,
}
impl fmt::Display for TransportError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use TransportError::*;
match self {
NotConnected => write!(f, "transport not connected"),
AddressInvalid(s) => write!(f, "invalid address: {s}"),
ConnectFailed(s) => write!(f, "connect failed: {s}"),
Io(s) => write!(f, "io error: {s}"),
Timeout => write!(f, "operation timed out"),
Tls(s) => write!(f, "tls error: {s}"),
SerialFraming(s) => write!(f, "serial framing error: {s}"),
Closed => write!(f, "connection closed"),
}
}
}
impl core::error::Error for TransportError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CodecError {
UnexpectedEof,
FrameTooLarge {
max: u32,
actual: u32,
},
ProtocolViolation(SmolStr),
ChecksumMismatch,
InvalidString(SmolStr),
ValueOutOfRange(SmolStr),
}
impl fmt::Display for CodecError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use CodecError::*;
match self {
UnexpectedEof => write!(f, "unexpected end of buffer while decoding"),
FrameTooLarge { max, actual } => {
write!(f, "frame too large: {actual} bytes, max {max}")
}
ProtocolViolation(s) => write!(f, "protocol violation: {s}"),
ChecksumMismatch => write!(f, "checksum mismatch"),
InvalidString(s) => write!(f, "invalid string: {s}"),
ValueOutOfRange(s) => write!(f, "value out of range: {s}"),
}
}
}
impl core::error::Error for CodecError {}
#[derive(Debug)]
#[non_exhaustive]
pub enum IotError {
NotConnected,
ConnectionFailed(SmolStr),
AuthFailed(SmolStr),
Timeout,
PathNotBound(PropertyPath),
PathKindMismatch {
path: PropertyPath,
expected: &'static str,
},
Codec(CodecError),
Transport(TransportError),
Protocol(SmolStr),
}
impl fmt::Display for IotError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use IotError::*;
match self {
NotConnected => write!(f, "client not connected"),
ConnectionFailed(s) => write!(f, "connection failed: {s}"),
AuthFailed(s) => write!(f, "authentication failed: {s}"),
Timeout => write!(f, "operation timed out"),
PathNotBound(p) => write!(f, "path not bound: {p}"),
PathKindMismatch { path, expected } => {
write!(f, "path {path} is not a {expected} binding")
}
Codec(e) => write!(f, "codec error: {e}"),
Transport(e) => write!(f, "transport error: {e}"),
Protocol(s) => write!(f, "protocol error: {s}"),
}
}
}
impl core::error::Error for IotError {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
IotError::Codec(e) => Some(e),
IotError::Transport(e) => Some(e),
_ => None,
}
}
}
impl From<CodecError> for IotError {
fn from(e: CodecError) -> Self {
Self::Codec(e)
}
}
impl From<TransportError> for IotError {
fn from(e: TransportError) -> Self {
Self::Transport(e)
}
}
impl embedded_io_async::Error for TransportError {
fn kind(&self) -> embedded_io_async::ErrorKind {
use embedded_io_async::ErrorKind as K;
match self {
TransportError::NotConnected => K::NotConnected,
TransportError::AddressInvalid(_) => K::AddrNotAvailable,
TransportError::ConnectFailed(_) => K::ConnectionRefused,
TransportError::Io(_) => K::Other,
TransportError::Timeout => K::TimedOut,
TransportError::Tls(_) => K::PermissionDenied,
TransportError::SerialFraming(_) => K::InvalidData,
TransportError::Closed => K::ConnectionAborted,
}
}
}