Skip to main content

mountain_mqtt/data/
reason_code.rs

1use core::fmt::{Display, Formatter};
2
3use crate::codec::{read::Read, write::Write};
4use crate::error::PacketReadError;
5
6#[macro_export]
7macro_rules! packet_reason_codes {
8    ( $n:ident, [ $( $c:ident ),+ ] ) => {
9        #[derive(Debug, PartialEq, Clone, Copy)]
10        #[repr(u8)]
11        pub enum $n{
12            $(
13                $c = ReasonCode::$c as u8,
14            )*
15        }
16
17        impl From<$n> for ReasonCode {
18            fn from(value: $n) -> Self {
19                match value {
20                    $(
21                        $n::$c => ReasonCode::$c,
22                    )*
23                }
24            }
25        }
26
27        impl TryFrom<u8> for $n {
28            type Error = PacketReadError;
29
30            fn try_from(value: u8) -> Result<Self, Self::Error> {
31                $(
32                    if value == ReasonCode::$c as u8 {
33                        Ok(Self::$c)
34                    } else
35                )*
36                {
37                    Err(PacketReadError::UnknownReasonCode)
38                }
39            }
40        }
41
42        impl TryFrom<ReasonCode> for $n {
43            type Error = PacketReadError;
44
45            fn try_from(value: ReasonCode) -> Result<Self, Self::Error> {
46                match value {
47                    $(
48                        ReasonCode::$c => Ok(Self::$c),
49                    )*
50
51                    _ => Err(PacketReadError::UnknownReasonCode),
52                }
53            }
54        }
55
56        impl $n {
57            pub fn is_error(&self) -> bool {
58                (*self as u8) > 128
59            }
60        }
61
62        impl<'a> Read<'a> for $n {
63            fn read<R: $crate::codec::mqtt_reader::MqttReader<'a>>(
64                reader: &mut R,
65            ) -> $crate::codec::mqtt_reader::Result<Self>
66            where
67                Self: Sized,
68            {
69                let encoded = reader.get_u8()?;
70                encoded.try_into()
71            }
72        }
73
74        impl Write for $n {
75            fn write<'a, W: $crate::codec::mqtt_writer::MqttWriter<'a>>(
76                &self,
77                writer: &mut W,
78            ) -> $crate::codec::mqtt_writer::Result<()> {
79                writer.put_u8(*self as u8)
80            }
81        }
82
83        impl Display for $n {
84            fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
85                match self {
86                    $(
87                        Self::$c => write!(f, "$n($c)"),
88                    )*
89                }
90            }
91        }
92
93        #[cfg(feature = "defmt")]
94        impl defmt::Format for $n {
95            fn format(&self, f: defmt::Formatter) {
96                match self {
97                    $(
98                        Self::$c => defmt::write!(f, "$n($c)"),
99                    )*
100                }
101            }
102        }
103
104    };
105}
106
107/// A Reason Code is a one byte unsigned value that indicates the result of an operation. Reason Codes less
108/// than 128 indicate successful completion of an operation. The normal Reason Code for success is 0.
109/// Reason Code values of 128 or greater indicate failure.
110#[repr(u8)]
111#[derive(Clone, Copy, PartialEq, Debug)]
112pub enum ReasonCode {
113    Success = 0,
114    GrantedQos1 = 1,
115    GrantedQos2 = 2,
116    DisconnectWithWillMessage = 4,
117    NoMatchingSubscribers = 16,
118    NoSubscriptionExisted = 17,
119    ContinueAuthentication = 24,
120    ReAuthenticate = 25,
121    UnspecifiedError = 128,
122    MalformedPacket = 129,
123    ProtocolError = 130,
124    ImplementationSpecificError = 131,
125    UnsupportedProtocolVersion = 132,
126    ClientIdentifierNotValid = 133,
127    BadUserNameOrPassword = 134,
128    NotAuthorized = 135,
129    ServerUnavailable = 136,
130    ServerBusy = 137,
131    Banned = 138,
132    ServerShuttingDown = 139,
133    BadAuthenticationMethod = 140,
134    KeepAliveTimeout = 141,
135    SessionTakenOver = 142,
136    TopicFilterInvalid = 143,
137    TopicNameInvalid = 144,
138    PacketIdentifierInUse = 145,
139    PacketIdentifierNotFound = 146,
140    ReceiveMaximumExceeded = 147,
141    TopicAliasInvalid = 148,
142    PacketTooLarge = 149,
143    MessageRateTooHigh = 150,
144    QuotaExceeded = 151,
145    AdministrativeAction = 152,
146    PayloadFormatInvalid = 153,
147    RetainNotSupported = 154,
148    QosNotSupported = 155,
149    UseAnotherServer = 156,
150    ServerMoved = 157,
151    SharedSubscriptionsNotSupported = 158,
152    ConnectionRateExceeded = 159,
153    MaximumConnectTime = 160,
154    SubscriptionIdentifiersNotSupported = 161,
155    WildcardSubscriptionsNotSupported = 162,
156}
157
158impl ReasonCode {
159    pub fn is_error(&self) -> bool {
160        (*self as u8) > 128
161    }
162}
163
164packet_reason_codes!(
165    ConnectReasonCode,
166    [
167        Success,
168        UnspecifiedError,
169        MalformedPacket,
170        ProtocolError,
171        ImplementationSpecificError,
172        UnsupportedProtocolVersion,
173        ClientIdentifierNotValid,
174        BadUserNameOrPassword,
175        NotAuthorized,
176        ServerUnavailable,
177        ServerBusy,
178        Banned,
179        BadAuthenticationMethod,
180        TopicNameInvalid,
181        PacketTooLarge,
182        QuotaExceeded,
183        PayloadFormatInvalid,
184        RetainNotSupported,
185        QosNotSupported,
186        UseAnotherServer,
187        ServerMoved,
188        ConnectionRateExceeded
189    ]
190);
191
192packet_reason_codes!(
193    DisconnectReasonCode,
194    [
195        Success, //Normal disconnection in spec
196        DisconnectWithWillMessage,
197        UnspecifiedError,
198        MalformedPacket,
199        ProtocolError,
200        ImplementationSpecificError,
201        NotAuthorized,
202        ServerBusy,
203        ServerShuttingDown,
204        KeepAliveTimeout,
205        SessionTakenOver,
206        TopicFilterInvalid,
207        TopicNameInvalid,
208        ReceiveMaximumExceeded,
209        TopicAliasInvalid,
210        PacketTooLarge,
211        MessageRateTooHigh,
212        QuotaExceeded,
213        AdministrativeAction,
214        PayloadFormatInvalid,
215        RetainNotSupported,
216        QosNotSupported,
217        UseAnotherServer,
218        ServerMoved,
219        SharedSubscriptionsNotSupported,
220        ConnectionRateExceeded,
221        MaximumConnectTime,
222        SubscriptionIdentifiersNotSupported,
223        WildcardSubscriptionsNotSupported
224    ]
225);
226
227packet_reason_codes!(
228    PublishReasonCode,
229    [
230        Success,
231        NoMatchingSubscribers,
232        UnspecifiedError,
233        ImplementationSpecificError,
234        NotAuthorized,
235        TopicNameInvalid,
236        PacketIdentifierInUse,
237        QuotaExceeded,
238        PayloadFormatInvalid
239    ]
240);
241
242packet_reason_codes!(PubrelReasonCode, [Success, PacketIdentifierNotFound]);
243
244packet_reason_codes!(
245    SubscribeReasonCode,
246    [
247        Success,
248        GrantedQos1,
249        GrantedQos2,
250        UnspecifiedError,
251        ImplementationSpecificError,
252        NotAuthorized,
253        TopicFilterInvalid,
254        PacketIdentifierInUse,
255        QuotaExceeded,
256        SharedSubscriptionsNotSupported,
257        SubscriptionIdentifiersNotSupported,
258        WildcardSubscriptionsNotSupported
259    ]
260);
261
262packet_reason_codes!(
263    UnsubscribeReasonCode,
264    [
265        Success,
266        NoSubscriptionExisted,
267        UnspecifiedError,
268        ImplementationSpecificError,
269        NotAuthorized,
270        TopicFilterInvalid,
271        PacketIdentifierInUse
272    ]
273);
274
275packet_reason_codes!(
276    AuthReasonCode,
277    [Success, ContinueAuthentication, ReAuthenticate]
278);
279
280#[cfg(test)]
281mod tests {
282    use crate::codec::{
283        mqtt_reader::{MqttBufReader, MqttReader},
284        mqtt_writer::{MqttBufWriter, MqttWriter},
285    };
286
287    use super::*;
288
289    packet_reason_codes!(ExampleReasonCode, [UnspecifiedError, MalformedPacket]);
290
291    const DATA: [(ExampleReasonCode, u8); 2] = [
292        (
293            ExampleReasonCode::UnspecifiedError,
294            ReasonCode::UnspecifiedError as u8,
295        ),
296        (
297            ExampleReasonCode::MalformedPacket,
298            ReasonCode::MalformedPacket as u8,
299        ),
300    ];
301
302    #[test]
303    fn all_expected_codes_exist_with_expected_u8_value() {
304        for (code, value) in DATA.iter() {
305            assert_eq!(*code as u8, *value);
306        }
307    }
308
309    #[test]
310    fn can_write_codes() {
311        for (code, value) in DATA.iter() {
312            let mut buf = [0u8];
313            {
314                let mut r = MqttBufWriter::new(&mut buf);
315                r.put(code).unwrap();
316                assert_eq!(r.position(), 1);
317                assert_eq!(r.remaining(), 0);
318            }
319            assert_eq!(buf, [*value]);
320        }
321    }
322
323    #[test]
324    fn can_read_codes() {
325        for (code, value) in DATA.iter() {
326            let buf = [*value];
327            let mut r = MqttBufReader::new(&buf);
328            let read_code: ExampleReasonCode = r.get().unwrap();
329            assert_eq!(r.position(), 1);
330            assert_eq!(r.remaining(), 0);
331            assert_eq!(&read_code, code);
332        }
333    }
334
335    #[test]
336    fn can_try_to_get_packet_reason_code_from_reason_code() {
337        assert_eq!(
338            ExampleReasonCode::try_from(ReasonCode::UnspecifiedError),
339            Ok(ExampleReasonCode::UnspecifiedError)
340        );
341        assert_eq!(
342            ExampleReasonCode::try_from(ReasonCode::MalformedPacket),
343            Ok(ExampleReasonCode::MalformedPacket)
344        );
345        assert_eq!(
346            ExampleReasonCode::try_from(ReasonCode::AdministrativeAction),
347            Err(PacketReadError::UnknownReasonCode)
348        );
349    }
350
351    #[test]
352    fn can_try_to_get_packet_reason_code_from_u8() {
353        assert_eq!(
354            ExampleReasonCode::try_from(ReasonCode::UnspecifiedError as u8),
355            Ok(ExampleReasonCode::UnspecifiedError)
356        );
357        assert_eq!(
358            ExampleReasonCode::try_from(ReasonCode::MalformedPacket as u8),
359            Ok(ExampleReasonCode::MalformedPacket)
360        );
361        assert_eq!(
362            ExampleReasonCode::try_from(ReasonCode::AdministrativeAction as u8),
363            Err(PacketReadError::UnknownReasonCode)
364        );
365    }
366
367    #[test]
368    fn can_get_reason_code_from_packet_reason_code() {
369        assert_eq!(
370            ReasonCode::from(ExampleReasonCode::UnspecifiedError),
371            ReasonCode::UnspecifiedError
372        );
373        assert_eq!(
374            ReasonCode::from(ExampleReasonCode::MalformedPacket),
375            ReasonCode::MalformedPacket
376        );
377    }
378}