1mod estimate;
28
29pub use estimate::{OverallEstimate, OverallEstimator, PhaseRates};
30
31#[non_exhaustive]
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
37pub enum ProgressPhase {
38 ReceivingObjects,
40 IndexingObjects,
42 ResolvingDeltas,
44 CopyingObjects,
46 CheckingOutFiles,
48 StagingFiles,
50 TransferringBundle,
55}
56
57impl ProgressPhase {
58 #[must_use]
60 pub fn default_unit(self) -> ProgressUnit {
61 match self {
62 Self::ReceivingObjects
63 | Self::IndexingObjects
64 | Self::ResolvingDeltas
65 | Self::CopyingObjects => ProgressUnit::Objects,
66 Self::CheckingOutFiles | Self::StagingFiles => ProgressUnit::Files,
67 Self::TransferringBundle => ProgressUnit::Bytes,
68 }
69 }
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
74pub enum ProgressUnit {
75 Objects,
77 Bytes,
79 Files,
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
89pub struct ReceivedObjects {
90 pub received: u64,
92 pub total: u64,
94 pub deltas: u64,
96}
97
98pub trait ProgressSink {
105 fn start(&mut self, phase: ProgressPhase, total: Option<u64>) {
112 let _ = (phase, total);
113 }
114
115 fn start_with_unit(&mut self, phase: ProgressPhase, unit: ProgressUnit, total: Option<u64>) {
121 let _ = unit;
122 self.start(phase, total);
123 }
124
125 fn inc(&mut self, units: u64) {
127 let _ = units;
128 }
129
130 fn set(&mut self, current: u64) {
132 let _ = current;
133 }
134
135 fn finish(&mut self) {}
137
138 fn should_cancel(&self) -> bool {
144 false
145 }
146}
147
148impl<T: ProgressSink + ?Sized> ProgressSink for &mut T {
151 fn start(&mut self, phase: ProgressPhase, total: Option<u64>) {
152 (**self).start(phase, total);
153 }
154 fn start_with_unit(&mut self, phase: ProgressPhase, unit: ProgressUnit, total: Option<u64>) {
155 (**self).start_with_unit(phase, unit, total);
156 }
157 fn inc(&mut self, units: u64) {
158 (**self).inc(units);
159 }
160 fn set(&mut self, current: u64) {
161 (**self).set(current);
162 }
163 fn finish(&mut self) {
164 (**self).finish();
165 }
166 fn should_cancel(&self) -> bool {
167 (**self).should_cancel()
168 }
169}
170
171#[derive(Debug, Default, Clone, Copy)]
174pub struct NullProgress;
175
176impl ProgressSink for NullProgress {}
177
178pub trait Cancel {
181 fn is_cancelled(&self) -> bool;
183
184 fn shared_watch(&self) -> Option<std::sync::Arc<std::sync::atomic::AtomicBool>> {
187 None
188 }
189}
190
191#[derive(Debug, Default, Clone, Copy)]
194pub struct NeverCancel;
195
196impl Cancel for NeverCancel {
197 fn is_cancelled(&self) -> bool {
198 false
199 }
200}
201
202#[derive(Debug, Clone)]
204pub struct FlagCancel(pub std::sync::Arc<std::sync::atomic::AtomicBool>);
205
206impl FlagCancel {
207 #[must_use]
209 pub fn new() -> Self {
210 Self(std::sync::Arc::new(std::sync::atomic::AtomicBool::new(
211 false,
212 )))
213 }
214
215 pub fn cancel(&self) {
217 self.0.store(true, std::sync::atomic::Ordering::Relaxed);
218 }
219}
220
221impl Default for FlagCancel {
222 fn default() -> Self {
223 Self::new()
224 }
225}
226
227impl Cancel for FlagCancel {
228 fn is_cancelled(&self) -> bool {
229 self.0.load(std::sync::atomic::Ordering::Relaxed)
230 }
231
232 fn shared_watch(&self) -> Option<std::sync::Arc<std::sync::atomic::AtomicBool>> {
233 Some(std::sync::Arc::clone(&self.0))
234 }
235}
236
237#[must_use]
243pub fn cancel_watch_arc(cancel: &dyn Cancel) -> std::sync::Arc<std::sync::atomic::AtomicBool> {
244 cancel.shared_watch().unwrap_or_else(|| {
245 std::sync::Arc::new(std::sync::atomic::AtomicBool::new(cancel.is_cancelled()))
246 })
247}
248
249#[inline]
251pub fn poll_watch(watch: &std::sync::atomic::AtomicBool) -> crate::error::Result<()> {
252 if watch.load(std::sync::atomic::Ordering::Relaxed) {
253 Err(crate::error::Error::Cancelled)
254 } else {
255 Ok(())
256 }
257}
258
259#[inline]
261pub fn poll_cancel_watch(
262 progress: &mut dyn ProgressSink,
263 cancel: &dyn Cancel,
264 watch: &std::sync::atomic::AtomicBool,
265) -> crate::error::Result<()> {
266 if cancel.is_cancelled() || progress.should_cancel() {
267 watch.store(true, std::sync::atomic::Ordering::Relaxed);
268 return Err(crate::error::Error::Cancelled);
269 }
270 poll_watch(watch)
271}
272
273pub struct WatchProgress<'a> {
275 inner: &'a mut dyn ProgressSink,
276 watch: std::sync::Arc<std::sync::atomic::AtomicBool>,
277}
278
279impl<'a> WatchProgress<'a> {
280 #[must_use]
282 pub fn new(
283 inner: &'a mut dyn ProgressSink,
284 watch: std::sync::Arc<std::sync::atomic::AtomicBool>,
285 ) -> Self {
286 Self { inner, watch }
287 }
288
289 fn note_cancel(&self) {
290 if self.inner.should_cancel() {
291 self.watch.store(true, std::sync::atomic::Ordering::Relaxed);
292 }
293 }
294}
295
296impl ProgressSink for WatchProgress<'_> {
297 fn start(&mut self, phase: ProgressPhase, total: Option<u64>) {
298 self.note_cancel();
299 self.inner.start(phase, total);
300 }
301 fn start_with_unit(&mut self, phase: ProgressPhase, unit: ProgressUnit, total: Option<u64>) {
302 self.note_cancel();
303 self.inner.start_with_unit(phase, unit, total);
304 }
305 fn inc(&mut self, units: u64) {
306 self.note_cancel();
307 self.inner.inc(units);
308 }
309 fn set(&mut self, current: u64) {
310 self.note_cancel();
311 self.inner.set(current);
312 }
313 fn finish(&mut self) {
314 self.note_cancel();
315 self.inner.finish();
316 }
317 fn should_cancel(&self) -> bool {
318 self.inner.should_cancel()
319 }
320}
321
322pub(crate) struct ProgressScope<'a> {
325 sink: WatchProgress<'a>,
326 cancel: &'a dyn Cancel,
327 watch: std::sync::Arc<std::sync::atomic::AtomicBool>,
328}
329
330impl<'a> ProgressScope<'a> {
331 pub(crate) fn new(sink: &'a mut dyn ProgressSink, cancel: &'a dyn Cancel) -> Self {
332 let watch = cancel_watch_arc(cancel);
333 Self {
334 sink: WatchProgress::new(sink, std::sync::Arc::clone(&watch)),
335 cancel,
336 watch,
337 }
338 }
339
340 pub(crate) fn poll(&mut self) -> crate::error::Result<()> {
342 poll_cancel_watch(&mut self.sink, self.cancel, &self.watch)
343 }
344
345 pub(crate) fn watch(&self) -> &std::sync::atomic::AtomicBool {
347 &self.watch
348 }
349
350 pub(crate) fn sink(&mut self) -> &mut dyn ProgressSink {
351 &mut self.sink
352 }
353}
354
355pub(crate) struct UncancellableProgress<'a> {
358 inner: &'a mut dyn ProgressSink,
359}
360
361impl<'a> UncancellableProgress<'a> {
362 pub(crate) fn new(inner: &'a mut dyn ProgressSink) -> Self {
363 Self { inner }
364 }
365}
366
367impl ProgressSink for UncancellableProgress<'_> {
368 fn start(&mut self, phase: ProgressPhase, total: Option<u64>) {
369 self.inner.start(phase, total);
370 }
371 fn start_with_unit(&mut self, phase: ProgressPhase, unit: ProgressUnit, total: Option<u64>) {
372 self.inner.start_with_unit(phase, unit, total);
373 }
374 fn inc(&mut self, units: u64) {
375 self.inner.inc(units);
376 }
377 fn set(&mut self, current: u64) {
378 self.inner.set(current);
379 }
380 fn finish(&mut self) {
381 self.inner.finish();
382 }
383}
384
385#[inline]
387pub fn poll_cancel(
388 progress: &mut dyn ProgressSink,
389 cancel: &dyn Cancel,
390) -> crate::error::Result<()> {
391 if cancel.is_cancelled() || progress.should_cancel() {
392 Err(crate::error::Error::Cancelled)
393 } else {
394 Ok(())
395 }
396}
397
398pub struct CancelProgress<'a> {
403 inner: &'a mut dyn ProgressSink,
404 cancel: &'a dyn Cancel,
405 stopped: std::cell::Cell<bool>,
406}
407
408impl<'a> CancelProgress<'a> {
409 pub fn new(inner: &'a mut dyn ProgressSink, cancel: &'a dyn Cancel) -> Self {
411 Self {
412 inner,
413 cancel,
414 stopped: std::cell::Cell::new(false),
415 }
416 }
417
418 #[must_use]
420 pub fn stopped(&self) -> bool {
421 self.stopped.get()
422 }
423
424 pub fn finish_poll(&self) -> crate::error::Result<()> {
426 if self.stopped() {
427 Err(crate::error::Error::Cancelled)
428 } else {
429 Ok(())
430 }
431 }
432
433 fn note_stop(&self) {
434 if self.cancel.is_cancelled() || self.inner.should_cancel() {
435 self.stopped.set(true);
436 }
437 }
438}
439
440impl ProgressSink for CancelProgress<'_> {
441 fn start(&mut self, phase: ProgressPhase, total: Option<u64>) {
442 self.note_stop();
443 if !self.stopped.get() {
444 self.inner.start(phase, total);
445 }
446 }
447
448 fn start_with_unit(&mut self, phase: ProgressPhase, unit: ProgressUnit, total: Option<u64>) {
449 self.note_stop();
450 if !self.stopped.get() {
451 self.inner.start_with_unit(phase, unit, total);
452 }
453 }
454
455 fn inc(&mut self, units: u64) {
456 self.note_stop();
457 if !self.stopped.get() {
458 self.inner.inc(units);
459 }
460 }
461
462 fn set(&mut self, current: u64) {
463 self.note_stop();
464 if !self.stopped.get() {
465 self.inner.set(current);
466 }
467 }
468
469 fn finish(&mut self) {
470 self.note_stop();
471 if !self.stopped.get() {
472 self.inner.finish();
473 }
474 }
475
476 fn should_cancel(&self) -> bool {
477 self.stopped.get() || self.cancel.is_cancelled() || self.inner.should_cancel()
478 }
479}
480
481#[cfg(test)]
482mod tests {
483 use super::*;
484 use crate::fetch::{NoProgress, Progress};
485
486 #[derive(Default)]
489 struct PhaseRecorder {
490 started: Vec<(ProgressPhase, Option<u64>)>,
491 incs: u64,
492 finished: usize,
493 }
494
495 impl ProgressSink for PhaseRecorder {
496 fn start(&mut self, phase: ProgressPhase, total: Option<u64>) {
497 self.started.push((phase, total));
498 }
499 fn inc(&mut self, units: u64) {
500 self.incs += units;
501 }
502 fn finish(&mut self) {
503 self.finished += 1;
504 }
505 }
506
507 fn drive_typed(sink: &mut impl ProgressSink) {
508 sink.start(ProgressPhase::ReceivingObjects, Some(3));
509 sink.inc(1);
510 sink.inc(2);
511 sink.finish();
512 }
513
514 #[test]
515 fn null_progress_is_inert() {
516 drive_typed(&mut NullProgress);
517 }
518
519 #[test]
520 fn no_progress_is_inert() {
521 drive_typed(&mut NoProgress);
522 }
523
524 #[test]
525 fn typed_phase_recorder_roundtrip() {
526 let mut rec = PhaseRecorder::default();
527 drive_typed(&mut &mut rec);
528 assert_eq!(
529 rec.started,
530 vec![(ProgressPhase::ReceivingObjects, Some(3))]
531 );
532 assert_eq!(rec.incs, 3);
533 assert_eq!(rec.finished, 1);
534 }
535
536 #[test]
537 fn progress_supertrait_upcasts() {
538 struct SidebandCap(Vec<u8>);
539
540 impl ProgressSink for SidebandCap {}
541
542 impl Progress for SidebandCap {
543 fn message(&mut self, bytes: &[u8]) {
544 self.0.extend_from_slice(bytes);
545 }
546 }
547
548 fn takes_sink(sink: &mut dyn ProgressSink) {
549 sink.start(ProgressPhase::IndexingObjects, None);
550 sink.finish();
551 }
552
553 let mut cap = SidebandCap(Vec::new());
554 let progress: &mut dyn Progress = &mut cap;
555 takes_sink(progress);
556 }
557
558 #[test]
559 fn never_cancel_never_cancels() {
560 assert!(!NeverCancel.is_cancelled());
561 }
562
563 #[test]
564 fn poll_cancel_returns_typed_error() {
565 let flag = FlagCancel::new();
566 flag.cancel();
567 let mut sink = NullProgress;
568 let err = poll_cancel(&mut sink, &flag).expect_err("cancelled");
569 assert!(matches!(err, crate::error::Error::Cancelled));
570 }
571
572 #[test]
573 fn phase_default_unit() {
574 assert_eq!(
575 ProgressPhase::ReceivingObjects.default_unit(),
576 ProgressUnit::Objects
577 );
578 assert_eq!(
579 ProgressPhase::CheckingOutFiles.default_unit(),
580 ProgressUnit::Files
581 );
582 assert_eq!(
583 ProgressPhase::StagingFiles.default_unit(),
584 ProgressUnit::Files
585 );
586 }
587}