mqtt_proto/common/
error.rs

1use alloc::string::String;
2
3use embedded_io::ReadExactError;
4use thiserror::Error;
5
6use crate::Protocol;
7
8/// Errors returned by encoding and decoding process.
9#[derive(Error, Debug, Clone, PartialEq, Eq)]
10pub enum Error {
11    /// Invalid remaining length.
12    #[error("invalid remaining length")]
13    InvalidRemainingLength,
14
15    /// No subscription in subscribe packet.
16    #[error("empty subscription")]
17    EmptySubscription,
18
19    /// Packet identifier is 0.
20    #[error("packet identifier is 0")]
21    ZeroPid,
22
23    /// Invalid QoS value.
24    #[error("invalid qos: `{0}`")]
25    InvalidQos(u8),
26
27    /// Invalid connect flags.
28    #[error("invalid connect flags: `{0}`")]
29    InvalidConnectFlags(u8),
30
31    /// Invalid connack flags (not 0 or 1).
32    #[error("invalid connack flags: `{0}`")]
33    InvalidConnackFlags(u8),
34
35    /// Invalid connect return code (value > 5).
36    #[error("invalid connect return code: `{0}`")]
37    InvalidConnectReturnCode(u8),
38
39    /// Invalid protocol.
40    #[error("invalid protocol: {0}, {1}")]
41    InvalidProtocol(String, u8),
42
43    /// Unexpected protocol
44    #[error("unexpected protocol version: `{0}`")]
45    UnexpectedProtocol(Protocol),
46
47    /// Invalid fixed header (packet type, flags, or remaining_length).
48    #[error("invalid header")]
49    InvalidHeader,
50
51    /// Invalid variable byte integer, the value MUST smaller than `268,435,456`.
52    #[error("invalid variable byte integer")]
53    InvalidVarByteInt,
54
55    /// Invalid Topic Name
56    #[error("invalid topic name: {0}")]
57    InvalidTopicName(String),
58
59    /// Invalid topic filter
60    #[error("invalid topic filter: {0}")]
61    InvalidTopicFilter(String),
62
63    /// Trying to decode a non-utf8 string.
64    #[error("invalid string")]
65    InvalidString,
66
67    /// Catch-all error when converting from `io::Error`.
68    #[error("io error: {0:?}")]
69    IoError(IoErrorKind),
70}
71
72/// IoErrorKind for both std and no-std environments
73#[derive(Debug, Clone, PartialEq, Eq)]
74pub enum IoErrorKind {
75    UnexpectedEof,
76    InvalidData,
77    WriteZero,
78    Other,
79}
80
81impl Error {
82    pub fn is_eof(&self) -> bool {
83        matches!(self, Error::IoError(IoErrorKind::UnexpectedEof))
84    }
85}
86
87impl<E: embedded_io::Error> From<E> for Error {
88    fn from(err: E) -> Error {
89        let kind = match err.kind() {
90            embedded_io::ErrorKind::InvalidData => IoErrorKind::InvalidData,
91            embedded_io::ErrorKind::WriteZero => IoErrorKind::WriteZero,
92            _ => IoErrorKind::Other,
93        };
94        Error::IoError(kind)
95    }
96}
97
98pub fn from_read_exact_error<E: Into<Error>>(e: ReadExactError<E>) -> Error {
99    match e {
100        ReadExactError::UnexpectedEof => Error::IoError(IoErrorKind::UnexpectedEof),
101        ReadExactError::Other(e) => e.into(),
102    }
103}
104
105#[cfg(feature = "std")]
106impl From<Error> for std::io::Error {
107    fn from(err: Error) -> std::io::Error {
108        match err {
109            Error::IoError(IoErrorKind::UnexpectedEof) => {
110                std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "unexpected eof")
111            }
112            Error::IoError(IoErrorKind::InvalidData) => {
113                std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid data")
114            }
115            Error::IoError(IoErrorKind::WriteZero) => {
116                std::io::Error::new(std::io::ErrorKind::WriteZero, "write zero")
117            }
118            Error::IoError(IoErrorKind::Other) => std::io::Error::other("other error"),
119            _ => std::io::Error::new(std::io::ErrorKind::InvalidData, "mqtt protocol error"),
120        }
121    }
122}