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