unistore_progress/
event.rs1use crate::deps::Duration;
8
9#[derive(Debug, Clone)]
11pub struct ProgressEvent {
12 pub current: u64,
14 pub total: u64,
16 pub message: String,
18 pub elapsed: Duration,
20 pub eta: Option<Duration>,
22 pub finished: bool,
24}
25
26impl ProgressEvent {
27 pub fn percentage(&self) -> f64 {
29 if self.total == 0 {
30 return 0.0;
31 }
32 (self.current as f64 / self.total as f64) * 100.0
33 }
34
35 pub fn ratio(&self) -> f64 {
37 if self.total == 0 {
38 return 0.0;
39 }
40 self.current as f64 / self.total as f64
41 }
42
43 pub fn message(&self) -> &str {
45 &self.message
46 }
47
48 pub fn is_finished(&self) -> bool {
50 self.finished
51 }
52
53 pub fn rate(&self) -> f64 {
55 let secs = self.elapsed.as_secs_f64();
56 if secs == 0.0 {
57 return 0.0;
58 }
59 self.current as f64 / secs
60 }
61
62 pub fn to_string_human(&self) -> String {
64 let pct = self.percentage();
65 let rate = self.rate();
66
67 let eta_str = match self.eta {
68 Some(eta) => format_duration(eta),
69 None => "计算中...".to_string(),
70 };
71
72 if self.finished {
73 format!(
74 "✓ 完成 {}/{} (用时: {})",
75 self.current,
76 self.total,
77 format_duration(self.elapsed)
78 )
79 } else {
80 format!(
81 "{:.1}% ({}/{}) - {:.1}/s - ETA: {} - {}",
82 pct, self.current, self.total, rate, eta_str, self.message
83 )
84 }
85 }
86}
87
88fn format_duration(d: Duration) -> String {
90 let secs = d.as_secs();
91 if secs < 60 {
92 format!("{}s", secs)
93 } else if secs < 3600 {
94 format!("{}m {}s", secs / 60, secs % 60)
95 } else {
96 format!("{}h {}m", secs / 3600, (secs % 3600) / 60)
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn test_percentage() {
106 let event = ProgressEvent {
107 current: 50,
108 total: 100,
109 message: "test".to_string(),
110 elapsed: Duration::from_secs(10),
111 eta: Some(Duration::from_secs(10)),
112 finished: false,
113 };
114 assert!((event.percentage() - 50.0).abs() < 0.001);
115 }
116
117 #[test]
118 fn test_ratio() {
119 let event = ProgressEvent {
120 current: 25,
121 total: 100,
122 message: "test".to_string(),
123 elapsed: Duration::from_secs(5),
124 eta: None,
125 finished: false,
126 };
127 assert!((event.ratio() - 0.25).abs() < 0.001);
128 }
129
130 #[test]
131 fn test_rate() {
132 let event = ProgressEvent {
133 current: 100,
134 total: 200,
135 message: "test".to_string(),
136 elapsed: Duration::from_secs(10),
137 eta: None,
138 finished: false,
139 };
140 assert!((event.rate() - 10.0).abs() < 0.001);
141 }
142}