1use crate::styles::*;
17use crate::term;
18use std::sync::atomic::{AtomicUsize, Ordering};
19use std::sync::Arc;
20
21pub struct Progress {
23 total: usize,
24 current: Arc<AtomicUsize>,
25}
26
27impl Progress {
28 pub fn new(total: usize) -> Self {
30 Self {
31 total,
32 current: Arc::new(AtomicUsize::new(0)),
33 }
34 }
35
36 pub fn ok(&self, kind: &str, source: &str, url: &str, local: &str, bytes: usize) {
42 let n = self.current.fetch_add(1, Ordering::Relaxed) + 1;
43 let total = self.total;
44 let size = format_bytes(bytes);
45 let term_w = term::width();
46
47 let counter = paint(DIM, &format!("({n}/{total})"));
49 let kind_s = paint(DIM, &format!("[{:<6}]", kind));
50 let src_s = paint(DIM, &format!("[{:<6}]", source));
51 let arrow = paint(DIM, "→");
52 let local_s = paint(CYAN, local);
53 let size_s = paint(DIM, &format!("[{size}]"));
54
55 let prefix_len = 3 + 8 + 8 + 4 + local.len() + size.len() + 12;
57 let url_budget = term_w.saturating_sub(prefix_len).max(20);
58 let url_display = truncate_url(url, url_budget);
59 let url_s = paint(DIM, &url_display);
60
61 eprintln!(
62 " {} {} {} {} {} {} {}",
63 counter, kind_s, src_s, url_s, arrow, local_s, size_s
64 );
65 }
66
67 pub fn fail(&self, kind: &str, url: &str, error: &str) {
73 let n = self.current.fetch_add(1, Ordering::Relaxed) + 1;
74 let total = self.total;
75 let counter = paint(DIM, &format!("({n}/{total})"));
76 let term_w = term::width();
77 let prefix_len = 3 + 8 + 8 + 4 + error.len() + 6;
78 let url_budget = term_w.saturating_sub(prefix_len).max(20);
79 let url_s = paint(DIM, &truncate_url(url, url_budget));
80 eprintln!(
81 " {} {} {} {} {}",
82 counter,
83 paint(ERR, &format!("[{:<6}]", kind)),
84 paint(ERR, "[error ]"),
85 url_s,
86 paint(ERR, &format!("({error})")),
87 );
88 }
89
90 pub fn finish(&self) {
92 eprintln!();
93 }
94}
95
96pub fn substep(url: &str, local: &str) {
102 eprintln!(
103 " {} {} {} {}",
104 paint(DIM, BRANCH),
105 paint(DIM, url),
106 paint(DIM, "→"),
107 paint(CYAN, local),
108 );
109}
110
111pub fn format_bytes(bytes: usize) -> String {
121 if bytes >= 1_048_576 {
122 format!("{:.2} MB", bytes as f64 / 1_048_576.0)
123 } else if bytes >= 1_024 {
124 format!("{:.2} KB", bytes as f64 / 1_024.0)
125 } else {
126 format!("{bytes} B")
127 }
128}
129
130fn truncate_url(url: &str, max_len: usize) -> String {
134 if url.len() <= max_len {
135 return url.to_string();
136 }
137 let keep = max_len.saturating_sub(1);
138 format!("…{}", &url[url.len() - keep..])
139}