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