rsynko_manager/
transfer.rs1use alux_ext::ext;
2use std::path::Path;
3use std::time::Duration;
4
5pub trait TransferViewAlg {
7 fn transferred(&self) -> u64;
9 fn transfer_total(&self) -> Option<u64>;
11 fn transfer_elapsed(&self) -> Duration;
13 fn transfer_complete(&self) -> bool;
15 fn transfer_destination(&self) -> Option<&Path>;
17 fn transfer_summary(&self) -> Option<&str>;
19 fn transfer_detail(&self) -> Option<&str>;
21}
22
23#[ext(name = TransferProgressExt)]
25pub impl<This> This
26where
27 This: TransferViewAlg,
28{
29 fn bytes_per_second(&self) -> Option<u64> {
31 let elapsed = self.transfer_elapsed().as_nanos();
32 let transferred = self.transferred();
33 if transferred == 0 || elapsed == 0 {
34 return None;
35 }
36 let rate = u128::from(transferred).saturating_mul(1_000_000_000) / elapsed;
37 Some(u64::try_from(rate.max(1)).unwrap_or(u64::MAX))
38 }
39
40 fn estimated_remaining(&self) -> Option<Duration> {
42 let total = self.transfer_total()?;
43 let elapsed = self.transfer_elapsed().as_nanos();
44 let transferred = self.transferred();
45 if transferred == 0 || elapsed == 0 {
46 return None;
47 }
48 let remaining = total.saturating_sub(transferred);
49 let nanos = u128::from(remaining).saturating_mul(elapsed) / u128::from(transferred);
50 Some(Duration::from_nanos(u64::try_from(nanos).unwrap_or(u64::MAX)))
51 }
52
53 fn percent(&self) -> Option<u16> {
55 if self.transfer_complete() {
56 return Some(100);
57 }
58 let total = self.transfer_total()?;
59 if total == 0 {
60 return Some(0);
61 }
62 let percent = u128::from(self.transferred()).saturating_mul(100) / u128::from(total);
63 Some(u16::try_from(percent.min(100)).unwrap_or(100))
64 }
65}
66
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
69pub enum TransferPhase {
70 Ready,
72 Waiting,
74 Rehearsing,
76 Extracting,
78 Downloading,
80 Paused,
82 Publishing,
84 Complete,
86 Failed,
88}
89
90impl TransferPhase {
91 pub const LIFECYCLE: [Self; 9] = [
93 Self::Ready,
94 Self::Waiting,
95 Self::Rehearsing,
96 Self::Extracting,
97 Self::Downloading,
98 Self::Paused,
99 Self::Publishing,
100 Self::Complete,
101 Self::Failed,
102 ];
103
104 pub const fn is_running(self) -> bool {
109 matches!(self, Self::Rehearsing | Self::Extracting | Self::Downloading | Self::Paused | Self::Publishing)
110 }
111}