rust_mqtt/client/err.rs
1use crate::{
2 client::raw::RawError,
3 eio::ErrorKind,
4 header::Reserved,
5 types::{MqttString, ReasonCode, TooLargeToEncode},
6};
7
8/// The main error returned by [`crate::client::Client`].
9///
10/// Distincts between unrecoverable and recoverable errors.
11/// Recoverability in this context refers to whether the current network connection can
12/// be used for further communication after the error has occured.
13///
14/// # Recovery
15/// - For unrecoverable errors, [`crate::client::Client::abort`] can be called to send an optional DISCONNECT
16/// packet if allowed by specification. You can then try to recover the session by calling
17/// [`crate::client::Client::connect`] again without clean start.
18/// - For recoverable errors, follow the error-specific behaviour.
19#[derive(Debug, Clone, PartialEq, Eq)]
20#[cfg_attr(feature = "defmt", derive(defmt::Format))]
21pub enum Error<'e> {
22 /// An underlying Read/Write method returned an error.
23 ///
24 /// Unrecoverable error. [`crate::client::Client::abort`] should be called.
25 Network(ErrorKind),
26
27 /// The remote server did something the client does not understand / does not match the specification.
28 ///
29 /// Unrecoverable error. [`crate::client::Client::abort`] should be called.
30 Server,
31
32 /// A buffer provision by the [`crate::buffer::BufferProvider`] failed. Therefore a packet
33 /// could not be received correctly.
34 ///
35 /// Unrecoverable error. [`crate::client::Client::abort`] should be called.
36 Alloc,
37
38 /// An AUTH packet header has been received by the client. AUTH packets are not supported by the client.
39 /// The client has scheduled a DISCONNECT packet with [`ReasonCode::ImplementationSpecificError`].
40 /// The packet body has not been decoded.
41 ///
42 /// Unrecoverable error. [`crate::client::Client::abort`] should be called.
43 AuthPacketReceived,
44
45 /// The client could not connect to the broker or the broker has sent a DISCONNECT packet.
46 ///
47 /// Unrecoverable error. [`crate::client::Client::abort`] should be called.
48 Disconnect {
49 /// The [`ReasonCode`] of the causing CONNACK or DISCONNECT packet. If the disconnection is caused
50 /// by a CONNACK packet, the reason code ss always erroneous.
51 reason: ReasonCode,
52
53 /// The reason string property of the causing CONNACK or DISCONNECT packet if the server included
54 /// a reason string.
55 reason_string: Option<MqttString<'e>>,
56
57 /// The server reference property of the causing CONNACK or DISCONNCET packet if the server included
58 /// a server reference. Identifies another server which can be used.
59 server_reference: Option<MqttString<'e>>,
60 },
61
62 /// Another unrecoverable error has been returned earlier. The underlying connection is in a state,
63 /// in which it refuses/is not able to perform regular communication.
64 ///
65 /// Unrecoverable error. [`crate::client::Client::abort`] should be called.
66 RecoveryRequired,
67
68 /// A republish of a packet without an in flight entry was attempted.
69 ///
70 /// Recoverable error. No action has been taken by the client.
71 PacketIdentifierNotInFlight,
72
73 /// A republish of a packet with a quality of service that does not match the quality of
74 /// service of the original publication was attempted.
75 ///
76 /// Recoverable error. No action has been taken by the client.
77 RepublishQoSNotMatching,
78
79 /// A republish of a packet whose corresponding PUBREL packet has already been sent was attempted.
80 /// Sending the PUBLISH packet in this case would result in a protocol violation.
81 ///
82 /// Recoverable error. No action has been taken by the client.
83 PacketIdentifierAwaitingPubcomp,
84
85 /// A packet was too long to encode its length with the variable byte integer.
86 ///
87 /// This can currently only be returned from [`crate::client::Client::publish`] or
88 /// [`crate::client::Client::republish`].
89 ///
90 /// Recoverable error. No action has been taken by the client.
91 PacketMaximumLengthExceeded,
92
93 /// A packet is too long and would exceed the servers maximum packet size.
94 ///
95 /// Recoverable error. No action has been taken by the client.
96 ServerMaximumPacketSizeExceeded,
97
98 /// The value of a topic alias in an outgoing PUBLISH packet was 0 or greater than the server's maximum allowed
99 /// value. Sending this PUBLISH packet would raise a protocol error.
100 ///
101 /// Recoverable error. No action has been taken by the client.
102 InvalidTopicAlias,
103
104 /// An action was rejected because an internal buffer used for tracking session state is full.
105 ///
106 /// Recoverable error. Try again after a [`crate::client::event::Event`] has been emitted that indicates
107 /// that buffer might be free again.
108 ///
109 /// Example:
110 /// [`crate::client::Client::subscribe`] returns this error. Wait until a
111 /// [`crate::client::event::Event::Suback`] is received.
112 /// This clears a spot in the subscribe packet identifiers.
113 SessionBuffer,
114
115 /// A publish now would exceed the server's receive maximum and ultimately cause a protocol error.
116 ///
117 /// Recoverable error. Try again after either [`crate::client::event::Event::PublishAcknowledged`] or
118 /// [`crate::client::event::Event::PublishComplete`] has been emitted that indicates that buffer might be
119 /// free again.
120 /// been emitted that indicates that buffer might be free again.
121 SendQuotaExceeded,
122
123 /// A disconnect now with the given session expiry interval would cause a protocol error.
124 ///
125 /// A disconnection was attempted with a session expiry interval change where the session expiry interval in the
126 /// CONNECT packet was zero ([`crate::config::SessionExpiryInterval::EndOnDisconnect`]) and was
127 /// greater than zero ([`crate::config::SessionExpiryInterval::NeverEnd`] or
128 /// [`crate::config::SessionExpiryInterval::Seconds`]) in the DISCONNECT packet.
129 ///
130 /// Recoverable error. Try disconnecting again without an session expiry interval or with a
131 /// session expiry interval of zero ([`crate::config::SessionExpiryInterval::EndOnDisconnect`]).
132 IllegalDisconnectSessionExpiryInterval,
133}
134
135impl Error<'_> {
136 /// Returns whether the client can recover from this error without closing the network connection.
137 #[must_use]
138 pub fn is_recoverable(&self) -> bool {
139 matches!(
140 self,
141 Self::PacketIdentifierNotInFlight
142 | Self::RepublishQoSNotMatching
143 | Self::PacketIdentifierAwaitingPubcomp
144 | Self::PacketMaximumLengthExceeded
145 | Self::ServerMaximumPacketSizeExceeded
146 | Self::InvalidTopicAlias
147 | Self::SessionBuffer
148 | Self::SendQuotaExceeded
149 | Self::IllegalDisconnectSessionExpiryInterval
150 )
151 }
152}
153
154impl From<Reserved> for Error<'_> {
155 fn from(_: Reserved) -> Self {
156 Self::Server
157 }
158}
159
160impl<B> From<RawError<B>> for Error<'_> {
161 fn from(e: RawError<B>) -> Self {
162 match e {
163 RawError::Disconnected => Self::RecoveryRequired,
164 RawError::Network(e) => Self::Network(e),
165 RawError::Alloc(_) => Self::Alloc,
166 RawError::Server => Self::Server,
167 }
168 }
169}
170
171impl From<TooLargeToEncode> for Error<'_> {
172 fn from(_: TooLargeToEncode) -> Self {
173 Self::PacketMaximumLengthExceeded
174 }
175}