cached_path/
progress_bar.rs1use std::io::{self, Write};
2use std::time::Instant;
3
4#[derive(Debug, Clone, Default)]
9pub enum ProgressBar {
10 #[default]
12 Full,
13 Light,
18}
19
20impl ProgressBar {
21 pub(crate) fn wrap_download<W: Write>(
22 &self,
23 resource: &str,
24 content_length: Option<u64>,
25 writer: W,
26 ) -> DownloadWrapper<W> {
27 let bar: Box<dyn DownloadBar> = match self {
28 ProgressBar::Full => Box::new(FullDownloadBar::new(content_length)),
29 ProgressBar::Light => Box::new(LightDownloadBar::new(resource, content_length)),
30 };
31 DownloadWrapper::new(bar, writer)
32 }
33}
34
35pub(crate) struct DownloadWrapper<W: Write> {
36 bar: Box<dyn DownloadBar>,
37 writer: W,
38}
39
40impl<W> DownloadWrapper<W>
41where
42 W: Write,
43{
44 fn new(bar: Box<dyn DownloadBar>, writer: W) -> Self {
45 Self { bar, writer }
46 }
47
48 pub(crate) fn finish(&self) {
49 self.bar.finish();
50 }
51}
52
53impl<W: Write> Write for DownloadWrapper<W> {
54 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
55 self.writer.write(buf)
56 }
57
58 fn flush(&mut self) -> io::Result<()> {
59 self.writer.flush()
60 }
61
62 fn write_vectored(&mut self, bufs: &[io::IoSlice]) -> io::Result<usize> {
63 self.writer.write_vectored(bufs)
64 }
65
66 fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
67 self.writer.write_all(buf).map(|()| {
68 self.bar.tick(buf.len());
69 })
70 }
71}
72
73trait DownloadBar {
74 fn tick(&mut self, chunk_size: usize);
75
76 fn finish(&self);
77}
78
79pub(crate) struct FullDownloadBar {
80 bar: indicatif::ProgressBar,
81}
82
83impl FullDownloadBar {
84 pub(crate) fn new(content_length: Option<u64>) -> Self {
85 let bar = match content_length {
86 Some(length) => {
87 let bar = indicatif::ProgressBar::new(length);
88 bar.set_style(
89 indicatif::ProgressStyle::default_bar()
90 .progress_chars("=>-")
91 .template(
92 "{msg:.bold.cyan/blue} [{bar:20.cyan/blue}][{percent}%] {bytes}/{total_bytes:.bold} |{bytes_per_sec}|",
93 )
94 );
95 bar
96 }
97 None => {
98 let bar = indicatif::ProgressBar::new_spinner();
99 bar.set_style(
100 indicatif::ProgressStyle::default_bar()
101 .tick_strings(&[
102 "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖",
103 "⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐",
104 "⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒",
105 "⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋",
106 "⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈",
107 "⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈⠉",
108 "⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈⠉⠙⠚",
109 "⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈⠉⠙⠚⠒⠂",
110 "⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈⠉⠙⠚⠒⠂⠂⠒",
111 "⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈⠉⠙⠚⠒⠂⠂⠒⠲⠴",
112 "⠒⠐⠐⠒⠓⠋⠉⠈⠈⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄",
113 "⠐⠒⠓⠋⠉⠈⠈⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤",
114 "⠓⠋⠉⠈⠈⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠",
115 ])
116 .template(
117 "{msg:.bold.cyan/blue} [{spinner:.cyan/blue}] {bytes:.bold} |{bytes_per_sec}|",
118 ),
119 );
120 bar
121 }
122 };
123 bar.set_message("Downloading");
124 bar.set_draw_delta(1_000_000);
128 Self { bar }
129 }
130}
131
132impl DownloadBar for FullDownloadBar {
133 fn tick(&mut self, chunk_size: usize) {
134 self.bar.inc(chunk_size as u64);
135 }
136
137 fn finish(&self) {
138 self.bar.set_message("Downloaded");
139 self.bar.set_style(
140 indicatif::ProgressStyle::default_bar()
141 .template("{msg:.green.bold} {total_bytes:.bold} in {elapsed}"),
142 );
143 self.bar.finish_at_current_pos();
144 }
145}
146
147pub(crate) struct LightDownloadBar {
148 start_time: Instant,
149 bytes: usize,
150 bytes_since_last_update: usize,
151}
152
153impl LightDownloadBar {
154 pub(crate) fn new(resource: &str, content_length: Option<u64>) -> Self {
155 if let Some(size) = content_length {
156 eprint!(
157 "Downloading {} [{}]...",
158 resource,
159 indicatif::HumanBytes(size)
160 );
161 } else {
162 eprint!("Downloading {}...", resource);
163 }
164 io::stderr().flush().ok();
165 Self {
166 start_time: Instant::now(),
167 bytes: 0,
168 bytes_since_last_update: 0,
169 }
170 }
171}
172
173impl DownloadBar for LightDownloadBar {
174 fn tick(&mut self, chunk_size: usize) {
175 self.bytes_since_last_update += chunk_size;
176 if self.bytes_since_last_update > 100_000_000 {
178 eprint!(".");
179 io::stderr().flush().ok();
180 self.bytes_since_last_update = 0;
181 }
182 self.bytes += chunk_size;
183 }
184
185 fn finish(&self) {
186 let duration = Instant::now().duration_since(self.start_time);
187 eprintln!(
188 " ✓ Done! Finished in {}",
189 indicatif::HumanDuration(duration)
190 );
191 io::stderr().flush().ok();
192 }
193}