1use crate::ProgressEntry;
4use bytes::Bytes;
5
6pub type ProgressListener = Box<dyn FnMut(ProgressEntry) + Send + 'static>;
13
14pub trait Pusher: Send + 'static {
18 type Error: std::error::Error + Send + Sync + Unpin + 'static;
19 #[allow(clippy::missing_errors_doc)]
26 fn push(&mut self, range: &ProgressEntry, content: Bytes) -> Result<(), (Self::Error, Bytes)>;
27 #[allow(clippy::missing_errors_doc)]
32 fn flush(&mut self) -> Result<(), Self::Error> {
33 Ok(())
34 }
35 #[allow(clippy::needless_pass_by_value)]
42 #[allow(unused_variables)]
43 fn set_listener(&mut self, cb: ProgressListener) {}
44}
45
46pub trait AnyError: std::error::Error + Send + Sync + Unpin + 'static {}
48impl<T: std::error::Error + Send + Sync + Unpin + 'static> AnyError for T {}
49
50impl std::error::Error for Box<dyn AnyError> {}
51
52#[allow(missing_debug_implementations)]
56pub struct BoxPusher {
57 pub pusher: Box<dyn Pusher<Error = Box<dyn AnyError>>>,
59}
60impl Pusher for BoxPusher {
61 type Error = Box<dyn AnyError>;
62 fn set_listener(&mut self, cb: ProgressListener) {
63 self.pusher.set_listener(cb);
64 }
65 fn push(&mut self, range: &ProgressEntry, content: Bytes) -> Result<(), (Self::Error, Bytes)> {
66 self.pusher.push(range, content)
67 }
68 fn flush(&mut self) -> Result<(), Self::Error> {
69 self.pusher.flush()
70 }
71}
72
73struct PusherAdapter<P: Pusher> {
74 inner: P,
75}
76impl<P: Pusher> Pusher for PusherAdapter<P> {
77 type Error = Box<dyn AnyError>;
78 fn set_listener(&mut self, cb: ProgressListener) {
79 self.inner.set_listener(cb);
80 }
81 fn push(&mut self, range: &ProgressEntry, content: Bytes) -> Result<(), (Self::Error, Bytes)> {
82 self.inner
83 .push(range, content)
84 .map_err(|(e, b)| (BoxPusher::upcast(e), b))
85 }
86 fn flush(&mut self) -> Result<(), Self::Error> {
87 self.inner.flush().map_err(|e| BoxPusher::upcast(e))
88 }
89}
90
91impl BoxPusher {
92 pub fn new<P: Pusher>(pusher: P) -> Self {
93 Self {
94 pusher: Box::new(PusherAdapter { inner: pusher }),
95 }
96 }
97 pub fn upcast<E: AnyError>(e: E) -> Box<dyn AnyError> {
98 Box::new(e)
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 #![allow(clippy::unwrap_used)]
105 use super::*;
106 use std::sync::atomic::{AtomicBool, Ordering};
107 use std::sync::{Arc, Mutex};
108
109 struct DummyPusher;
111 impl Pusher for DummyPusher {
112 type Error = std::io::Error;
113 fn push(
114 &mut self,
115 _range: &ProgressEntry,
116 _content: Bytes,
117 ) -> Result<(), (Self::Error, Bytes)> {
118 Ok(())
119 }
120 }
121
122 #[test]
123 fn default_set_listener_is_noop() {
124 let mut p = DummyPusher;
127 p.set_listener(Box::new(|_| {}));
128 p.push(&(0..0), Bytes::new()).unwrap();
129 }
130
131 #[test]
132 fn upcast_boxes_any_error() {
133 let boxed: Box<dyn AnyError> = BoxPusher::upcast(std::io::Error::other("boom"));
135 let _ = boxed;
136 }
137
138 #[derive(Clone)]
142 struct RecordingPusher {
143 pushes: Arc<Mutex<Vec<(ProgressEntry, Bytes)>>>,
144 fail_push: Arc<AtomicBool>,
145 fail_flush: Arc<AtomicBool>,
146 listener_set: Arc<AtomicBool>,
147 listener: Arc<Mutex<Option<ProgressListener>>>,
148 }
149 impl RecordingPusher {
150 fn new() -> Self {
151 Self {
152 pushes: Arc::new(Mutex::new(Vec::new())),
153 fail_push: Arc::new(AtomicBool::new(false)),
154 fail_flush: Arc::new(AtomicBool::new(false)),
155 listener_set: Arc::new(AtomicBool::new(false)),
156 listener: Arc::new(Mutex::new(None)),
157 }
158 }
159 }
160 impl Pusher for RecordingPusher {
161 type Error = std::io::Error;
162 fn set_listener(&mut self, cb: ProgressListener) {
163 self.listener_set.store(true, Ordering::SeqCst);
164 *self.listener.lock().unwrap() = Some(cb);
165 }
166 fn push(
167 &mut self,
168 range: &ProgressEntry,
169 bytes: Bytes,
170 ) -> Result<(), (Self::Error, Bytes)> {
171 if self.fail_push.swap(false, Ordering::SeqCst) {
172 return Err((std::io::Error::other("push"), bytes));
173 }
174 if let Some(cb) = self.listener.lock().unwrap().as_mut() {
175 cb(range.clone());
176 }
177 self.pushes.lock().unwrap().push((range.clone(), bytes));
178 Ok(())
179 }
180 fn flush(&mut self) -> Result<(), Self::Error> {
181 if self.fail_flush.swap(false, Ordering::SeqCst) {
182 Err(std::io::Error::other("flush"))
183 } else {
184 Ok(())
185 }
186 }
187 }
188
189 #[test]
190 fn box_pusher_forwards_push_flush_and_listener() {
191 let inner = RecordingPusher::new();
193 let mut bp = BoxPusher::new(inner.clone());
194 bp.set_listener(Box::new(|_| {}));
195 assert!(inner.listener_set.load(Ordering::SeqCst));
196 bp.push(&(0..3), Bytes::copy_from_slice(b"abc")).unwrap();
197 bp.flush().unwrap();
198 let pushes = inner.pushes.lock().unwrap();
199 assert_eq!(pushes.len(), 1);
200 assert_eq!(pushes[0].0, 0..3);
201 drop(pushes);
202 }
203
204 #[test]
205 fn box_pusher_upcasts_push_error() {
206 let inner = RecordingPusher::new();
208 inner.fail_push.store(true, Ordering::SeqCst);
209 let mut bp = BoxPusher::new(inner);
210 let res = bp.push(&(0..3), Bytes::copy_from_slice(b"abc"));
211 assert!(res.is_err());
212 let _ = res.unwrap_err();
213 }
214
215 #[test]
216 fn box_pusher_upcasts_flush_error() {
217 let inner = RecordingPusher::new();
219 inner.fail_flush.store(true, Ordering::SeqCst);
220 let mut bp = BoxPusher::new(inner);
221 assert!(bp.flush().is_err());
222 }
223
224 #[test]
225 fn box_pusher_listener_fires_on_successful_push() {
226 let inner = RecordingPusher::new();
230 let seen = Arc::new(Mutex::new(Vec::<ProgressEntry>::new()));
231 let seen2 = seen.clone();
232 let mut bp = BoxPusher::new(inner);
233 bp.set_listener(Box::new(move |r| seen2.lock().unwrap().push(r)));
234 bp.push(&(0..3), Bytes::copy_from_slice(b"abc")).unwrap();
235 let s = seen.lock().unwrap();
236 assert_eq!(s.len(), 1);
237 assert_eq!(s[0], 0..3);
238 }
239
240 #[test]
241 fn box_pusher_push_error_preserves_unwritten_bytes() {
242 let inner = RecordingPusher::new();
246 inner.fail_push.store(true, Ordering::SeqCst);
247 let mut bp = BoxPusher::new(inner);
248 let payload = Bytes::copy_from_slice(b"hello");
249 let res = bp.push(&(0..5), payload.clone());
250 let (_, unwritten) = res.unwrap_err();
251 assert_eq!(unwritten, payload);
252 }
253
254 #[test]
255 fn box_pusher_default_flush_is_ok() {
256 let mut bp = BoxPusher::new(DummyPusher);
259 assert!(bp.flush().is_ok());
260 }
261}