1use std::collections::{HashMap, VecDeque};
10use std::sync::{Arc, Mutex, PoisonError};
11
12use pamoja_core::{Error, Result};
13use rumqttc::QoS;
14use tokio::sync::oneshot;
15
16#[derive(Debug)]
23#[must_use = "a delivery says nothing until it is awaited"]
24pub struct Delivery {
25 settled: oneshot::Receiver<Result<()>>,
26}
27
28impl Delivery {
29 pub async fn confirmed(self) -> Result<()> {
41 self.settled.await.unwrap_or(Err(Error::Closed))
42 }
43}
44
45struct Waiting {
46 qos: QoS,
47 settle: oneshot::Sender<Result<()>>,
48}
49
50#[derive(Clone, Default)]
53pub(crate) struct Requested(Arc<Mutex<VecDeque<Waiting>>>);
54
55impl Requested {
56 pub(crate) fn push(&self, qos: QoS) -> Delivery {
58 let (settle, settled) = oneshot::channel();
59 self.lock().push_back(Waiting { qos, settle });
60 Delivery { settled }
61 }
62
63 pub(crate) fn withdraw(&self) {
65 self.lock().pop_back();
66 }
67
68 fn lock(&self) -> std::sync::MutexGuard<'_, VecDeque<Waiting>> {
69 self.0.lock().unwrap_or_else(PoisonError::into_inner)
70 }
71}
72
73#[derive(Default)]
76pub(crate) struct InFlight {
77 by_id: HashMap<u16, oneshot::Sender<Result<()>>>,
78}
79
80impl InFlight {
81 pub(crate) fn sent(&mut self, requested: &Requested, pkid: u16) {
83 let Some(waiting) = requested.lock().pop_front() else {
84 return;
85 };
86 if waiting.qos == QoS::AtMostOnce {
87 let _ = waiting.settle.send(Ok(()));
88 } else {
89 self.by_id.insert(pkid, waiting.settle);
90 }
91 }
92
93 pub(crate) fn acknowledged(&mut self, pkid: u16) {
95 if let Some(settle) = self.by_id.remove(&pkid) {
96 let _ = settle.send(Ok(()));
97 }
98 }
99
100 pub(crate) fn fail(&mut self, requested: &Requested, reason: &str) {
102 let waiting = requested
103 .lock()
104 .drain(..)
105 .map(|waiting| waiting.settle)
106 .collect::<Vec<_>>();
107 for settle in self.by_id.drain().map(|(_, settle)| settle).chain(waiting) {
108 let _ = settle.send(Err(Error::Transport(format!(
109 "the connection ended before the broker acknowledged the message: {reason}"
110 ))));
111 }
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[tokio::test]
120 async fn each_report_settles_the_request_it_belongs_to() {
121 let requested = Requested::default();
122 let mut in_flight = InFlight::default();
123 let quick = requested.push(QoS::AtMostOnce);
124 let once = requested.push(QoS::AtLeastOnce);
125 let exact = requested.push(QoS::ExactlyOnce);
126
127 in_flight.sent(&requested, 0);
128 in_flight.sent(&requested, 7);
129 in_flight.sent(&requested, 8);
130 quick
131 .confirmed()
132 .await
133 .expect("QoS 0 settles once it goes out");
134
135 in_flight.acknowledged(8);
136 in_flight.acknowledged(7);
137 exact
138 .confirmed()
139 .await
140 .expect("settled by its own identifier");
141 once.confirmed()
142 .await
143 .expect("whatever order the answers come in");
144 }
145
146 #[tokio::test]
147 async fn an_ended_connection_fails_what_is_still_waiting() {
148 let requested = Requested::default();
149 let mut in_flight = InFlight::default();
150 let out = requested.push(QoS::AtLeastOnce);
151 let queued = requested.push(QoS::AtLeastOnce);
152 in_flight.sent(&requested, 1);
153 in_flight.fail(&requested, "the broker went away");
154 for delivery in [out, queued] {
155 match delivery.confirmed().await {
156 Err(Error::Transport(reason)) => assert!(reason.contains("went away"), "{reason}"),
157 other => panic!("a delivery cut off is refused, got {other:?}"),
158 }
159 }
160 }
161
162 #[tokio::test]
163 async fn a_withdrawn_request_is_closed() {
164 let requested = Requested::default();
165 let delivery = requested.push(QoS::AtLeastOnce);
166 requested.withdraw();
167 assert!(matches!(delivery.confirmed().await, Err(Error::Closed)));
168 }
169}