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(
51 u64::try_from(nanos).unwrap_or(u64::MAX),
52 ))
53 }
54
55 fn percent(&self) -> Option<u16> {
57 if self.transfer_complete() {
58 return Some(100);
59 }
60 let total = self.transfer_total()?;
61 if total == 0 {
62 return Some(0);
63 }
64 let percent = u128::from(self.transferred()).saturating_mul(100) / u128::from(total);
65 Some(u16::try_from(percent.min(100)).unwrap_or(100))
66 }
67}
68
69#[derive(Clone, Copy, Debug, PartialEq, Eq)]
71pub enum TransferPhase {
72 Ready,
74 Waiting,
76 Rehearsing,
78 Extracting,
80 Downloading,
82 Paused,
84 Publishing,
86 Complete,
88 Failed,
90}
91
92impl TransferPhase {
93 pub const LIFECYCLE: [Self; 9] = [
95 Self::Ready,
96 Self::Waiting,
97 Self::Rehearsing,
98 Self::Extracting,
99 Self::Downloading,
100 Self::Paused,
101 Self::Publishing,
102 Self::Complete,
103 Self::Failed,
104 ];
105
106 pub const fn is_running(self) -> bool {
111 matches!(
112 self,
113 Self::Rehearsing
114 | Self::Extracting
115 | Self::Downloading
116 | Self::Paused
117 | Self::Publishing
118 )
119 }
120}