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