Skip to content

Commit 8686e24

Browse files
authored
refactor: control byte display precision with std::fmt options (#15246)
### What does this PR try to resolve? Just found we can simplify `human_readable_bytes` a bit. This also remove the `bytesize` dependency, since we already have a hand-rolled function. (It is a good crate, though) ### How should we test and review this PR? Should have no real functional difference. `cargo package` starts reporting IEC (KiB) instead of SI (KB). So do some thresholds of `cargo package`. Could also switch entirely to `bytesize` if that is preferred. ### Additional information
2 parents d5b25b8 + 8f1122d commit 8686e24

File tree

10 files changed

+73
-78
lines changed

10 files changed

+73
-78
lines changed

Cargo.lock

Lines changed: 0 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ anyhow = "1.0.95"
2525
base64 = "0.22.1"
2626
blake3 = "1.5.5"
2727
build-rs = { version = "0.3.0", path = "crates/build-rs" }
28-
bytesize = "1.3"
2928
cargo = { path = "" }
3029
cargo-credential = { version = "0.4.2", path = "credential/cargo-credential" }
3130
cargo-credential-libsecret = { version = "0.4.13", path = "credential/cargo-credential-libsecret" }
@@ -157,7 +156,6 @@ anstyle.workspace = true
157156
anyhow.workspace = true
158157
base64.workspace = true
159158
blake3.workspace = true
160-
bytesize.workspace = true
161159
cargo-credential.workspace = true
162160
cargo-platform.workspace = true
163161
cargo-util-schemas.workspace = true

src/cargo/core/package.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use std::rc::Rc;
99
use std::time::{Duration, Instant};
1010

1111
use anyhow::Context as _;
12-
use bytesize::ByteSize;
1312
use cargo_util_schemas::manifest::RustVersion;
1413
use curl::easy::Easy;
1514
use curl::multi::{EasyHandle, Multi};
@@ -35,6 +34,7 @@ use crate::util::network::http::http_handle_and_timeout;
3534
use crate::util::network::http::HttpTimeout;
3635
use crate::util::network::retry::{Retry, RetryResult};
3736
use crate::util::network::sleep::SleepTracker;
37+
use crate::util::HumanBytes;
3838
use crate::util::{self, internal, GlobalContext, Progress, ProgressStyle};
3939

4040
/// Information about a package that is available somewhere in the file system.
@@ -914,7 +914,8 @@ impl<'a, 'gctx> Downloads<'a, 'gctx> {
914914
// have a great view into the progress of the extraction. Let's prepare
915915
// the user for this CPU-heavy step if it looks like it'll take some
916916
// time to do so.
917-
if dl.total.get() < ByteSize::kb(400).0 {
917+
let kib_400 = 1024 * 400;
918+
if dl.total.get() < kib_400 {
918919
self.tick(WhyTick::DownloadFinished)?;
919920
} else {
920921
self.tick(WhyTick::Extracting(&dl.id.name()))?;
@@ -1113,7 +1114,7 @@ impl<'a, 'gctx> Downloads<'a, 'gctx> {
11131114
}
11141115
}
11151116
if remaining > 0 && dur > Duration::from_millis(500) {
1116-
msg.push_str(&format!(", remaining bytes: {}", ByteSize(remaining)));
1117+
msg.push_str(&format!(", remaining bytes: {:.1}", HumanBytes(remaining)));
11171118
}
11181119
}
11191120
}
@@ -1153,20 +1154,21 @@ impl<'a, 'gctx> Drop for Downloads<'a, 'gctx> {
11531154
"crates"
11541155
};
11551156
let mut status = format!(
1156-
"{} {} ({}) in {}",
1157+
"{} {} ({:.1}) in {}",
11571158
self.downloads_finished,
11581159
crate_string,
1159-
ByteSize(self.downloaded_bytes),
1160+
HumanBytes(self.downloaded_bytes),
11601161
util::elapsed(self.start.elapsed())
11611162
);
11621163
// print the size of largest crate if it was >1mb
11631164
// however don't print if only a single crate was downloaded
11641165
// because it is obvious that it will be the largest then
1165-
if self.largest.0 > ByteSize::mb(1).0 && self.downloads_finished > 1 {
1166+
let mib_1 = 1024 * 1024;
1167+
if self.largest.0 > mib_1 && self.downloads_finished > 1 {
11661168
status.push_str(&format!(
1167-
" (largest was `{}` at {})",
1169+
" (largest was `{}` at {:.1})",
11681170
self.largest.1,
1169-
ByteSize(self.largest.0),
1171+
HumanBytes(self.largest.0),
11701172
));
11711173
}
11721174
// Clear progress before displaying final summary.

src/cargo/ops/cargo_clean.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use crate::ops;
55
use crate::util::edit_distance;
66
use crate::util::errors::CargoResult;
77
use crate::util::interning::InternedString;
8-
use crate::util::{human_readable_bytes, GlobalContext, Progress, ProgressStyle};
8+
use crate::util::HumanBytes;
9+
use crate::util::{GlobalContext, Progress, ProgressStyle};
910
use anyhow::bail;
1011
use cargo_util::paths;
1112
use std::collections::{HashMap, HashSet};
@@ -457,13 +458,8 @@ impl<'gctx> CleanContext<'gctx> {
457458
let byte_count = if self.total_bytes_removed == 0 {
458459
String::new()
459460
} else {
460-
// Don't show a fractional number of bytes.
461-
if self.total_bytes_removed < 1024 {
462-
format!(", {}B total", self.total_bytes_removed)
463-
} else {
464-
let (bytes, unit) = human_readable_bytes(self.total_bytes_removed);
465-
format!(", {bytes:.1}{unit} total")
466-
}
461+
let bytes = HumanBytes(self.total_bytes_removed);
462+
format!(", {bytes:.1} total")
467463
};
468464
// I think displaying the number of directories removed isn't
469465
// particularly interesting to the user. However, if there are 0

src/cargo/ops/cargo_package/mod.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ use crate::sources::{PathSource, CRATES_IO_REGISTRY};
2222
use crate::util::cache_lock::CacheLockMode;
2323
use crate::util::context::JobsConfig;
2424
use crate::util::errors::CargoResult;
25-
use crate::util::human_readable_bytes;
2625
use crate::util::restricted_names;
2726
use crate::util::toml::prepare_for_publish;
2827
use crate::util::FileLock;
2928
use crate::util::Filesystem;
3029
use crate::util::GlobalContext;
3130
use crate::util::Graph;
31+
use crate::util::HumanBytes;
3232
use crate::{drop_println, ops};
3333
use anyhow::{bail, Context as _};
3434
use cargo_util::paths;
@@ -129,13 +129,10 @@ fn create_package(
129129
.with_context(|| format!("could not learn metadata for: `{}`", dst_path.display()))?;
130130
let compressed_size = dst_metadata.len();
131131

132-
let uncompressed = human_readable_bytes(uncompressed_size);
133-
let compressed = human_readable_bytes(compressed_size);
132+
let uncompressed = HumanBytes(uncompressed_size);
133+
let compressed = HumanBytes(compressed_size);
134134

135-
let message = format!(
136-
"{} files, {:.1}{} ({:.1}{} compressed)",
137-
filecount, uncompressed.0, uncompressed.1, compressed.0, compressed.1,
138-
);
135+
let message = format!("{filecount} files, {uncompressed:.1} ({compressed:.1} compressed)");
139136
// It doesn't really matter if this fails.
140137
drop(gctx.shell().status("Packaged", message));
141138

src/cargo/sources/git/oxide.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
//! `utils` closely for now. One day it can be renamed into `utils` once `git2` isn't required anymore.
33
44
use crate::util::network::http::HttpTimeout;
5-
use crate::util::{human_readable_bytes, network, MetricsCounter, Progress};
5+
use crate::util::HumanBytes;
6+
use crate::util::{network, MetricsCounter, Progress};
67
use crate::{CargoResult, GlobalContext};
78
use cargo_util::paths;
89
use gix::bstr::{BString, ByteSlice};
@@ -148,8 +149,8 @@ fn translate_progress_to_bar(
148149
counter.add(received_bytes, now);
149150
last_percentage_update = now;
150151
}
151-
let (rate, unit) = human_readable_bytes(counter.rate() as u64);
152-
let msg = format!(", {rate:.2}{unit}/s");
152+
let rate = HumanBytes(counter.rate() as u64);
153+
let msg = format!(", {rate:.2}/s");
153154

154155
progress_bar.tick(
155156
(total_objects * (num_phases - 2)) + objects,

src/cargo/sources/git/utils.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@ use crate::sources::git::fetch::RemoteKind;
66
use crate::sources::git::oxide;
77
use crate::sources::git::oxide::cargo_config_to_gitoxide_overrides;
88
use crate::util::errors::CargoResult;
9-
use crate::util::{
10-
human_readable_bytes, network, GlobalContext, IntoUrl, MetricsCounter, Progress,
11-
};
9+
use crate::util::HumanBytes;
10+
use crate::util::{network, GlobalContext, IntoUrl, MetricsCounter, Progress};
1211
use anyhow::{anyhow, Context as _};
1312
use cargo_util::{paths, ProcessBuilder};
1413
use curl::easy::List;
@@ -914,8 +913,8 @@ pub fn with_fetch_options(
914913
counter.add(stats.received_bytes(), now);
915914
last_update = now;
916915
}
917-
let (rate, unit) = human_readable_bytes(counter.rate() as u64);
918-
format!(", {:.2}{}/s", rate, unit)
916+
let rate = HumanBytes(counter.rate() as u64);
917+
format!(", {rate:.2}/s")
919918
};
920919
progress
921920
.tick(stats.indexed_objects(), stats.total_objects(), &msg)

src/cargo/util/mod.rs

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,26 @@ pub fn elapsed(duration: Duration) -> String {
8686
}
8787

8888
/// Formats a number of bytes into a human readable SI-prefixed size.
89-
/// Returns a tuple of `(quantity, units)`.
90-
pub fn human_readable_bytes(bytes: u64) -> (f32, &'static str) {
91-
static UNITS: [&str; 7] = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"];
92-
let bytes = bytes as f32;
93-
let i = ((bytes.log2() / 10.0) as usize).min(UNITS.len() - 1);
94-
(bytes / 1024_f32.powi(i as i32), UNITS[i])
89+
pub struct HumanBytes(pub u64);
90+
91+
impl std::fmt::Display for HumanBytes {
92+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93+
const UNITS: [&str; 7] = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"];
94+
let bytes = self.0 as f32;
95+
let i = ((bytes.log2() / 10.0) as usize).min(UNITS.len() - 1);
96+
let unit = UNITS[i];
97+
let size = bytes / 1024_f32.powi(i as i32);
98+
99+
// Don't show a fractional number of bytes.
100+
if i == 0 {
101+
return write!(f, "{size}{unit}");
102+
}
103+
104+
let Some(precision) = f.precision() else {
105+
return write!(f, "{size}{unit}");
106+
};
107+
write!(f, "{size:.precision$}{unit}",)
108+
}
95109
}
96110

97111
pub fn indented_lines(text: &str) -> String {
@@ -162,32 +176,30 @@ pub fn get_umask() -> u32 {
162176
mod test {
163177
use super::*;
164178

179+
#[track_caller]
180+
fn t(bytes: u64, expected: &str) {
181+
assert_eq!(&HumanBytes(bytes).to_string(), expected);
182+
}
183+
165184
#[test]
166185
fn test_human_readable_bytes() {
167-
assert_eq!(human_readable_bytes(0), (0., "B"));
168-
assert_eq!(human_readable_bytes(8), (8., "B"));
169-
assert_eq!(human_readable_bytes(1000), (1000., "B"));
170-
assert_eq!(human_readable_bytes(1024), (1., "KiB"));
171-
assert_eq!(human_readable_bytes(1024 * 420 + 512), (420.5, "KiB"));
172-
assert_eq!(human_readable_bytes(1024 * 1024), (1., "MiB"));
173-
assert_eq!(
174-
human_readable_bytes(1024 * 1024 + 1024 * 256),
175-
(1.25, "MiB")
176-
);
177-
assert_eq!(human_readable_bytes(1024 * 1024 * 1024), (1., "GiB"));
178-
assert_eq!(
179-
human_readable_bytes((1024. * 1024. * 1024. * 1.2345) as u64),
180-
(1.2345, "GiB")
181-
);
182-
assert_eq!(human_readable_bytes(1024 * 1024 * 1024 * 1024), (1., "TiB"));
183-
assert_eq!(
184-
human_readable_bytes(1024 * 1024 * 1024 * 1024 * 1024),
185-
(1., "PiB")
186-
);
186+
t(0, "0B");
187+
t(8, "8B");
188+
t(1000, "1000B");
189+
t(1024, "1KiB");
190+
t(1024 * 420 + 512, "420.5KiB");
191+
t(1024 * 1024, "1MiB");
192+
t(1024 * 1024 + 1024 * 256, "1.25MiB");
193+
t(1024 * 1024 * 1024, "1GiB");
194+
t((1024. * 1024. * 1024. * 1.2345) as u64, "1.2345GiB");
195+
t(1024 * 1024 * 1024 * 1024, "1TiB");
196+
t(1024 * 1024 * 1024 * 1024 * 1024, "1PiB");
197+
t(1024 * 1024 * 1024 * 1024 * 1024 * 1024, "1EiB");
198+
t(u64::MAX, "16EiB");
199+
187200
assert_eq!(
188-
human_readable_bytes(1024 * 1024 * 1024 * 1024 * 1024 * 1024),
189-
(1., "EiB")
201+
&format!("{:.3}", HumanBytes((1024. * 1.23456) as u64)),
202+
"1.234KiB"
190203
);
191-
assert_eq!(human_readable_bytes(u64::MAX), (16., "EiB"));
192204
}
193205
}

tests/testsuite/package.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3424,7 +3424,7 @@ fn verify_packaged_status_line(
34243424
uncompressed_size: u64,
34253425
compressed_size: u64,
34263426
) {
3427-
use cargo::util::human_readable_bytes;
3427+
use cargo::util::HumanBytes;
34283428

34293429
let stderr = String::from_utf8(output.stderr).unwrap();
34303430
let mut packaged_lines = stderr
@@ -3438,12 +3438,9 @@ fn verify_packaged_status_line(
34383438
"Only one `Packaged` status line should appear in stderr"
34393439
);
34403440
let size_info = packaged_line.trim().trim_start_matches("Packaged").trim();
3441-
let uncompressed = human_readable_bytes(uncompressed_size);
3442-
let compressed = human_readable_bytes(compressed_size);
3443-
let expected = format!(
3444-
"{} files, {:.1}{} ({:.1}{} compressed)",
3445-
num_files, uncompressed.0, uncompressed.1, compressed.0, compressed.1
3446-
);
3441+
let uncompressed = HumanBytes(uncompressed_size);
3442+
let compressed = HumanBytes(compressed_size);
3443+
let expected = format!("{num_files} files, {uncompressed:.1} ({compressed:.1} compressed)");
34473444
assert_eq!(size_info, expected);
34483445
}
34493446

tests/testsuite/progress.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ fn always_shows_progress() {
114114
p.cargo("check")
115115
.with_stderr_data(
116116
str![[r#"
117-
[DOWNLOADING] [..] crate
118-
[DOWNLOADED] 3 crates ([..]KB) in [..]s
117+
[DOWNLOADING] [..] crate [..]
118+
[DOWNLOADED] 3 crates ([..]) in [..]s
119119
[BUILDING] [..] [..]/4: [..]
120120
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
121121
...

0 commit comments

Comments
 (0)