mqtt5_protocol/
qos2.rs

1use crate::packet::pubcomp::PubCompPacket;
2use crate::packet::pubrec::PubRecPacket;
3use crate::packet::pubrel::PubRelPacket;
4use crate::protocol::v5::reason_codes::ReasonCode;
5
6#[derive(Debug, Clone, PartialEq)]
7pub enum QoS2Action {
8    SendPubRec {
9        packet_id: u16,
10        reason_code: ReasonCode,
11    },
12    SendPubRel {
13        packet_id: u16,
14    },
15    SendPubComp {
16        packet_id: u16,
17        reason_code: ReasonCode,
18    },
19    TrackOutgoingPubRec {
20        packet_id: u16,
21    },
22    TrackOutgoingPubRel {
23        packet_id: u16,
24    },
25    RemoveOutgoingPubRel {
26        packet_id: u16,
27    },
28    TrackIncomingPubRec {
29        packet_id: u16,
30    },
31    RemoveIncomingPubRec {
32        packet_id: u16,
33    },
34    DeliverMessage {
35        packet_id: u16,
36    },
37    CompleteFlow {
38        packet_id: u16,
39    },
40    ErrorFlow {
41        packet_id: u16,
42        reason_code: ReasonCode,
43    },
44}
45
46impl QoS2Action {
47    #[must_use]
48    pub fn to_pubrec_packet(&self) -> Option<PubRecPacket> {
49        match self {
50            QoS2Action::SendPubRec {
51                packet_id,
52                reason_code,
53            } => Some(PubRecPacket::new_with_reason(*packet_id, *reason_code)),
54            _ => None,
55        }
56    }
57
58    #[must_use]
59    pub fn to_pubrel_packet(&self) -> Option<PubRelPacket> {
60        match self {
61            QoS2Action::SendPubRel { packet_id } => Some(PubRelPacket::new(*packet_id)),
62            _ => None,
63        }
64    }
65
66    #[must_use]
67    pub fn to_pubcomp_packet(&self) -> Option<PubCompPacket> {
68        match self {
69            QoS2Action::SendPubComp {
70                packet_id,
71                reason_code,
72            } => Some(PubCompPacket::new_with_reason(*packet_id, *reason_code)),
73            _ => None,
74        }
75    }
76}
77
78#[must_use]
79pub fn handle_outgoing_publish_qos2(packet_id: u16) -> Vec<QoS2Action> {
80    vec![QoS2Action::TrackOutgoingPubRel { packet_id }]
81}
82
83#[must_use]
84pub fn handle_incoming_pubrec(
85    packet_id: u16,
86    reason_code: ReasonCode,
87    has_pending_publish: bool,
88) -> Vec<QoS2Action> {
89    if !has_pending_publish {
90        return vec![QoS2Action::ErrorFlow {
91            packet_id,
92            reason_code: ReasonCode::PacketIdentifierNotFound,
93        }];
94    }
95
96    if reason_code != ReasonCode::Success {
97        return vec![QoS2Action::ErrorFlow {
98            packet_id,
99            reason_code,
100        }];
101    }
102
103    vec![
104        QoS2Action::SendPubRel { packet_id },
105        QoS2Action::TrackOutgoingPubRel { packet_id },
106    ]
107}
108
109#[must_use]
110pub fn handle_incoming_pubcomp(
111    packet_id: u16,
112    reason_code: ReasonCode,
113    has_pending_pubrel: bool,
114) -> Vec<QoS2Action> {
115    if !has_pending_pubrel {
116        return vec![];
117    }
118
119    vec![
120        QoS2Action::RemoveOutgoingPubRel { packet_id },
121        if reason_code == ReasonCode::Success {
122            QoS2Action::CompleteFlow { packet_id }
123        } else {
124            QoS2Action::ErrorFlow {
125                packet_id,
126                reason_code,
127            }
128        },
129    ]
130}
131
132#[must_use]
133pub fn handle_incoming_publish_qos2(packet_id: u16, is_duplicate: bool) -> Vec<QoS2Action> {
134    if is_duplicate {
135        vec![QoS2Action::SendPubRec {
136            packet_id,
137            reason_code: ReasonCode::Success,
138        }]
139    } else {
140        vec![
141            QoS2Action::DeliverMessage { packet_id },
142            QoS2Action::SendPubRec {
143                packet_id,
144                reason_code: ReasonCode::Success,
145            },
146            QoS2Action::TrackIncomingPubRec { packet_id },
147        ]
148    }
149}
150
151#[must_use]
152pub fn handle_incoming_pubrel(packet_id: u16, has_pending_pubrec: bool) -> Vec<QoS2Action> {
153    if has_pending_pubrec {
154        vec![
155            QoS2Action::RemoveIncomingPubRec { packet_id },
156            QoS2Action::SendPubComp {
157                packet_id,
158                reason_code: ReasonCode::Success,
159            },
160        ]
161    } else {
162        vec![QoS2Action::SendPubComp {
163            packet_id,
164            reason_code: ReasonCode::PacketIdentifierNotFound,
165        }]
166    }
167}
168
169#[cfg(test)]
170mod tests {
171    use super::*;
172
173    #[test]
174    fn test_outgoing_publish_qos2() {
175        let actions = handle_outgoing_publish_qos2(123);
176        assert_eq!(actions.len(), 1);
177        assert_eq!(
178            actions[0],
179            QoS2Action::TrackOutgoingPubRel { packet_id: 123 }
180        );
181    }
182
183    #[test]
184    fn test_incoming_pubrec_success() {
185        let actions = handle_incoming_pubrec(123, ReasonCode::Success, true);
186        assert_eq!(actions.len(), 2);
187        assert_eq!(actions[0], QoS2Action::SendPubRel { packet_id: 123 });
188        assert_eq!(
189            actions[1],
190            QoS2Action::TrackOutgoingPubRel { packet_id: 123 }
191        );
192    }
193
194    #[test]
195    fn test_incoming_pubrec_error() {
196        let actions = handle_incoming_pubrec(123, ReasonCode::UnspecifiedError, true);
197        assert_eq!(actions.len(), 1);
198        match &actions[0] {
199            QoS2Action::ErrorFlow {
200                packet_id,
201                reason_code,
202            } => {
203                assert_eq!(*packet_id, 123);
204                assert_eq!(*reason_code, ReasonCode::UnspecifiedError);
205            }
206            _ => panic!("Expected ErrorFlow"),
207        }
208    }
209
210    #[test]
211    fn test_incoming_pubrec_no_pending() {
212        let actions = handle_incoming_pubrec(123, ReasonCode::Success, false);
213        assert_eq!(actions.len(), 1);
214        match &actions[0] {
215            QoS2Action::ErrorFlow {
216                packet_id,
217                reason_code,
218            } => {
219                assert_eq!(*packet_id, 123);
220                assert_eq!(*reason_code, ReasonCode::PacketIdentifierNotFound);
221            }
222            _ => panic!("Expected ErrorFlow"),
223        }
224    }
225
226    #[test]
227    fn test_incoming_pubcomp_success() {
228        let actions = handle_incoming_pubcomp(123, ReasonCode::Success, true);
229        assert_eq!(actions.len(), 2);
230        assert_eq!(
231            actions[0],
232            QoS2Action::RemoveOutgoingPubRel { packet_id: 123 }
233        );
234        assert_eq!(actions[1], QoS2Action::CompleteFlow { packet_id: 123 });
235    }
236
237    #[test]
238    fn test_incoming_pubcomp_no_pending() {
239        let actions = handle_incoming_pubcomp(123, ReasonCode::Success, false);
240        assert!(actions.is_empty());
241    }
242
243    #[test]
244    fn test_incoming_publish_qos2_new_message() {
245        let actions = handle_incoming_publish_qos2(123, false);
246        assert_eq!(actions.len(), 3);
247        assert_eq!(actions[0], QoS2Action::DeliverMessage { packet_id: 123 });
248        assert_eq!(
249            actions[1],
250            QoS2Action::SendPubRec {
251                packet_id: 123,
252                reason_code: ReasonCode::Success
253            }
254        );
255        assert_eq!(
256            actions[2],
257            QoS2Action::TrackIncomingPubRec { packet_id: 123 }
258        );
259    }
260
261    #[test]
262    fn test_incoming_publish_qos2_duplicate() {
263        let actions = handle_incoming_publish_qos2(123, true);
264        assert_eq!(actions.len(), 1);
265        assert_eq!(
266            actions[0],
267            QoS2Action::SendPubRec {
268                packet_id: 123,
269                reason_code: ReasonCode::Success
270            }
271        );
272    }
273
274    #[test]
275    fn test_incoming_pubrel_with_pubrec() {
276        let actions = handle_incoming_pubrel(123, true);
277        assert_eq!(actions.len(), 2);
278        assert_eq!(
279            actions[0],
280            QoS2Action::RemoveIncomingPubRec { packet_id: 123 }
281        );
282        assert_eq!(
283            actions[1],
284            QoS2Action::SendPubComp {
285                packet_id: 123,
286                reason_code: ReasonCode::Success
287            }
288        );
289    }
290
291    #[test]
292    fn test_incoming_pubrel_without_pubrec() {
293        let actions = handle_incoming_pubrel(123, false);
294        assert_eq!(actions.len(), 1);
295        assert_eq!(
296            actions[0],
297            QoS2Action::SendPubComp {
298                packet_id: 123,
299                reason_code: ReasonCode::PacketIdentifierNotFound
300            }
301        );
302    }
303
304    #[test]
305    fn test_action_to_packet_conversions() {
306        let action = QoS2Action::SendPubRec {
307            packet_id: 123,
308            reason_code: ReasonCode::Success,
309        };
310        assert!(action.to_pubrec_packet().is_some());
311        assert!(action.to_pubrel_packet().is_none());
312        assert!(action.to_pubcomp_packet().is_none());
313
314        let action = QoS2Action::SendPubRel { packet_id: 123 };
315        assert!(action.to_pubrec_packet().is_none());
316        assert!(action.to_pubrel_packet().is_some());
317        assert!(action.to_pubcomp_packet().is_none());
318
319        let action = QoS2Action::SendPubComp {
320            packet_id: 123,
321            reason_code: ReasonCode::Success,
322        };
323        assert!(action.to_pubrec_packet().is_none());
324        assert!(action.to_pubrel_packet().is_none());
325        assert!(action.to_pubcomp_packet().is_some());
326    }
327}