taba_client/client/handlers/
mod.rs1use std::fmt;
8use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
9use std::sync::{Condvar, Mutex};
10use std::time::Duration;
11
12use super::{Client, ConsumeOptions, Error, Message, VisibilityResponse};
13
14pub const DEFAULT_MAX_MESSAGES: u32 = 1;
16pub const DEFAULT_WAIT_TIME: u32 = 20;
18pub const DEFAULT_VISIBILITY_TIMEOUT: u32 = 120;
20
21pub type HandlerError = Box<dyn std::error::Error>;
23
24#[derive(Debug)]
26pub enum ConsumeError {
27 Api(Error),
28 Handler(HandlerError),
29}
30
31impl fmt::Display for ConsumeError {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 match self {
34 ConsumeError::Api(e) => write!(f, "{e}"),
35 ConsumeError::Handler(e) => write!(f, "handler: {e}"),
36 }
37 }
38}
39
40impl std::error::Error for ConsumeError {}
41
42impl From<Error> for ConsumeError {
43 fn from(e: Error) -> ConsumeError {
44 ConsumeError::Api(e)
45 }
46}
47
48pub struct Delivery<'a, T = ()> {
51 pub message: &'a Message,
52 pub body: T,
53 pub delete: bool,
56 client: &'a Client,
57 topic: &'a str,
58 lease: &'a AtomicU32,
59 lost: &'a AtomicBool,
60}
61
62impl<T> Delivery<'_, T> {
63 pub fn change_visibility(&self, seconds: u32) -> Result<VisibilityResponse, Error> {
65 let reply = self.client.change_visibility(
66 self.topic,
67 &self.message.id,
68 &self.message.receipt_token,
69 seconds,
70 )?;
71 self.lease.store(seconds, Ordering::SeqCst);
72 Ok(reply)
73 }
74
75 pub fn lease_seconds(&self) -> u32 {
77 self.lease.load(Ordering::SeqCst)
78 }
79
80 pub fn lease_lost(&self) -> bool {
82 self.lost.load(Ordering::SeqCst)
83 }
84}
85
86fn with_defaults(options: &ConsumeOptions) -> ConsumeOptions {
88 let mut options = options.clone();
89 options.max_messages.get_or_insert(DEFAULT_MAX_MESSAGES);
90 options.wait_time.get_or_insert(DEFAULT_WAIT_TIME);
91 options
92 .visibility_timeout
93 .get_or_insert(DEFAULT_VISIBILITY_TIMEOUT);
94 options
95}
96
97fn renewal_interval(lease_seconds: u32) -> Duration {
100 Duration::from_secs_f64(f64::from(lease_seconds.max(1)) / 3.0)
101}
102
103impl Client {
104 pub fn consume_with<H>(
107 &self,
108 topic: &str,
109 options: &ConsumeOptions,
110 mut handler: H,
111 ) -> Result<usize, ConsumeError>
112 where
113 H: FnMut(&mut Delivery<'_, ()>) -> Result<(), HandlerError>,
114 {
115 let options = with_defaults(options);
116 let lease = options
117 .visibility_timeout
118 .unwrap_or(DEFAULT_VISIBILITY_TIMEOUT);
119 let messages = self.consume(topic, &options)?;
120 let mut handled = 0;
121 for message in &messages {
122 self.handle_one(topic, lease, message, (), &mut handler)?;
123 handled += 1;
124 }
125 Ok(handled)
126 }
127
128 fn handle_one<T, H>(
129 &self,
130 topic: &str,
131 lease_seconds: u32,
132 message: &Message,
133 body: T,
134 handler: &mut H,
135 ) -> Result<(), ConsumeError>
136 where
137 H: FnMut(&mut Delivery<'_, T>) -> Result<(), HandlerError>,
138 {
139 let lease = AtomicU32::new(lease_seconds);
140 let lost = AtomicBool::new(false);
141 let stop = (Mutex::new(false), Condvar::new());
142 let mut delivery = Delivery {
143 message,
144 body,
145 delete: true,
146 client: self,
147 topic,
148 lease: &lease,
149 lost: &lost,
150 };
151 let result = std::thread::scope(|scope| {
152 scope.spawn(|| self.keep_alive(topic, message, &lease, &lost, &stop));
153 let result = handler(&mut delivery);
154 *stop.0.lock().unwrap() = true;
155 stop.1.notify_all();
156 result
157 });
158 result.map_err(ConsumeError::Handler)?;
159 if delivery.delete {
160 match self.ack(topic, &message.id, &message.receipt_token) {
161 Err(e) if e.status == 409 || e.status == 404 => {}
163 other => other?,
164 }
165 }
166 Ok(())
167 }
168
169 fn keep_alive(
171 &self,
172 topic: &str,
173 message: &Message,
174 lease: &AtomicU32,
175 lost: &AtomicBool,
176 stop: &(Mutex<bool>, Condvar),
177 ) {
178 loop {
179 let interval = renewal_interval(lease.load(Ordering::SeqCst));
180 let guard = stop.0.lock().unwrap();
181 let (guard, _) = stop
182 .1
183 .wait_timeout_while(guard, interval, |stopped| !*stopped)
184 .unwrap();
185 if *guard {
186 return;
187 }
188 drop(guard);
189 if let Err(e) = self.change_visibility(
190 topic,
191 &message.id,
192 &message.receipt_token,
193 lease.load(Ordering::SeqCst),
194 ) {
195 if e.status == 409 || e.status == 404 {
196 lost.store(true, Ordering::SeqCst);
198 return;
199 }
200 }
202 }
203 }
204}
205
206#[cfg(feature = "serde")]
207impl Client {
208 pub fn publish_object<T: serde::Serialize>(
211 &self,
212 topic: &str,
213 value: &T,
214 ) -> Result<super::PublishResponse, Error> {
215 let json = serde_json::to_vec(value)
216 .map_err(|e| Error::client("client.invalid-request", format!("objeto: {e}")))?;
217 self.publish(topic, &json, &super::PublishOptions::default())
218 }
219
220 pub fn consume_typed<T, H>(
224 &self,
225 topic: &str,
226 options: &ConsumeOptions,
227 mut handler: H,
228 ) -> Result<usize, ConsumeError>
229 where
230 T: serde::de::DeserializeOwned,
231 H: FnMut(&mut Delivery<'_, T>) -> Result<(), HandlerError>,
232 {
233 let options = with_defaults(options);
234 let lease = options
235 .visibility_timeout
236 .unwrap_or(DEFAULT_VISIBILITY_TIMEOUT);
237 let messages = self.consume(topic, &options)?;
238 let mut handled = 0;
239 for message in &messages {
240 let body: T = serde_json::from_slice(&message.body).map_err(|e| {
241 Error::client(
242 "client.invalid-response",
243 format!("message body {}: {e}", message.id),
244 )
245 })?;
246 self.handle_one(topic, lease, message, body, &mut handler)?;
247 handled += 1;
248 }
249 Ok(handled)
250 }
251}
252
253#[cfg(test)]
254mod tests;