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}