]> git.proxmox.com Git - rustc.git/blob - src/tools/cargo/src/cargo/util/mod.rs
New upstream version 1.75.0+dfsg1
[rustc.git] / src / tools / cargo / src / cargo / util / mod.rs
1 use std::fmt;
2 use std::path::{Path, PathBuf};
3 use std::time::Duration;
4
5 pub use self::canonical_url::CanonicalUrl;
6 pub use self::config::{homedir, Config, ConfigValue};
7 pub(crate) use self::counter::MetricsCounter;
8 pub use self::dependency_queue::DependencyQueue;
9 pub use self::diagnostic_server::RustfixDiagnosticServer;
10 pub use self::edit_distance::{closest, closest_msg, edit_distance};
11 pub use self::errors::CliError;
12 pub use self::errors::{internal, CargoResult, CliResult};
13 pub use self::flock::{FileLock, Filesystem};
14 pub use self::graph::Graph;
15 pub use self::hasher::StableHasher;
16 pub use self::hex::{hash_u64, short_hash, to_hex};
17 pub use self::hostname::hostname;
18 pub use self::into_url::IntoUrl;
19 pub use self::into_url_with_base::IntoUrlWithBase;
20 pub(crate) use self::io::LimitErrorReader;
21 pub use self::lockserver::{LockServer, LockServerClient, LockServerStarted};
22 pub use self::progress::{Progress, ProgressStyle};
23 pub use self::queue::Queue;
24 pub use self::restricted_names::validate_package_name;
25 pub use self::rustc::Rustc;
26 pub use self::semver_ext::{OptVersionReq, RustVersion};
27 pub use self::vcs::{existing_vcs_repo, FossilRepo, GitRepo, HgRepo, PijulRepo};
28 pub 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
33 pub mod auth;
34 pub mod cache_lock;
35 mod canonical_url;
36 pub mod command_prelude;
37 pub mod config;
38 mod counter;
39 pub mod cpu;
40 pub mod credential;
41 mod dependency_queue;
42 pub mod diagnostic_server;
43 pub mod edit_distance;
44 pub mod errors;
45 mod flock;
46 pub mod graph;
47 mod hasher;
48 pub mod hex;
49 mod hostname;
50 pub mod important_paths;
51 pub mod interning;
52 pub mod into_url;
53 mod into_url_with_base;
54 mod io;
55 pub mod job;
56 mod lockserver;
57 pub mod machine_message;
58 pub mod network;
59 pub mod profile;
60 mod progress;
61 mod queue;
62 pub mod restricted_names;
63 pub mod rustc;
64 mod semver_ext;
65 pub mod style;
66 pub mod toml;
67 pub mod toml_mut;
68 mod vcs;
69 mod workspace;
70
71 pub fn is_rustup() -> bool {
72 // ALLOWED: `RUSTUP_HOME` should only be read from process env, otherwise
73 // other tools may point to executables from incompatible distributions.
74 #[allow(clippy::disallowed_methods)]
75 std::env::var_os("RUSTUP_HOME").is_some()
76 }
77
78 pub fn elapsed(duration: Duration) -> String {
79 let secs = duration.as_secs();
80
81 if secs >= 60 {
82 format!("{}m {:02}s", secs / 60, secs % 60)
83 } else {
84 format!("{}.{:02}s", secs, duration.subsec_nanos() / 10_000_000)
85 }
86 }
87
88 /// 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])
95 }
96
97 pub fn iter_join_onto<W, I, T>(mut w: W, iter: I, delim: &str) -> fmt::Result
98 where
99 W: fmt::Write,
100 I: IntoIterator<Item = T>,
101 T: std::fmt::Display,
102 {
103 let mut it = iter.into_iter().peekable();
104 while let Some(n) = it.next() {
105 write!(w, "{}", n)?;
106 if it.peek().is_some() {
107 write!(w, "{}", delim)?;
108 }
109 }
110 Ok(())
111 }
112
113 pub fn iter_join<I, T>(iter: I, delim: &str) -> String
114 where
115 I: IntoIterator<Item = T>,
116 T: std::fmt::Display,
117 {
118 let mut s = String::new();
119 let _ = iter_join_onto(&mut s, iter, delim);
120 s
121 }
122
123 pub fn indented_lines(text: &str) -> String {
124 text.lines()
125 .map(|line| {
126 if line.is_empty() {
127 String::from("\n")
128 } else {
129 format!(" {}\n", line)
130 }
131 })
132 .collect()
133 }
134
135 pub fn truncate_with_ellipsis(s: &str, max_width: usize) -> String {
136 // We should truncate at grapheme-boundary and compute character-widths,
137 // yet the dependencies on unicode-segmentation and unicode-width are
138 // not worth it.
139 let mut chars = s.chars();
140 let mut prefix = (&mut chars).take(max_width - 1).collect::<String>();
141 if chars.next().is_some() {
142 prefix.push('…');
143 }
144 prefix
145 }
146
147 #[cfg(not(windows))]
148 #[inline]
149 pub fn try_canonicalize<P: AsRef<Path>>(path: P) -> std::io::Result<PathBuf> {
150 std::fs::canonicalize(&path)
151 }
152
153 #[cfg(windows)]
154 #[inline]
155 pub fn try_canonicalize<P: AsRef<Path>>(path: P) -> std::io::Result<PathBuf> {
156 use std::ffi::OsString;
157 use std::io::Error;
158 use std::os::windows::ffi::{OsStrExt, OsStringExt};
159 use std::{io::ErrorKind, ptr};
160 use windows_sys::Win32::Foundation::{GetLastError, SetLastError};
161 use windows_sys::Win32::Storage::FileSystem::GetFullPathNameW;
162
163 // On Windows `canonicalize` may fail, so we fall back to getting an absolute path.
164 std::fs::canonicalize(&path).or_else(|_| {
165 // Return an error if a file does not exist for better compatibility with `canonicalize`
166 if !path.as_ref().try_exists()? {
167 return Err(Error::new(ErrorKind::NotFound, "the path was not found"));
168 }
169
170 // This code is based on the unstable `std::path::absolute` and could be replaced with it
171 // if it's stabilized.
172
173 let path = path.as_ref().as_os_str();
174 let mut path_u16 = Vec::with_capacity(path.len() + 1);
175 path_u16.extend(path.encode_wide());
176 if path_u16.iter().find(|c| **c == 0).is_some() {
177 return Err(Error::new(
178 ErrorKind::InvalidInput,
179 "strings passed to WinAPI cannot contain NULs",
180 ));
181 }
182 path_u16.push(0);
183
184 loop {
185 unsafe {
186 SetLastError(0);
187 let len =
188 GetFullPathNameW(path_u16.as_ptr(), 0, &mut [] as *mut u16, ptr::null_mut());
189 if len == 0 {
190 let error = GetLastError();
191 if error != 0 {
192 return Err(Error::from_raw_os_error(error as i32));
193 }
194 }
195 let mut result = vec![0u16; len as usize];
196
197 let write_len = GetFullPathNameW(
198 path_u16.as_ptr(),
199 result.len().try_into().unwrap(),
200 result.as_mut_ptr().cast::<u16>(),
201 ptr::null_mut(),
202 );
203 if write_len == 0 {
204 let error = GetLastError();
205 if error != 0 {
206 return Err(Error::from_raw_os_error(error as i32));
207 }
208 }
209
210 if write_len <= len {
211 return Ok(PathBuf::from(OsString::from_wide(
212 &result[0..(write_len as usize)],
213 )));
214 }
215 }
216 }
217 })
218 }
219
220 /// Get the current [`umask`] value.
221 ///
222 /// [`umask`]: https://man7.org/linux/man-pages/man2/umask.2.html
223 #[cfg(unix)]
224 pub fn get_umask() -> u32 {
225 use std::sync::OnceLock;
226 static UMASK: OnceLock<libc::mode_t> = OnceLock::new();
227 // SAFETY: Syscalls are unsafe. Calling `umask` twice is even unsafer for
228 // multithreading program, since it doesn't provide a way to retrieve the
229 // value without modifications. We use a static `OnceLock` here to ensure
230 // it only gets call once during the entire program lifetime.
231 *UMASK.get_or_init(|| unsafe {
232 let umask = libc::umask(0o022);
233 libc::umask(umask);
234 umask
235 }) as u32 // it is u16 on macos
236 }
237
238 #[cfg(test)]
239 mod test {
240 use super::*;
241
242 #[test]
243 fn test_human_readable_bytes() {
244 assert_eq!(human_readable_bytes(0), (0., "B"));
245 assert_eq!(human_readable_bytes(8), (8., "B"));
246 assert_eq!(human_readable_bytes(1000), (1000., "B"));
247 assert_eq!(human_readable_bytes(1024), (1., "KiB"));
248 assert_eq!(human_readable_bytes(1024 * 420 + 512), (420.5, "KiB"));
249 assert_eq!(human_readable_bytes(1024 * 1024), (1., "MiB"));
250 assert_eq!(
251 human_readable_bytes(1024 * 1024 + 1024 * 256),
252 (1.25, "MiB")
253 );
254 assert_eq!(human_readable_bytes(1024 * 1024 * 1024), (1., "GiB"));
255 assert_eq!(
256 human_readable_bytes((1024. * 1024. * 1024. * 3.1415) as u64),
257 (3.1415, "GiB")
258 );
259 assert_eq!(human_readable_bytes(1024 * 1024 * 1024 * 1024), (1., "TiB"));
260 assert_eq!(
261 human_readable_bytes(1024 * 1024 * 1024 * 1024 * 1024),
262 (1., "PiB")
263 );
264 assert_eq!(
265 human_readable_bytes(1024 * 1024 * 1024 * 1024 * 1024 * 1024),
266 (1., "EiB")
267 );
268 assert_eq!(human_readable_bytes(u64::MAX), (16., "EiB"));
269 }
270 }