1use std::path::{Path, PathBuf};
2use std::time::Duration;
3
4pub use self::canonical_url::CanonicalUrl;
5pub use self::context::{ConfigValue, GlobalContext, homedir};
6pub(crate) use self::counter::MetricsCounter;
7pub use self::dependency_queue::DependencyQueue;
8pub use self::diagnostic_server::RustfixDiagnosticServer;
9pub use self::edit_distance::{closest, closest_msg, edit_distance};
10pub use self::errors::CliError;
11pub use self::errors::{CargoResult, CliResult, internal};
12pub use self::flock::{FileLock, Filesystem};
13pub use self::graph::Graph;
14pub use self::hasher::StableHasher;
15pub use self::hex::{hash_u64, short_hash, to_hex};
16pub use self::into_url::IntoUrl;
17pub use self::into_url_with_base::IntoUrlWithBase;
18pub(crate) use self::io::LimitErrorReader;
19pub use self::lockserver::{LockServer, LockServerClient, LockServerStarted};
20pub use self::logger::BuildLogger;
21pub use self::once::OnceExt;
22pub use self::progress::{Progress, ProgressStyle};
23pub use self::queue::Queue;
24pub use self::rustc::Rustc;
25pub use self::semver_ext::{OptVersionReq, VersionExt};
26pub use self::unhashed::Unhashed;
27pub use self::vcs::{FossilRepo, GitRepo, HgRepo, PijulRepo, existing_vcs_repo};
28pub use self::workspace::{
29 add_path_args, path_args, print_available_benches, print_available_binaries,
30 print_available_examples, print_available_packages, print_available_tests,
31};
32
33pub mod auth;
34pub mod cache_lock;
35mod canonical_url;
36pub mod command_prelude;
37pub mod context;
38mod counter;
39pub mod cpu;
40pub mod credential;
41mod dependency_queue;
42pub mod diagnostic_server;
43pub mod edit_distance;
44pub mod errors;
45pub mod flock;
46pub mod frontmatter;
47pub mod graph;
48mod hasher;
49pub mod hex;
50pub mod important_paths;
51pub mod interning;
52pub mod into_url;
53mod into_url_with_base;
54mod io;
55pub mod job;
56mod local_poll_adapter;
57pub use local_poll_adapter::LocalPollAdapter;
58pub mod data_structures;
59mod lockserver;
60pub mod log_message;
61pub mod logger;
62pub mod machine_message;
63pub mod network;
64mod once;
65pub mod open;
66mod progress;
67mod queue;
68pub mod restricted_names;
69pub mod rustc;
70mod semver_eval_ext;
71mod semver_ext;
72pub mod sqlite;
73pub mod time_span;
74pub mod toml;
75pub mod toml_mut;
76mod unhashed;
77mod vcs;
78mod workspace;
79
80pub use cargo_util_terminal::style;
81pub(crate) use futures::executor::block_on;
82pub(crate) use futures::executor::block_on_stream;
83
84pub fn is_rustup() -> bool {
85 #[expect(clippy::disallowed_methods, reason = "consistency with rustup")]
86 std::env::var_os("RUSTUP_HOME").is_some()
87}
88
89pub fn elapsed(duration: Duration) -> String {
90 let secs = duration.as_secs();
91
92 if secs >= 60 {
93 format!("{}m {:02}s", secs / 60, secs % 60)
94 } else {
95 format!("{}.{:02}s", secs, duration.subsec_nanos() / 10_000_000)
96 }
97}
98
99pub struct HumanBytes(pub u64);
101
102impl std::fmt::Display for HumanBytes {
103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 const UNITS: [&str; 7] = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"];
105 let bytes = self.0 as f32;
106 let i = ((bytes.log2() / 10.0) as usize).min(UNITS.len() - 1);
107 let unit = UNITS[i];
108 let size = bytes / 1024_f32.powi(i as i32);
109
110 if i == 0 {
112 return write!(f, "{size}{unit}");
113 }
114
115 let Some(precision) = f.precision() else {
116 return write!(f, "{size}{unit}");
117 };
118 write!(f, "{size:.precision$}{unit}",)
119 }
120}
121
122pub fn indented_lines(text: &str) -> String {
123 text.lines()
124 .map(|line| {
125 if line.is_empty() {
126 String::from("\n")
127 } else {
128 format!(" {}\n", line)
129 }
130 })
131 .collect()
132}
133
134pub fn truncate_with_ellipsis(s: &str, max_width: usize) -> String {
135 let mut chars = s.chars();
139 let mut prefix = (&mut chars).take(max_width - 1).collect::<String>();
140 if chars.next().is_some() {
141 prefix.push('…');
142 }
143 prefix
144}
145
146#[cfg(not(windows))]
147#[inline]
148pub fn try_canonicalize<P: AsRef<Path>>(path: P) -> std::io::Result<PathBuf> {
149 std::fs::canonicalize(&path)
150}
151
152#[cfg(windows)]
153#[inline]
154pub fn try_canonicalize<P: AsRef<Path>>(path: P) -> std::io::Result<PathBuf> {
155 use std::io::Error;
156 use std::io::ErrorKind;
157
158 std::fs::canonicalize(&path).or_else(|_| {
160 if !path.as_ref().try_exists()? {
162 return Err(Error::new(ErrorKind::NotFound, "the path was not found"));
163 }
164 std::path::absolute(&path)
165 })
166}
167
168#[cfg(unix)]
172pub fn get_umask() -> u32 {
173 use std::sync::OnceLock;
174 static UMASK: OnceLock<libc::mode_t> = OnceLock::new();
175 *UMASK.get_or_init(|| unsafe {
180 let umask = libc::umask(0o022);
181 libc::umask(umask);
182 umask
183 }) as u32 }
185
186#[cfg(test)]
187mod test {
188 use super::*;
189
190 #[track_caller]
191 fn t(bytes: u64, expected: &str) {
192 assert_eq!(&HumanBytes(bytes).to_string(), expected);
193 }
194
195 #[test]
196 fn test_human_readable_bytes() {
197 t(0, "0B");
198 t(8, "8B");
199 t(1000, "1000B");
200 t(1024, "1KiB");
201 t(1024 * 420 + 512, "420.5KiB");
202 t(1024 * 1024, "1MiB");
203 t(1024 * 1024 + 1024 * 256, "1.25MiB");
204 t(1024 * 1024 * 1024, "1GiB");
205 t((1024. * 1024. * 1024. * 1.2345) as u64, "1.2345GiB");
206 t(1024 * 1024 * 1024 * 1024, "1TiB");
207 t(1024 * 1024 * 1024 * 1024 * 1024, "1PiB");
208 t(1024 * 1024 * 1024 * 1024 * 1024 * 1024, "1EiB");
209 t(u64::MAX, "16EiB");
210
211 assert_eq!(
212 &format!("{:.3}", HumanBytes((1024. * 1.23456) as u64)),
213 "1.234KiB"
214 );
215 }
216}