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}