Skip to content

Commit 068f1ff

Browse files
committed
tighten math about ProgressBar::render
when `total` is defined
1 parent 21a9610 commit 068f1ff

3 files changed

Lines changed: 23 additions & 11 deletions

File tree

clang-installer/src/downloader/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use reqwest::ClientBuilder;
22
use std::{
33
fs,
44
io::Write,
5+
num::NonZero,
56
path::Path,
67
time::{Duration, SystemTime},
78
};
@@ -58,7 +59,7 @@ async fn download(url: &Url, cache_path: &Path, timeout: u64) -> Result<(), Down
5859
return Err(e.into());
5960
}
6061
let mut tmp_file = tempfile::NamedTempFile::new()?;
61-
let content_len = response.content_length();
62+
let content_len = response.content_length().and_then(NonZero::new);
6263
let mut progress_bar = ProgressBar::new(content_len, "Downloading");
6364
progress_bar.render()?;
6465
while let Some(chunk) = response.chunk().await? {

clang-installer/src/downloader/pypi.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::{
77
collections::HashMap,
88
fs,
99
io::{Read, Write},
10+
num::NonZero,
1011
path::PathBuf,
1112
str::FromStr,
1213
time::Duration,
@@ -475,25 +476,29 @@ impl PyPiDownloader {
475476
if let Some(parent) = extracted_bin.parent() {
476477
fs::create_dir_all(parent)?;
477478
}
478-
let file_size = file.size();
479+
let file_size = NonZero::new(file.size());
479480
let mut out = fs::OpenOptions::new()
480481
.write(true)
481482
.create(true)
482483
.truncate(true)
483484
.open(extracted_bin)?;
484485
let mut buffer = [0; EXTRACTED_CHUNK_SIZE as usize];
485486
let mut total_extracted = 0;
486-
let mut progress_bar =
487-
ProgressBar::new(Some(file_size), "Extracting binary from wheel");
487+
let mut progress_bar = ProgressBar::new(file_size, "Extracting binary from wheel");
488488
progress_bar.render()?;
489-
while total_extracted < file_size {
489+
loop {
490490
let bytes_read = file.read(&mut buffer)?;
491491
if bytes_read == 0 {
492492
break;
493493
}
494494
total_extracted += bytes_read as u64;
495495
out.write_all(&buffer[..bytes_read])?;
496496
progress_bar.inc(bytes_read as u64)?;
497+
if let Some(total_size) = file_size
498+
&& total_extracted >= total_size.get()
499+
{
500+
break;
501+
}
497502
}
498503
progress_bar.finish()?;
499504
#[cfg(unix)]

clang-installer/src/progress_bar.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
use std::io::{IsTerminal, Result, Write, stdout};
1+
use std::{
2+
io::{IsTerminal, Result, Write, stdout},
3+
num::NonZero,
4+
};
25

36
/// A simple progress bar implementation that supports both interactive and non-interactive terminals.
47
pub struct ProgressBar {
58
/// The `total` size of the task being tracked, if known.
6-
total: Option<u64>,
9+
total: Option<NonZero<u64>>,
710
/// The `current` progress towards the `total`.
811
current: u64,
912
/// The number of `steps` completed in the progress bar.
@@ -43,7 +46,7 @@ impl ProgressBar {
4346
/// progress_bar.finish().unwrap(); // clean up and write a line break (move to next line)
4447
/// // stdout lock is released when `progress_bar` goes out of scope
4548
/// ```
46-
pub fn new(total: Option<u64>, prompt: &str) -> Self {
49+
pub fn new(total: Option<NonZero<u64>>, prompt: &str) -> Self {
4750
let stdout_handle = stdout().lock();
4851
let is_interactive = stdout_handle.is_terminal();
4952
Self {
@@ -61,7 +64,7 @@ impl ProgressBar {
6164
/// If the `total` is known, then the progress bar will be updated based on the percentage of `current` to `total`.
6265
/// If the `total` is unknown, then the progress bar will simply increment by one step for each call to this method.
6366
pub fn inc(&mut self, delta: u64) -> Result<()> {
64-
self.current += delta;
67+
self.current = self.current.saturating_add(delta);
6568
self.render()
6669
}
6770

@@ -77,7 +80,8 @@ impl ProgressBar {
7780
/// Subsequent updates should be made using [`Self::inc()`], which will call this method internally.
7881
pub fn render(&mut self) -> Result<()> {
7982
let advance_bar = self.total.map(|total| {
80-
let progress = self.current as f64 / total as f64;
83+
let total = total.get();
84+
let progress = self.current.min(total) as f64 / total as f64;
8185

8286
(progress * Self::MAX_BAR_WIDTH as f64).floor() as u32
8387
});
@@ -142,6 +146,8 @@ impl ProgressBar {
142146

143147
#[cfg(test)]
144148
mod tests {
149+
use std::num::NonZero;
150+
145151
use super::ProgressBar;
146152

147153
#[test]
@@ -155,7 +161,7 @@ mod tests {
155161

156162
#[test]
157163
fn with_total() {
158-
let mut progress_bar = ProgressBar::new(Some(100), "Processing");
164+
let mut progress_bar = ProgressBar::new(Some(NonZero::new(100).unwrap()), "Processing");
159165
for _ in 0..100 {
160166
progress_bar.inc(1).unwrap();
161167
}

0 commit comments

Comments
 (0)