From 9835e2ae736235810b4ea1c162ca5e65c547e770 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 18 May 2024 04:49:50 +0200 Subject: Merging upstream version 1.71.1+dfsg1. Signed-off-by: Daniel Baumann --- src/tools/cargo/crates/cargo-platform/Cargo.toml | 4 +- src/tools/cargo/crates/cargo-test-macro/Cargo.toml | 1 + .../cargo/crates/cargo-test-support/Cargo.toml | 41 +- .../cargo/crates/cargo-test-support/src/install.rs | 6 +- .../cargo/crates/cargo-test-support/src/lib.rs | 33 +- .../cargo/crates/cargo-test-support/src/publish.rs | 4 + .../crates/cargo-test-support/src/registry.rs | 15 +- src/tools/cargo/crates/cargo-util/Cargo.toml | 28 +- src/tools/cargo/crates/crates-io/Cargo.toml | 14 +- src/tools/cargo/crates/crates-io/lib.rs | 3 +- src/tools/cargo/crates/credential/README.md | 8 - .../cargo-credential-1password/Cargo.toml | 12 - .../cargo-credential-1password/src/main.rs | 314 -------------- .../cargo-credential-gnome-secret/Cargo.toml | 13 - .../cargo-credential-gnome-secret/build.rs | 3 - .../cargo-credential-gnome-secret/src/main.rs | 194 --------- .../cargo-credential-macos-keychain/Cargo.toml | 11 - .../cargo-credential-macos-keychain/src/main.rs | 50 --- .../credential/cargo-credential-wincred/Cargo.toml | 11 - .../cargo-credential-wincred/src/main.rs | 111 ----- .../crates/credential/cargo-credential/Cargo.toml | 9 - .../crates/credential/cargo-credential/README.md | 41 -- .../crates/credential/cargo-credential/src/lib.rs | 86 ---- src/tools/cargo/crates/home/CHANGELOG.md | 11 +- src/tools/cargo/crates/home/Cargo.toml | 4 +- src/tools/cargo/crates/home/src/env.rs | 2 +- src/tools/cargo/crates/home/src/lib.rs | 42 +- src/tools/cargo/crates/mdman/Cargo.lock | 459 --------------------- src/tools/cargo/crates/mdman/Cargo.toml | 17 +- src/tools/cargo/crates/mdman/build-man.sh | 7 - src/tools/cargo/crates/mdman/doc/mdman.md | 2 +- src/tools/cargo/crates/mdman/doc/out/mdman.1 | 8 +- src/tools/cargo/crates/mdman/doc/out/mdman.md | 6 +- src/tools/cargo/crates/mdman/doc/out/mdman.txt | 8 +- src/tools/cargo/crates/resolver-tests/Cargo.toml | 15 +- src/tools/cargo/crates/resolver-tests/src/lib.rs | 4 + src/tools/cargo/crates/semver-check/Cargo.toml | 11 + src/tools/cargo/crates/semver-check/src/main.rs | 293 +++++++++++++ src/tools/cargo/crates/xtask-build-man/Cargo.toml | 7 + src/tools/cargo/crates/xtask-build-man/src/main.rs | 108 +++++ .../cargo/crates/xtask-stale-label/Cargo.toml | 8 + .../cargo/crates/xtask-stale-label/src/main.rs | 91 ++++ .../cargo/crates/xtask-unpublished/Cargo.toml | 12 + .../cargo/crates/xtask-unpublished/src/main.rs | 15 + .../cargo/crates/xtask-unpublished/src/xtask.rs | 200 +++++++++ 45 files changed, 905 insertions(+), 1437 deletions(-) delete mode 100644 src/tools/cargo/crates/credential/README.md delete mode 100644 src/tools/cargo/crates/credential/cargo-credential-1password/Cargo.toml delete mode 100644 src/tools/cargo/crates/credential/cargo-credential-1password/src/main.rs delete mode 100644 src/tools/cargo/crates/credential/cargo-credential-gnome-secret/Cargo.toml delete mode 100644 src/tools/cargo/crates/credential/cargo-credential-gnome-secret/build.rs delete mode 100644 src/tools/cargo/crates/credential/cargo-credential-gnome-secret/src/main.rs delete mode 100644 src/tools/cargo/crates/credential/cargo-credential-macos-keychain/Cargo.toml delete mode 100644 src/tools/cargo/crates/credential/cargo-credential-macos-keychain/src/main.rs delete mode 100644 src/tools/cargo/crates/credential/cargo-credential-wincred/Cargo.toml delete mode 100644 src/tools/cargo/crates/credential/cargo-credential-wincred/src/main.rs delete mode 100644 src/tools/cargo/crates/credential/cargo-credential/Cargo.toml delete mode 100644 src/tools/cargo/crates/credential/cargo-credential/README.md delete mode 100644 src/tools/cargo/crates/credential/cargo-credential/src/lib.rs delete mode 100644 src/tools/cargo/crates/mdman/Cargo.lock delete mode 100755 src/tools/cargo/crates/mdman/build-man.sh create mode 100644 src/tools/cargo/crates/semver-check/Cargo.toml create mode 100644 src/tools/cargo/crates/semver-check/src/main.rs create mode 100644 src/tools/cargo/crates/xtask-build-man/Cargo.toml create mode 100644 src/tools/cargo/crates/xtask-build-man/src/main.rs create mode 100644 src/tools/cargo/crates/xtask-stale-label/Cargo.toml create mode 100644 src/tools/cargo/crates/xtask-stale-label/src/main.rs create mode 100644 src/tools/cargo/crates/xtask-unpublished/Cargo.toml create mode 100644 src/tools/cargo/crates/xtask-unpublished/src/main.rs create mode 100644 src/tools/cargo/crates/xtask-unpublished/src/xtask.rs (limited to 'src/tools/cargo/crates') diff --git a/src/tools/cargo/crates/cargo-platform/Cargo.toml b/src/tools/cargo/crates/cargo-platform/Cargo.toml index a5e51ee5d..423cf491d 100644 --- a/src/tools/cargo/crates/cargo-platform/Cargo.toml +++ b/src/tools/cargo/crates/cargo-platform/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cargo-platform" -version = "0.1.2" +version = "0.1.3" edition = "2021" license = "MIT OR Apache-2.0" homepage = "https://github.com/rust-lang/cargo" @@ -9,4 +9,4 @@ documentation = "https://docs.rs/cargo-platform" description = "Cargo's representation of a target platform." [dependencies] -serde = "1.0.82" +serde.workspace = true diff --git a/src/tools/cargo/crates/cargo-test-macro/Cargo.toml b/src/tools/cargo/crates/cargo-test-macro/Cargo.toml index 04dafc028..e40602ae3 100644 --- a/src/tools/cargo/crates/cargo-test-macro/Cargo.toml +++ b/src/tools/cargo/crates/cargo-test-macro/Cargo.toml @@ -7,6 +7,7 @@ homepage = "https://github.com/rust-lang/cargo" repository = "https://github.com/rust-lang/cargo" documentation = "https://github.com/rust-lang/cargo" description = "Helper proc-macro for Cargo's testsuite." +publish = false [lib] proc-macro = true diff --git a/src/tools/cargo/crates/cargo-test-support/Cargo.toml b/src/tools/cargo/crates/cargo-test-support/Cargo.toml index 91e6e4e34..305c809a8 100644 --- a/src/tools/cargo/crates/cargo-test-support/Cargo.toml +++ b/src/tools/cargo/crates/cargo-test-support/Cargo.toml @@ -3,30 +3,31 @@ name = "cargo-test-support" version = "0.1.0" license = "MIT OR Apache-2.0" edition = "2021" +publish = false [lib] doctest = false [dependencies] -anyhow = "1.0.34" -cargo-test-macro = { path = "../cargo-test-macro" } -cargo-util = { path = "../cargo-util" } -crates-io = { path = "../crates-io" } -filetime = "0.2" -flate2 = { version = "1.0", default-features = false, features = ["zlib"] } -git2 = "0.17.0" -glob = "0.3" -itertools = "0.10.0" -lazy_static = "1.0" -pasetors = { version = "0.6.4", features = ["v3", "paserk", "std", "serde"] } -serde = { version = "1.0.123", features = ["derive"] } -serde_json = "1.0" -snapbox = { version = "0.4.0", features = ["diff", "path"] } -tar = { version = "0.4.38", default-features = false } -termcolor = "1.1.2" -time = { version = "0.3", features = ["parsing", "formatting"]} -toml = "0.7.0" -url = "2.2.2" +anyhow.workspace = true +cargo-test-macro.workspace = true +cargo-util.workspace = true +crates-io.workspace = true +filetime.workspace = true +flate2.workspace = true +git2.workspace = true +glob.workspace = true +itertools.workspace = true +lazy_static.workspace = true +pasetors.workspace = true +serde = { workspace = true, features = ["derive"] } +serde_json.workspace = true +snapbox.workspace = true +tar.workspace = true +termcolor.workspace = true +time.workspace = true +toml.workspace = true +url.workspace = true [target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.45.0", features = ["Win32_Storage_FileSystem"] } +windows-sys = { workspace = true, features = ["Win32_Storage_FileSystem"] } diff --git a/src/tools/cargo/crates/cargo-test-support/src/install.rs b/src/tools/cargo/crates/cargo-test-support/src/install.rs index 478b482d2..02842ef7b 100644 --- a/src/tools/cargo/crates/cargo-test-support/src/install.rs +++ b/src/tools/cargo/crates/cargo-test-support/src/install.rs @@ -4,8 +4,12 @@ use std::path::{Path, PathBuf}; /// Used by `cargo install` tests to assert an executable binary /// has been installed. Example usage: +/// ```no_run +/// use cargo_test_support::install::assert_has_installed_exe; +/// use cargo_test_support::install::cargo_home; /// -/// assert_has_installed_exe(cargo_home(), "foo"); +/// assert_has_installed_exe(cargo_home(), "foo"); +/// ``` #[track_caller] pub fn assert_has_installed_exe>(path: P, name: &'static str) { assert!(check_has_installed_exe(path, name)); diff --git a/src/tools/cargo/crates/cargo-test-support/src/lib.rs b/src/tools/cargo/crates/cargo-test-support/src/lib.rs index 04d6ce9f8..d27aab44f 100644 --- a/src/tools/cargo/crates/cargo-test-support/src/lib.rs +++ b/src/tools/cargo/crates/cargo-test-support/src/lib.rs @@ -59,8 +59,8 @@ pub fn panic_error(what: &str, err: impl Into) -> ! { fn pe(what: &str, err: anyhow::Error) -> ! { let mut result = format!("{}\nerror: {}", what, err); for cause in err.chain().skip(1) { - drop(writeln!(result, "\nCaused by:")); - drop(write!(result, "{}", cause)); + let _ = writeln!(result, "\nCaused by:"); + let _ = write!(result, "{}", cause); } panic!("\n{}", result); } @@ -517,6 +517,29 @@ pub fn cargo_exe() -> PathBuf { snapbox::cmd::cargo_bin("cargo") } +/// A wrapper around `rustc` instead of calling `clippy`. +pub fn wrapped_clippy_driver() -> PathBuf { + let clippy_driver = project() + .at(paths::global_root().join("clippy-driver")) + .file("Cargo.toml", &basic_manifest("clippy-driver", "0.0.1")) + .file( + "src/main.rs", + r#" + fn main() { + let mut args = std::env::args_os(); + let _me = args.next().unwrap(); + let rustc = args.next().unwrap(); + let status = std::process::Command::new(rustc).args(args).status().unwrap(); + std::process::exit(status.code().unwrap_or(1)); + } + "#, + ) + .build(); + clippy_driver.cargo("build").run(); + + clippy_driver.bin("clippy-driver") +} + /// This is the raw output from the process. /// /// This is similar to `std::process::Output`, however the `status` is @@ -677,13 +700,15 @@ impl Execs { /// The substrings are matched as `contains`. Example: /// /// ```no_run - /// execs.with_stderr_line_without( + /// use cargo_test_support::execs; + /// + /// execs().with_stderr_line_without( /// &[ /// "[RUNNING] `rustc --crate-name build_script_build", /// "-C opt-level=3", /// ], /// &["-C debuginfo", "-C incremental"], - /// ) + /// ); /// ``` /// /// This will check that a build line includes `-C opt-level=3` but does diff --git a/src/tools/cargo/crates/cargo-test-support/src/publish.rs b/src/tools/cargo/crates/cargo-test-support/src/publish.rs index 64774bc43..dccc8356d 100644 --- a/src/tools/cargo/crates/cargo-test-support/src/publish.rs +++ b/src/tools/cargo/crates/cargo-test-support/src/publish.rs @@ -165,6 +165,7 @@ pub(crate) fn create_index_line( features: crate::registry::FeatureMap, yanked: bool, links: Option, + rust_version: Option<&str>, v: Option, ) -> String { // This emulates what crates.io does to retain backwards compatibility. @@ -185,6 +186,9 @@ pub(crate) fn create_index_line( if let Some(v) = v { json["v"] = serde_json::json!(v); } + if let Some(rust_version) = rust_version { + json["rust_version"] = serde_json::json!(rust_version); + } json.to_string() } diff --git a/src/tools/cargo/crates/cargo-test-support/src/registry.rs b/src/tools/cargo/crates/cargo-test-support/src/registry.rs index 5faf23540..0cf82cb70 100644 --- a/src/tools/cargo/crates/cargo-test-support/src/registry.rs +++ b/src/tools/cargo/crates/cargo-test-support/src/registry.rs @@ -450,7 +450,10 @@ impl RegistryBuilder { /// `VendorPackage` which implements directory sources. /// /// # Example -/// ``` +/// ```no_run +/// use cargo_test_support::registry::Package; +/// use cargo_test_support::project; +/// /// // Publish package "a" depending on "b". /// Package::new("a", "1.0.0") /// .dep("b", "1.0.0") @@ -1144,6 +1147,7 @@ fn save_new_crate( false, new_crate.links, None, + None, ); write_to_index(registry_path, &new_crate.name, line, false); @@ -1240,7 +1244,7 @@ impl Package { } /// Adds a normal dependency. Example: - /// ``` + /// ```toml /// [dependencies] /// foo = {version = "1.0"} /// ``` @@ -1249,7 +1253,7 @@ impl Package { } /// Adds a dependency with the given feature. Example: - /// ``` + /// ```toml /// [dependencies] /// foo = {version = "1.0", "features": ["feat1", "feat2"]} /// ``` @@ -1272,7 +1276,7 @@ impl Package { } /// Adds a dev-dependency. Example: - /// ``` + /// ```toml /// [dev-dependencies] /// foo = {version = "1.0"} /// ``` @@ -1281,7 +1285,7 @@ impl Package { } /// Adds a build-dependency. Example: - /// ``` + /// ```toml /// [build-dependencies] /// foo = {version = "1.0"} /// ``` @@ -1400,6 +1404,7 @@ impl Package { self.features.clone(), self.yanked, self.links.clone(), + self.rust_version.as_deref(), self.v, ); diff --git a/src/tools/cargo/crates/cargo-util/Cargo.toml b/src/tools/cargo/crates/cargo-util/Cargo.toml index 7427ceb1a..f01705fca 100644 --- a/src/tools/cargo/crates/cargo-util/Cargo.toml +++ b/src/tools/cargo/crates/cargo-util/Cargo.toml @@ -8,21 +8,21 @@ repository = "https://github.com/rust-lang/cargo" description = "Miscellaneous support code used by Cargo." [dependencies] -anyhow = "1.0.34" -sha2 = "0.10.6" -filetime = "0.2.9" -hex = "0.4.2" -jobserver = "0.1.26" -libc = "0.2.88" -log = "0.4.6" -same-file = "1.0.6" -shell-escape = "0.1.4" -tempfile = "3.1.0" -walkdir = "2.3.1" +anyhow.workspace = true +sha2.workspace = true +filetime.workspace = true +hex.workspace = true +jobserver.workspace = true +libc.workspace = true +log.workspace = true +same-file.workspace = true +shell-escape.workspace = true +tempfile.workspace = true +walkdir.workspace = true [target.'cfg(target_os = "macos")'.dependencies] -core-foundation = { version = "0.9.0", features = ["mac_os_10_7_support"] } +core-foundation.workspace = true [target.'cfg(windows)'.dependencies] -miow = "0.5.0" -windows-sys = { version = "0.45.0", features = ["Win32_Storage_FileSystem", "Win32_Foundation", "Win32_System_Console"] } +miow.workspace = true +windows-sys = { workspace = true, features = ["Win32_Storage_FileSystem", "Win32_Foundation", "Win32_System_Console"] } diff --git a/src/tools/cargo/crates/crates-io/Cargo.toml b/src/tools/cargo/crates/crates-io/Cargo.toml index 004e2daff..034c2fca5 100644 --- a/src/tools/cargo/crates/crates-io/Cargo.toml +++ b/src/tools/cargo/crates/crates-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "crates-io" -version = "0.36.0" +version = "0.37.0" edition = "2021" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/cargo" @@ -13,9 +13,9 @@ name = "crates_io" path = "lib.rs" [dependencies] -anyhow = "1.0.34" -curl = "0.4" -percent-encoding = "2.0" -serde = { version = "1.0", features = ['derive'] } -serde_json = "1.0" -url = "2.0" +anyhow.workspace = true +curl.workspace = true +percent-encoding.workspace = true +serde = { workspace = true, features = ["derive"] } +serde_json.workspace = true +url.workspace = true diff --git a/src/tools/cargo/crates/crates-io/lib.rs b/src/tools/cargo/crates/crates-io/lib.rs index e0197568a..243808098 100644 --- a/src/tools/cargo/crates/crates-io/lib.rs +++ b/src/tools/cargo/crates/crates-io/lib.rs @@ -57,6 +57,7 @@ pub struct NewCrate { pub repository: Option, pub badges: BTreeMap>, pub links: Option, + pub rust_version: Option, } #[derive(Serialize, Deserialize)] @@ -199,7 +200,7 @@ impl Registry { /// let mut handle = Easy::new(); /// // If connecting to crates.io, a user-agent is required. /// handle.useragent("my_crawler (example.com/info)"); - /// let mut reg = Registry::new_handle(String::from("https://crates.io"), None, handle); + /// let mut reg = Registry::new_handle(String::from("https://crates.io"), None, handle, true); /// ``` pub fn new_handle( host: String, diff --git a/src/tools/cargo/crates/credential/README.md b/src/tools/cargo/crates/credential/README.md deleted file mode 100644 index 168cc71c3..000000000 --- a/src/tools/cargo/crates/credential/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Cargo Credential Packages - -This directory contains Cargo packages for handling storage of tokens in a -secure manner. - -`cargo-credential` is a generic library to assist writing a credential -process. The other directories contain implementations that integrate with -specific credential systems. diff --git a/src/tools/cargo/crates/credential/cargo-credential-1password/Cargo.toml b/src/tools/cargo/crates/credential/cargo-credential-1password/Cargo.toml deleted file mode 100644 index 093fde8e5..000000000 --- a/src/tools/cargo/crates/credential/cargo-credential-1password/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "cargo-credential-1password" -version = "0.2.0" -edition = "2021" -license = "MIT OR Apache-2.0" -repository = "https://github.com/rust-lang/cargo" -description = "A Cargo credential process that stores tokens in a 1password vault." - -[dependencies] -cargo-credential = { version = "0.2.0", path = "../cargo-credential" } -serde = { version = "1.0.117", features = ["derive"] } -serde_json = "1.0.59" diff --git a/src/tools/cargo/crates/credential/cargo-credential-1password/src/main.rs b/src/tools/cargo/crates/credential/cargo-credential-1password/src/main.rs deleted file mode 100644 index 4f512b717..000000000 --- a/src/tools/cargo/crates/credential/cargo-credential-1password/src/main.rs +++ /dev/null @@ -1,314 +0,0 @@ -//! Cargo registry 1password credential process. - -use cargo_credential::{Credential, Error}; -use serde::Deserialize; -use std::io::Read; -use std::process::{Command, Stdio}; - -const CARGO_TAG: &str = "cargo-registry"; - -/// Implementation of 1password keychain access for Cargo registries. -struct OnePasswordKeychain { - account: Option, - vault: Option, -} - -/// 1password Login item type, used for the JSON output of `op item get`. -#[derive(Deserialize)] -struct Login { - fields: Vec, -} - -#[derive(Deserialize)] -struct Field { - id: String, - value: Option, -} - -/// 1password item from `op items list`. -#[derive(Deserialize)] -struct ListItem { - id: String, - urls: Vec, -} - -#[derive(Deserialize)] -struct Url { - href: String, -} - -impl OnePasswordKeychain { - fn new() -> Result { - let mut args = std::env::args().skip(1); - let mut action = false; - let mut account = None; - let mut vault = None; - while let Some(arg) = args.next() { - match arg.as_str() { - "--account" => { - account = Some(args.next().ok_or("--account needs an arg")?); - } - "--vault" => { - vault = Some(args.next().ok_or("--vault needs an arg")?); - } - s if s.starts_with('-') => { - return Err(format!("unknown option {}", s).into()); - } - _ => { - if action { - return Err("too many arguments".into()); - } else { - action = true; - } - } - } - } - Ok(OnePasswordKeychain { account, vault }) - } - - fn signin(&self) -> Result, Error> { - // If there are any session env vars, we'll assume that this is the - // correct account, and that the user knows what they are doing. - if std::env::vars().any(|(name, _)| name.starts_with("OP_SESSION_")) { - return Ok(None); - } - let mut cmd = Command::new("op"); - cmd.args(&["signin", "--raw"]); - cmd.stdout(Stdio::piped()); - self.with_tty(&mut cmd)?; - let mut child = cmd - .spawn() - .map_err(|e| format!("failed to spawn `op`: {}", e))?; - let mut buffer = String::new(); - child - .stdout - .as_mut() - .unwrap() - .read_to_string(&mut buffer) - .map_err(|e| format!("failed to get session from `op`: {}", e))?; - if let Some(end) = buffer.find('\n') { - buffer.truncate(end); - } - let status = child - .wait() - .map_err(|e| format!("failed to wait for `op`: {}", e))?; - if !status.success() { - return Err(format!("failed to run `op signin`: {}", status).into()); - } - if buffer.is_empty() { - // When using CLI integration, `op signin` returns no output, - // so there is no need to set the session. - return Ok(None); - } - Ok(Some(buffer)) - } - - fn make_cmd(&self, session: &Option, args: &[&str]) -> Command { - let mut cmd = Command::new("op"); - cmd.args(args); - if let Some(account) = &self.account { - cmd.arg("--account"); - cmd.arg(account); - } - if let Some(vault) = &self.vault { - cmd.arg("--vault"); - cmd.arg(vault); - } - if let Some(session) = session { - cmd.arg("--session"); - cmd.arg(session); - } - cmd - } - - fn with_tty(&self, cmd: &mut Command) -> Result<(), Error> { - #[cfg(unix)] - const IN_DEVICE: &str = "/dev/tty"; - #[cfg(windows)] - const IN_DEVICE: &str = "CONIN$"; - let stdin = std::fs::OpenOptions::new() - .read(true) - .write(true) - .open(IN_DEVICE)?; - cmd.stdin(stdin); - Ok(()) - } - - fn run_cmd(&self, mut cmd: Command) -> Result { - cmd.stdout(Stdio::piped()); - let mut child = cmd - .spawn() - .map_err(|e| format!("failed to spawn `op`: {}", e))?; - let mut buffer = String::new(); - child - .stdout - .as_mut() - .unwrap() - .read_to_string(&mut buffer) - .map_err(|e| format!("failed to read `op` output: {}", e))?; - let status = child - .wait() - .map_err(|e| format!("failed to wait for `op`: {}", e))?; - if !status.success() { - return Err(format!("`op` command exit error: {}", status).into()); - } - Ok(buffer) - } - - fn search(&self, session: &Option, index_url: &str) -> Result, Error> { - let cmd = self.make_cmd( - session, - &[ - "items", - "list", - "--categories", - "Login", - "--tags", - CARGO_TAG, - "--format", - "json", - ], - ); - let buffer = self.run_cmd(cmd)?; - let items: Vec = serde_json::from_str(&buffer) - .map_err(|e| format!("failed to deserialize JSON from 1password list: {}", e))?; - let mut matches = items - .into_iter() - .filter(|item| item.urls.iter().any(|url| url.href == index_url)); - match matches.next() { - Some(login) => { - // Should this maybe just sort on `updatedAt` and return the newest one? - if matches.next().is_some() { - return Err(format!( - "too many 1password logins match registry `{}`, \ - consider deleting the excess entries", - index_url - ) - .into()); - } - Ok(Some(login.id)) - } - None => Ok(None), - } - } - - fn modify( - &self, - session: &Option, - id: &str, - token: &str, - _name: Option<&str>, - ) -> Result<(), Error> { - let cmd = self.make_cmd( - session, - &["item", "edit", id, &format!("password={}", token)], - ); - self.run_cmd(cmd)?; - Ok(()) - } - - fn create( - &self, - session: &Option, - index_url: &str, - token: &str, - name: Option<&str>, - ) -> Result<(), Error> { - let title = match name { - Some(name) => format!("Cargo registry token for {}", name), - None => "Cargo registry token".to_string(), - }; - let mut cmd = self.make_cmd( - session, - &[ - "item", - "create", - "--category", - "Login", - &format!("password={}", token), - &format!("url={}", index_url), - "--title", - &title, - "--tags", - CARGO_TAG, - ], - ); - // For unknown reasons, `op item create` seems to not be happy if - // stdin is not a tty. Otherwise it returns with a 0 exit code without - // doing anything. - self.with_tty(&mut cmd)?; - self.run_cmd(cmd)?; - Ok(()) - } - - fn get_token(&self, session: &Option, id: &str) -> Result { - let cmd = self.make_cmd(session, &["item", "get", "--format=json", id]); - let buffer = self.run_cmd(cmd)?; - let item: Login = serde_json::from_str(&buffer) - .map_err(|e| format!("failed to deserialize JSON from 1password get: {}", e))?; - let password = item.fields.into_iter().find(|item| item.id == "password"); - match password { - Some(password) => password - .value - .ok_or_else(|| format!("missing password value for entry").into()), - None => Err("could not find password field".into()), - } - } - - fn delete(&self, session: &Option, id: &str) -> Result<(), Error> { - let cmd = self.make_cmd(session, &["item", "delete", id]); - self.run_cmd(cmd)?; - Ok(()) - } -} - -impl Credential for OnePasswordKeychain { - fn name(&self) -> &'static str { - env!("CARGO_PKG_NAME") - } - - fn get(&self, index_url: &str) -> Result { - let session = self.signin()?; - if let Some(id) = self.search(&session, index_url)? { - self.get_token(&session, &id) - } else { - return Err(format!( - "no 1password entry found for registry `{}`, try `cargo login` to add a token", - index_url - ) - .into()); - } - } - - fn store(&self, index_url: &str, token: &str, name: Option<&str>) -> Result<(), Error> { - let session = self.signin()?; - // Check if an item already exists. - if let Some(id) = self.search(&session, index_url)? { - self.modify(&session, &id, token, name) - } else { - self.create(&session, index_url, token, name) - } - } - - fn erase(&self, index_url: &str) -> Result<(), Error> { - let session = self.signin()?; - // Check if an item already exists. - if let Some(id) = self.search(&session, index_url)? { - self.delete(&session, &id)?; - } else { - eprintln!("not currently logged in to `{}`", index_url); - } - Ok(()) - } -} - -fn main() { - let op = match OnePasswordKeychain::new() { - Ok(op) => op, - Err(e) => { - eprintln!("error: {}", e); - std::process::exit(1); - } - }; - cargo_credential::main(op); -} diff --git a/src/tools/cargo/crates/credential/cargo-credential-gnome-secret/Cargo.toml b/src/tools/cargo/crates/credential/cargo-credential-gnome-secret/Cargo.toml deleted file mode 100644 index 12e25cfb6..000000000 --- a/src/tools/cargo/crates/credential/cargo-credential-gnome-secret/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "cargo-credential-gnome-secret" -version = "0.2.0" -edition = "2021" -license = "MIT OR Apache-2.0" -repository = "https://github.com/rust-lang/cargo" -description = "A Cargo credential process that stores tokens with GNOME libsecret." - -[dependencies] -cargo-credential = { version = "0.2.0", path = "../cargo-credential" } - -[build-dependencies] -pkg-config = "0.3.19" diff --git a/src/tools/cargo/crates/credential/cargo-credential-gnome-secret/build.rs b/src/tools/cargo/crates/credential/cargo-credential-gnome-secret/build.rs deleted file mode 100644 index 9283535af..000000000 --- a/src/tools/cargo/crates/credential/cargo-credential-gnome-secret/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - pkg_config::probe_library("libsecret-1").unwrap(); -} diff --git a/src/tools/cargo/crates/credential/cargo-credential-gnome-secret/src/main.rs b/src/tools/cargo/crates/credential/cargo-credential-gnome-secret/src/main.rs deleted file mode 100644 index 40972b05d..000000000 --- a/src/tools/cargo/crates/credential/cargo-credential-gnome-secret/src/main.rs +++ /dev/null @@ -1,194 +0,0 @@ -//! Cargo registry gnome libsecret credential process. - -use cargo_credential::{Credential, Error}; -use std::ffi::{CStr, CString}; -use std::os::raw::{c_char, c_int}; -use std::ptr::{null, null_mut}; - -#[allow(non_camel_case_types)] -type gchar = c_char; - -#[allow(non_camel_case_types)] -type gboolean = c_int; - -type GQuark = u32; - -#[repr(C)] -struct GError { - domain: GQuark, - code: c_int, - message: *mut gchar, -} - -#[repr(C)] -struct GCancellable { - _private: [u8; 0], -} - -#[repr(C)] -struct SecretSchema { - name: *const gchar, - flags: SecretSchemaFlags, - attributes: [SecretSchemaAttribute; 32], -} - -#[repr(C)] -#[derive(Copy, Clone)] -struct SecretSchemaAttribute { - name: *const gchar, - attr_type: SecretSchemaAttributeType, -} - -#[repr(C)] -enum SecretSchemaFlags { - None = 0, -} - -#[repr(C)] -#[derive(Copy, Clone)] -enum SecretSchemaAttributeType { - String = 0, -} - -extern "C" { - fn secret_password_store_sync( - schema: *const SecretSchema, - collection: *const gchar, - label: *const gchar, - password: *const gchar, - cancellable: *mut GCancellable, - error: *mut *mut GError, - ... - ) -> gboolean; - fn secret_password_clear_sync( - schema: *const SecretSchema, - cancellable: *mut GCancellable, - error: *mut *mut GError, - ... - ) -> gboolean; - fn secret_password_lookup_sync( - schema: *const SecretSchema, - cancellable: *mut GCancellable, - error: *mut *mut GError, - ... - ) -> *mut gchar; -} - -struct GnomeSecret; - -fn label(index_url: &str) -> CString { - CString::new(format!("cargo-registry:{}", index_url)).unwrap() -} - -fn schema() -> SecretSchema { - let mut attributes = [SecretSchemaAttribute { - name: null(), - attr_type: SecretSchemaAttributeType::String, - }; 32]; - attributes[0] = SecretSchemaAttribute { - name: b"url\0".as_ptr() as *const gchar, - attr_type: SecretSchemaAttributeType::String, - }; - SecretSchema { - name: b"org.rust-lang.cargo.registry\0".as_ptr() as *const gchar, - flags: SecretSchemaFlags::None, - attributes, - } -} - -impl Credential for GnomeSecret { - fn name(&self) -> &'static str { - env!("CARGO_PKG_NAME") - } - - fn get(&self, index_url: &str) -> Result { - let mut error: *mut GError = null_mut(); - let attr_url = CString::new("url").unwrap(); - let index_url_c = CString::new(index_url).unwrap(); - let schema = schema(); - unsafe { - let token_c = secret_password_lookup_sync( - &schema, - null_mut(), - &mut error, - attr_url.as_ptr(), - index_url_c.as_ptr(), - null() as *const gchar, - ); - if !error.is_null() { - return Err(format!( - "failed to get token: {}", - CStr::from_ptr((*error).message).to_str()? - ) - .into()); - } - if token_c.is_null() { - return Err(format!("cannot find token for {}", index_url).into()); - } - let token = CStr::from_ptr(token_c) - .to_str() - .map_err(|e| format!("expected utf8 token: {}", e))? - .to_string(); - Ok(token) - } - } - - fn store(&self, index_url: &str, token: &str, name: Option<&str>) -> Result<(), Error> { - let label = label(name.unwrap_or(index_url)); - let token = CString::new(token).unwrap(); - let mut error: *mut GError = null_mut(); - let attr_url = CString::new("url").unwrap(); - let index_url_c = CString::new(index_url).unwrap(); - let schema = schema(); - unsafe { - secret_password_store_sync( - &schema, - b"default\0".as_ptr() as *const gchar, - label.as_ptr(), - token.as_ptr(), - null_mut(), - &mut error, - attr_url.as_ptr(), - index_url_c.as_ptr(), - null() as *const gchar, - ); - if !error.is_null() { - return Err(format!( - "failed to store token: {}", - CStr::from_ptr((*error).message).to_str()? - ) - .into()); - } - } - Ok(()) - } - - fn erase(&self, index_url: &str) -> Result<(), Error> { - let schema = schema(); - let mut error: *mut GError = null_mut(); - let attr_url = CString::new("url").unwrap(); - let index_url_c = CString::new(index_url).unwrap(); - unsafe { - secret_password_clear_sync( - &schema, - null_mut(), - &mut error, - attr_url.as_ptr(), - index_url_c.as_ptr(), - null() as *const gchar, - ); - if !error.is_null() { - return Err(format!( - "failed to erase token: {}", - CStr::from_ptr((*error).message).to_str()? - ) - .into()); - } - } - Ok(()) - } -} - -fn main() { - cargo_credential::main(GnomeSecret); -} diff --git a/src/tools/cargo/crates/credential/cargo-credential-macos-keychain/Cargo.toml b/src/tools/cargo/crates/credential/cargo-credential-macos-keychain/Cargo.toml deleted file mode 100644 index c2c22a425..000000000 --- a/src/tools/cargo/crates/credential/cargo-credential-macos-keychain/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "cargo-credential-macos-keychain" -version = "0.2.0" -edition = "2021" -license = "MIT OR Apache-2.0" -repository = "https://github.com/rust-lang/cargo" -description = "A Cargo credential process that stores tokens in a macOS keychain." - -[dependencies] -cargo-credential = { version = "0.2.0", path = "../cargo-credential" } -security-framework = "2.0.0" diff --git a/src/tools/cargo/crates/credential/cargo-credential-macos-keychain/src/main.rs b/src/tools/cargo/crates/credential/cargo-credential-macos-keychain/src/main.rs deleted file mode 100644 index 3fef3f92a..000000000 --- a/src/tools/cargo/crates/credential/cargo-credential-macos-keychain/src/main.rs +++ /dev/null @@ -1,50 +0,0 @@ -//! Cargo registry macos keychain credential process. - -use cargo_credential::{Credential, Error}; -use security_framework::os::macos::keychain::SecKeychain; - -struct MacKeychain; - -/// The account name is not used. -const ACCOUNT: &'static str = ""; - -fn registry(registry_name: &str) -> String { - format!("cargo-registry:{}", registry_name) -} - -impl Credential for MacKeychain { - fn name(&self) -> &'static str { - env!("CARGO_PKG_NAME") - } - - fn get(&self, index_url: &str) -> Result { - let keychain = SecKeychain::default().unwrap(); - let service_name = registry(index_url); - let (pass, _item) = keychain.find_generic_password(&service_name, ACCOUNT)?; - String::from_utf8(pass.as_ref().to_vec()) - .map_err(|_| "failed to convert token to UTF8".into()) - } - - fn store(&self, index_url: &str, token: &str, name: Option<&str>) -> Result<(), Error> { - let keychain = SecKeychain::default().unwrap(); - let service_name = registry(name.unwrap_or(index_url)); - if let Ok((_pass, mut item)) = keychain.find_generic_password(&service_name, ACCOUNT) { - item.set_password(token.as_bytes())?; - } else { - keychain.add_generic_password(&service_name, ACCOUNT, token.as_bytes())?; - } - Ok(()) - } - - fn erase(&self, index_url: &str) -> Result<(), Error> { - let keychain = SecKeychain::default().unwrap(); - let service_name = registry(index_url); - let (_pass, item) = keychain.find_generic_password(&service_name, ACCOUNT)?; - item.delete(); - Ok(()) - } -} - -fn main() { - cargo_credential::main(MacKeychain); -} diff --git a/src/tools/cargo/crates/credential/cargo-credential-wincred/Cargo.toml b/src/tools/cargo/crates/credential/cargo-credential-wincred/Cargo.toml deleted file mode 100644 index 83c38e80d..000000000 --- a/src/tools/cargo/crates/credential/cargo-credential-wincred/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "cargo-credential-wincred" -version = "0.2.0" -edition = "2021" -license = "MIT OR Apache-2.0" -repository = "https://github.com/rust-lang/cargo" -description = "A Cargo credential process that stores tokens with Windows Credential Manager." - -[dependencies] -cargo-credential = { version = "0.2.0", path = "../cargo-credential" } -windows-sys = { version = "0.45", features = ["Win32_Foundation", "Win32_Security_Credentials"] } diff --git a/src/tools/cargo/crates/credential/cargo-credential-wincred/src/main.rs b/src/tools/cargo/crates/credential/cargo-credential-wincred/src/main.rs deleted file mode 100644 index 8ae48f348..000000000 --- a/src/tools/cargo/crates/credential/cargo-credential-wincred/src/main.rs +++ /dev/null @@ -1,111 +0,0 @@ -//! Cargo registry windows credential process. - -use cargo_credential::{Credential, Error}; -use std::ffi::OsStr; -use std::os::windows::ffi::OsStrExt; - -use windows_sys::core::PWSTR; -use windows_sys::Win32::Foundation::ERROR_NOT_FOUND; -use windows_sys::Win32::Foundation::FILETIME; -use windows_sys::Win32::Foundation::TRUE; -use windows_sys::Win32::Security::Credentials::CredDeleteW; -use windows_sys::Win32::Security::Credentials::CredReadW; -use windows_sys::Win32::Security::Credentials::CredWriteW; -use windows_sys::Win32::Security::Credentials::CREDENTIALW; -use windows_sys::Win32::Security::Credentials::CRED_PERSIST_LOCAL_MACHINE; -use windows_sys::Win32::Security::Credentials::CRED_TYPE_GENERIC; - -struct WindowsCredential; - -/// Converts a string to a nul-terminated wide UTF-16 byte sequence. -fn wstr(s: &str) -> Vec { - let mut wide: Vec = OsStr::new(s).encode_wide().collect(); - if wide.iter().any(|b| *b == 0) { - panic!("nul byte in wide string"); - } - wide.push(0); - wide -} - -fn target_name(registry_name: &str) -> Vec { - wstr(&format!("cargo-registry:{}", registry_name)) -} - -impl Credential for WindowsCredential { - fn name(&self) -> &'static str { - env!("CARGO_PKG_NAME") - } - - fn get(&self, index_url: &str) -> Result { - let target_name = target_name(index_url); - let p_credential: *mut CREDENTIALW = std::ptr::null_mut() as *mut _; - unsafe { - if CredReadW( - target_name.as_ptr(), - CRED_TYPE_GENERIC, - 0, - p_credential as *mut _ as *mut _, - ) != TRUE - { - return Err( - format!("failed to fetch token: {}", std::io::Error::last_os_error()).into(), - ); - } - let bytes = std::slice::from_raw_parts( - (*p_credential).CredentialBlob, - (*p_credential).CredentialBlobSize as usize, - ); - String::from_utf8(bytes.to_vec()).map_err(|_| "failed to convert token to UTF8".into()) - } - } - - fn store(&self, index_url: &str, token: &str, name: Option<&str>) -> Result<(), Error> { - let token = token.as_bytes(); - let target_name = target_name(index_url); - let comment = match name { - Some(name) => wstr(&format!("Cargo registry token for {}", name)), - None => wstr("Cargo registry token"), - }; - let mut credential = CREDENTIALW { - Flags: 0, - Type: CRED_TYPE_GENERIC, - TargetName: target_name.as_ptr() as PWSTR, - Comment: comment.as_ptr() as PWSTR, - LastWritten: FILETIME { - dwLowDateTime: 0, - dwHighDateTime: 0, - }, - CredentialBlobSize: token.len() as u32, - CredentialBlob: token.as_ptr() as *mut u8, - Persist: CRED_PERSIST_LOCAL_MACHINE, - AttributeCount: 0, - Attributes: std::ptr::null_mut(), - TargetAlias: std::ptr::null_mut(), - UserName: std::ptr::null_mut(), - }; - let result = unsafe { CredWriteW(&mut credential, 0) }; - if result != TRUE { - let err = std::io::Error::last_os_error(); - return Err(format!("failed to store token: {}", err).into()); - } - Ok(()) - } - - fn erase(&self, index_url: &str) -> Result<(), Error> { - let target_name = target_name(index_url); - let result = unsafe { CredDeleteW(target_name.as_ptr(), CRED_TYPE_GENERIC, 0) }; - if result != TRUE { - let err = std::io::Error::last_os_error(); - if err.raw_os_error() == Some(ERROR_NOT_FOUND as i32) { - eprintln!("not currently logged in to `{}`", index_url); - return Ok(()); - } - return Err(format!("failed to remove token: {}", err).into()); - } - Ok(()) - } -} - -fn main() { - cargo_credential::main(WindowsCredential); -} diff --git a/src/tools/cargo/crates/credential/cargo-credential/Cargo.toml b/src/tools/cargo/crates/credential/cargo-credential/Cargo.toml deleted file mode 100644 index 2addaf5af..000000000 --- a/src/tools/cargo/crates/credential/cargo-credential/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "cargo-credential" -version = "0.2.0" -edition = "2021" -license = "MIT OR Apache-2.0" -repository = "https://github.com/rust-lang/cargo" -description = "A library to assist writing Cargo credential helpers." - -[dependencies] diff --git a/src/tools/cargo/crates/credential/cargo-credential/README.md b/src/tools/cargo/crates/credential/cargo-credential/README.md deleted file mode 100644 index 1f75e598a..000000000 --- a/src/tools/cargo/crates/credential/cargo-credential/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# cargo-credential - -This package is a library to assist writing a Cargo credential helper, which -provides an interface to store tokens for authorizing access to a registry -such as https://crates.io/. - -Documentation about credential processes may be found at -https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process - -Example implementations may be found at -https://github.com/rust-lang/cargo/tree/master/crates/credential - -## Usage - -Create a Cargo project with this as a dependency: - -```toml -# Add this to your Cargo.toml: - -[dependencies] -cargo-credential = "0.1" -``` - -And then include a `main.rs` binary which implements the `Credential` trait, and calls -the `main` function which will call the appropriate method of the trait: - -```rust -// src/main.rs - -use cargo_credential::{Credential, Error}; - -struct MyCredential; - -impl Credential for MyCredential { - /// implement trait methods here... -} - -fn main() { - cargo_credential::main(MyCredential); -} -``` diff --git a/src/tools/cargo/crates/credential/cargo-credential/src/lib.rs b/src/tools/cargo/crates/credential/cargo-credential/src/lib.rs deleted file mode 100644 index 3baf42d77..000000000 --- a/src/tools/cargo/crates/credential/cargo-credential/src/lib.rs +++ /dev/null @@ -1,86 +0,0 @@ -//! Helper library for writing Cargo credential processes. -//! -//! A credential process should have a `struct` that implements the `Credential` trait. -//! The `main` function should be called with an instance of that struct, such as: -//! -//! ```rust,ignore -//! fn main() { -//! cargo_credential::main(MyCredential); -//! } -//! ``` -//! -//! This will determine the action to perform (get/store/erase) by looking at -//! the CLI arguments for the first argument that does not start with `-`. It -//! will then call the corresponding method of the trait to perform the -//! requested action. - -pub type Error = Box; - -pub trait Credential { - /// Returns the name of this credential process. - fn name(&self) -> &'static str; - - /// Retrieves a token for the given registry. - fn get(&self, index_url: &str) -> Result; - - /// Stores the given token for the given registry. - fn store(&self, index_url: &str, token: &str, name: Option<&str>) -> Result<(), Error>; - - /// Removes the token for the given registry. - /// - /// If the user is not logged in, this should print a message to stderr if - /// possible indicating that the user is not currently logged in, and - /// return `Ok`. - fn erase(&self, index_url: &str) -> Result<(), Error>; -} - -/// Runs the credential interaction by processing the command-line and -/// environment variables. -pub fn main(credential: impl Credential) { - let name = credential.name(); - if let Err(e) = doit(credential) { - eprintln!("{} error: {}", name, e); - std::process::exit(1); - } -} - -fn env(name: &str) -> Result { - std::env::var(name).map_err(|_| format!("environment variable `{}` is not set", name).into()) -} - -fn doit(credential: impl Credential) -> Result<(), Error> { - let which = std::env::args() - .skip(1) - .skip_while(|arg| arg.starts_with('-')) - .next() - .ok_or_else(|| "first argument must be the {action}")?; - let index_url = env("CARGO_REGISTRY_INDEX_URL")?; - let name = std::env::var("CARGO_REGISTRY_NAME_OPT").ok(); - let result = match which.as_ref() { - "get" => credential.get(&index_url).and_then(|token| { - println!("{}", token); - Ok(()) - }), - "store" => { - read_token().and_then(|token| credential.store(&index_url, &token, name.as_deref())) - } - "erase" => credential.erase(&index_url), - _ => { - return Err(format!( - "unexpected command-line argument `{}`, expected get/store/erase", - which - ) - .into()) - } - }; - result.map_err(|e| format!("failed to `{}` token: {}", which, e).into()) -} - -fn read_token() -> Result { - let mut buffer = String::new(); - std::io::stdin().read_line(&mut buffer)?; - if buffer.ends_with('\n') { - buffer.pop(); - } - Ok(buffer) -} diff --git a/src/tools/cargo/crates/home/CHANGELOG.md b/src/tools/cargo/crates/home/CHANGELOG.md index 7674667c9..58f960cc3 100644 --- a/src/tools/cargo/crates/home/CHANGELOG.md +++ b/src/tools/cargo/crates/home/CHANGELOG.md @@ -4,7 +4,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - +## 0.5.6 +- Fixed & enhanced documentation. + [#12047](https://github.com/rust-lang/cargo/pull/12047) + +## 0.5.5 - 2023-04-25 +- The `home` crate has migrated to the repository. + [#11359](https://github.com/rust-lang/cargo/pull/11359) +- Replaced the winapi dependency with windows-sys. + [#11656](https://github.com/rust-lang/cargo/pull/11656) ## [0.5.4] - 2022-10-10 - Add `_with_env` variants of functions to support in-process threaded tests for @@ -38,7 +46,6 @@ Use Rust 1.36.0 as minimum Rust version. ### Removed - Remove support for `multirust` folder used in old version of `rustup`. -[Unreleased]: https://github.com/brson/home/compare/v0.5.4...HEAD [0.5.4]: https://github.com/brson/home/compare/v0.5.3...v0.5.4 [0.5.3]: https://github.com/brson/home/compare/v0.5.2...v0.5.3 [0.5.2]: https://github.com/brson/home/compare/v0.5.1...v0.5.2 diff --git a/src/tools/cargo/crates/home/Cargo.toml b/src/tools/cargo/crates/home/Cargo.toml index 2c5b92bcb..6c65ecc18 100644 --- a/src/tools/cargo/crates/home/Cargo.toml +++ b/src/tools/cargo/crates/home/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "home" -version = "0.5.4" # also update `html_root_url` in `src/lib.rs` +version = "0.5.6" # also update `html_root_url` in `src/lib.rs` authors = ["Brian Anderson "] documentation = "https://docs.rs/home" edition = "2018" @@ -17,4 +17,4 @@ repository = "https://github.com/rust-lang/cargo" description = "Shared definitions of home directories." [target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.45.0", features = ["Win32_Foundation", "Win32_UI_Shell"] } +windows-sys = { workspace = true, features = ["Win32_Foundation", "Win32_UI_Shell"] } diff --git a/src/tools/cargo/crates/home/src/env.rs b/src/tools/cargo/crates/home/src/env.rs index e47273bc8..49fea5422 100644 --- a/src/tools/cargo/crates/home/src/env.rs +++ b/src/tools/cargo/crates/home/src/env.rs @@ -9,7 +9,7 @@ use std::{ /// Permits parameterizing the home functions via the _from variants - used for /// in-process unit testing by rustup. pub trait Env { - /// Return the path to the the users home dir, or None if any error occurs: + /// Return the path to the users home dir, or None if any error occurs: /// see home_inner. fn home_dir(&self) -> Option; /// Return the current working directory. diff --git a/src/tools/cargo/crates/home/src/lib.rs b/src/tools/cargo/crates/home/src/lib.rs index 306026e40..0e1e975e4 100644 --- a/src/tools/cargo/crates/home/src/lib.rs +++ b/src/tools/cargo/crates/home/src/lib.rs @@ -1,14 +1,5 @@ //! Canonical definitions of `home_dir`, `cargo_home`, and `rustup_home`. //! -//! This provides the definition of `home_dir` used by Cargo and -//! rustup, as well functions to find the correct value of -//! `CARGO_HOME` and `RUSTUP_HOME`. -//! -//! See also the [`dirs`](https://docs.rs/dirs) crate. -//! -//! _Note that as of 2019/08/06 it appears that cargo uses this crate. And -//! rustup has used this crate since 2019/08/21._ -//! //! The definition of `home_dir` provided by the standard library is //! incorrect because it considers the `HOME` environment variable on //! Windows. This causes surprising situations where a Rust program @@ -17,15 +8,17 @@ //! rustup use the standard libraries definition - they use the //! definition here. //! -//! This crate further provides two functions, `cargo_home` and +//! This crate provides two additional functions, `cargo_home` and //! `rustup_home`, which are the canonical way to determine the -//! location that Cargo and rustup store their data. +//! location that Cargo and rustup use to store their data. +//! The `env` module contains utilities for mocking the process environment +//! by Cargo and rustup. //! //! See also this [discussion]. //! //! [discussion]: https://github.com/rust-lang/rust/pull/46799#issuecomment-361156935 -#![doc(html_root_url = "https://docs.rs/home/0.5.4")] +#![doc(html_root_url = "https://docs.rs/home/0.5.6")] #![deny(rust_2018_idioms)] pub mod env; @@ -36,29 +29,34 @@ mod windows; use std::io; use std::path::{Path, PathBuf}; -/// Returns the path of the current user's home directory if known. +/// Returns the path of the current user's home directory using environment +/// variables or OS-specific APIs. /// /// # Unix /// /// Returns the value of the `HOME` environment variable if it is set -/// and not equal to the empty string. Otherwise, it tries to determine the -/// home directory by invoking the `getpwuid_r` function on the UID of the -/// current user. +/// **even** if it is an empty string. Otherwise, it tries to determine the +/// home directory by invoking the [`getpwuid_r`][getpwuid] function with +/// the UID of the current user. +/// +/// [getpwuid]: https://linux.die.net/man/3/getpwuid_r /// /// # Windows /// -/// Returns the value of the `USERPROFILE` environment variable if it -/// is set and not equal to the empty string. If both do not exist, -/// [`SHGetFolderPathW`][msdn] is used to return the appropriate path. +/// Returns the value of the `USERPROFILE` environment variable if it is set +/// **and** it is not an empty string. Otherwise, it tries to determine the +/// home directory by invoking the [`SHGetFolderPathW`][shgfp] function with +/// [`CSIDL_PROFILE`][csidl]. /// -/// [msdn]: https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetfolderpathw +/// [shgfp]: https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetfolderpathw +/// [csidl]: https://learn.microsoft.com/en-us/windows/win32/shell/csidl /// /// # Examples /// /// ``` /// match home::home_dir() { -/// Some(path) => println!("{}", path.display()), -/// None => println!("Impossible to get your home dir!"), +/// Some(path) if !path.as_os_str().is_empty() => println!("{}", path.display()), +/// _ => println!("Unable to get your home dir!"), /// } /// ``` pub fn home_dir() -> Option { diff --git a/src/tools/cargo/crates/mdman/Cargo.lock b/src/tools/cargo/crates/mdman/Cargo.lock deleted file mode 100644 index 51fe47a9c..000000000 --- a/src/tools/cargo/crates/mdman/Cargo.lock +++ /dev/null @@ -1,459 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "anyhow" -version = "1.0.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -dependencies = [ - "block-padding", - "byte-tools", - "byteorder", - "generic-array", -] - -[[package]] -name = "block-padding" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -dependencies = [ - "byte-tools", -] - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - -[[package]] -name = "byteorder" -version = "1.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "ctor" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39858aa5bac06462d4dd4b9164848eb81ffc4aa5c479746393598fd193afa227" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "diff" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" - -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array", -] - -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" - -[[package]] -name = "form_urlencoded" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" -dependencies = [ - "matches", - "percent-encoding", -] - -[[package]] -name = "generic-array" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -dependencies = [ - "typenum", -] - -[[package]] -name = "handlebars" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86dbc8a0746b08f363d2e00da48e6c9ceb75c198ac692d2715fcbb5bee74c87d" -dependencies = [ - "log", - "pest", - "pest_derive", - "quick-error", - "serde", - "serde_json", - "walkdir", -] - -[[package]] -name = "idna" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "itoa" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" - -[[package]] -name = "log" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - -[[package]] -name = "matches" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" - -[[package]] -name = "mdman" -version = "0.1.0" -dependencies = [ - "anyhow", - "handlebars", - "pretty_assertions", - "pulldown-cmark", - "same-file", - "serde_json", - "url", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" - -[[package]] -name = "output_vt100" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" -dependencies = [ - "winapi", -] - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - -[[package]] -name = "pest" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" -dependencies = [ - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pest_meta" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" -dependencies = [ - "maplit", - "pest", - "sha-1", -] - -[[package]] -name = "pretty_assertions" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" -dependencies = [ - "ctor", - "diff", - "output_vt100", - "yansi", -] - -[[package]] -name = "proc-macro2" -version = "1.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "pulldown-cmark" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" -dependencies = [ - "bitflags", - "memchr", - "unicase", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quote" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "ryu" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "serde" -version = "1.0.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" - -[[package]] -name = "serde_json" -version = "1.0.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha-1" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" -dependencies = [ - "block-buffer", - "digest", - "fake-simd", - "opaque-debug", -] - -[[package]] -name = "syn" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cdb98bcb1f9d81d07b536179c269ea15999b5d14ea958196413869445bb5250" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "tinyvec" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53953d2d3a5ad81d9f844a32f14ebb121f50b650cd59d0ee2a07cf13c617efed" - -[[package]] -name = "typenum" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" - -[[package]] -name = "ucd-trie" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" - -[[package]] -name = "unicase" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -dependencies = [ - "matches", -] - -[[package]] -name = "unicode-normalization" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-xid" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" - -[[package]] -name = "url" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" -dependencies = [ - "form_urlencoded", - "idna", - "matches", - "percent-encoding", -] - -[[package]] -name = "version_check" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" - -[[package]] -name = "walkdir" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" -dependencies = [ - "same-file", - "winapi", - "winapi-util", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "yansi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/src/tools/cargo/crates/mdman/Cargo.toml b/src/tools/cargo/crates/mdman/Cargo.toml index 92cdf2eb6..812f1393a 100644 --- a/src/tools/cargo/crates/mdman/Cargo.toml +++ b/src/tools/cargo/crates/mdman/Cargo.toml @@ -1,17 +1,18 @@ [package] name = "mdman" -version = "0.1.0" +version = "0.0.0" edition = "2021" license = "MIT OR Apache-2.0" description = "Creates a man page page from markdown." +publish = false [dependencies] -anyhow = "1.0.31" -handlebars = { version = "3.2.1", features = ["dir_source"] } -pulldown-cmark = { version = "0.9.2", default-features = false } -same-file = "1.0.6" -serde_json = "1.0.56" -url = "2.2.2" +anyhow.workspace = true +handlebars.workspace = true +pulldown-cmark.workspace = true +same-file.workspace = true +serde_json.workspace = true +url.workspace = true [dev-dependencies] -pretty_assertions = "1.3.0" +pretty_assertions.workspace = true diff --git a/src/tools/cargo/crates/mdman/build-man.sh b/src/tools/cargo/crates/mdman/build-man.sh deleted file mode 100755 index 9286b17c2..000000000 --- a/src/tools/cargo/crates/mdman/build-man.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -set -e - -cargo run -- -t md -o doc/out doc/*.md -cargo run -- -t txt -o doc/out doc/*.md -cargo run -- -t man -o doc/out doc/*.md diff --git a/src/tools/cargo/crates/mdman/doc/mdman.md b/src/tools/cargo/crates/mdman/doc/mdman.md index 2025c13dc..bfa394648 100644 --- a/src/tools/cargo/crates/mdman/doc/mdman.md +++ b/src/tools/cargo/crates/mdman/doc/mdman.md @@ -28,7 +28,7 @@ man page: and `{{/options}}` tags. This tells the processor where the options start and end. - Each option must be expressed with a `{{#option}}` block. The parameters to - the the block are a sequence of strings indicating the option. For example, + the block are a sequence of strings indicating the option. For example, ```{{#option "`-p` _spec_..." "`--package` _spec_..."}}``` is an option that has two different forms. The text within the string is processed as markdown. It is recommended to use formatting similar to this example. diff --git a/src/tools/cargo/crates/mdman/doc/out/mdman.1 b/src/tools/cargo/crates/mdman/doc/out/mdman.1 index 0718d6ddb..b114715ae 100644 --- a/src/tools/cargo/crates/mdman/doc/out/mdman.1 +++ b/src/tools/cargo/crates/mdman/doc/out/mdman.1 @@ -6,7 +6,7 @@ .SH "NAME" mdman \- Converts markdown to a man page .SH "SYNOPSIS" -\fBmdman\fR [\fIoptions\fR] \fB\-t\fR \fItype\fR \fB\-o\fR \fIoutdir\fR \fIsources...\fR +\fBmdman\fR [\fIoptions\fR] \fB\-t\fR \fItype\fR \fB\-o\fR \fIoutdir\fR \fIsources\[u2026]\fR .SH "DESCRIPTION" Converts a markdown file to a man page. .sp @@ -29,7 +29,7 @@ and end. .sp .RS 4 \h'-04'\(bu\h'+02'Each option must be expressed with a \fB{{#option}}\fR block. The parameters to -the the block are a sequence of strings indicating the option. For example, +the block are a sequence of strings indicating the option. For example, \fB{{#option "`\-p` _spec_..." "`\-\-package` _spec_..."}}\fR is an option that has two different forms. The text within the string is processed as markdown. It is recommended to use formatting similar to this example. @@ -83,7 +83,7 @@ Outputs with the \fB\&.md\fR extension. .RE .sp .RS 4 -\h'-04'\(bu\h'+02'\fBtxt\fR \[em] A text file, rendered for situations where a man page viewer isn't +\h'-04'\(bu\h'+02'\fBtxt\fR \[em] A text file, rendered for situations where a man page viewer isn\[cq]t available. Outputs with the \fB\&.txt\fR extension. .RE .RE @@ -107,7 +107,7 @@ matching \fB\-\-man\fR entry, then a relative link to a file named \fIname\fR\fB be used. .RE .sp -\fIsources...\fR +\fIsources\[u2026]\fR .RS 4 The source input filename, may be specified multiple times. .RE diff --git a/src/tools/cargo/crates/mdman/doc/out/mdman.md b/src/tools/cargo/crates/mdman/doc/out/mdman.md index d0dd34511..9d2fe9326 100644 --- a/src/tools/cargo/crates/mdman/doc/out/mdman.md +++ b/src/tools/cargo/crates/mdman/doc/out/mdman.md @@ -27,7 +27,7 @@ man page: and `{{/options}}` tags. This tells the processor where the options start and end. - Each option must be expressed with a `{{#option}}` block. The parameters to - the the block are a sequence of strings indicating the option. For example, + the block are a sequence of strings indicating the option. For example, ```{{#option "`-p` _spec_..." "`--package` _spec_..."}}``` is an option that has two different forms. The text within the string is processed as markdown. It is recommended to use formatting similar to this example. @@ -61,7 +61,7 @@ man page: .1) matching the man page section.
  • md — A markdown file, after all handlebars processing has been finished. Outputs with the .md extension.
  • -
  • txt — A text file, rendered for situations where a man page viewer isn't +
  • txt — A text file, rendered for situations where a man page viewer isn’t available. Outputs with the .txt extension.
  • @@ -82,7 +82,7 @@ matching --man entry, then a relative link to a file named name be used. -
    sources...
    +
    sources…
    The source input filename, may be specified multiple times.
    diff --git a/src/tools/cargo/crates/mdman/doc/out/mdman.txt b/src/tools/cargo/crates/mdman/doc/out/mdman.txt index 83fa7de90..6c8dcd65d 100644 --- a/src/tools/cargo/crates/mdman/doc/out/mdman.txt +++ b/src/tools/cargo/crates/mdman/doc/out/mdman.txt @@ -4,7 +4,7 @@ NAME mdman - Converts markdown to a man page SYNOPSIS - mdman [options] -t type -o outdir sources... + mdman [options] -t type -o outdir sources… DESCRIPTION Converts a markdown file to a man page. @@ -25,7 +25,7 @@ DESCRIPTION the options start and end. o Each option must be expressed with a {{#option}} block. The - parameters to the the block are a sequence of strings indicating the + parameters to the block are a sequence of strings indicating the option. For example, {{#option "`-p` _spec_..." "`--package` _spec_..."}} is an option that has two different forms. The text within the string is processed as markdown. It is recommended to use @@ -65,7 +65,7 @@ OPTIONS finished. Outputs with the .md extension. o txt — A text file, rendered for situations where a man page - viewer isn't available. Outputs with the .txt extension. + viewer isn’t available. Outputs with the .txt extension. -o outdir Specifies the directory where to save the output. @@ -81,7 +81,7 @@ OPTIONS does not have a matching --man entry, then a relative link to a file named name.md will be used. - sources... + sources… The source input filename, may be specified multiple times. EXAMPLES diff --git a/src/tools/cargo/crates/resolver-tests/Cargo.toml b/src/tools/cargo/crates/resolver-tests/Cargo.toml index e4aab4325..8a7cab113 100644 --- a/src/tools/cargo/crates/resolver-tests/Cargo.toml +++ b/src/tools/cargo/crates/resolver-tests/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "resolver-tests" -version = "0.1.0" +version = "0.0.0" edition = "2018" +publish = false [dependencies] -cargo = { path = "../.." } -cargo-util = { path = "../cargo-util" } -is-terminal = "0.4.0" -lazy_static = "1.3.0" -proptest = "1.1.0" -varisat = "0.2.1" +cargo.workspace = true +cargo-util.workspace = true +is-terminal.workspace = true +lazy_static.workspace = true +proptest.workspace = true +varisat.workspace = true diff --git a/src/tools/cargo/crates/resolver-tests/src/lib.rs b/src/tools/cargo/crates/resolver-tests/src/lib.rs index 3ffb6c5d2..01d9b5e6d 100644 --- a/src/tools/cargo/crates/resolver-tests/src/lib.rs +++ b/src/tools/cargo/crates/resolver-tests/src/lib.rs @@ -184,6 +184,7 @@ pub fn resolve_with_config_raw( deps, &BTreeMap::new(), None::<&String>, + None::<&String>, ) .unwrap(); let opts = ResolveOpts::everything(); @@ -585,6 +586,7 @@ pub fn pkg_dep(name: T, dep: Vec) -> Summary { dep, &BTreeMap::new(), link, + None::<&String>, ) .unwrap() } @@ -613,6 +615,7 @@ pub fn pkg_loc(name: &str, loc: &str) -> Summary { Vec::new(), &BTreeMap::new(), link, + None::<&String>, ) .unwrap() } @@ -627,6 +630,7 @@ pub fn remove_dep(sum: &Summary, ind: usize) -> Summary { deps, &BTreeMap::new(), sum.links().map(|a| a.as_str()), + None::<&String>, ) .unwrap() } diff --git a/src/tools/cargo/crates/semver-check/Cargo.toml b/src/tools/cargo/crates/semver-check/Cargo.toml new file mode 100644 index 000000000..f7b8c7d48 --- /dev/null +++ b/src/tools/cargo/crates/semver-check/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "semver-check" +version = "0.0.0" +authors = ["Eric Huss"] +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tempfile.workspace = true diff --git a/src/tools/cargo/crates/semver-check/src/main.rs b/src/tools/cargo/crates/semver-check/src/main.rs new file mode 100644 index 000000000..fa4639eb7 --- /dev/null +++ b/src/tools/cargo/crates/semver-check/src/main.rs @@ -0,0 +1,293 @@ +//! Test runner for the semver compatibility doc chapter. +//! +//! This extracts all the "rust" annotated code blocks and tests that they +//! either fail or succeed as expected. This also checks that the examples are +//! formatted correctly. +//! +//! An example with the word "MINOR" at the top is expected to successfully +//! build against the before and after. Otherwise it should fail. A comment of +//! "// Error:" will check that the given message appears in the error output. + +use std::error::Error; +use std::fs; +use std::path::Path; +use std::process::{Command, Output}; + +fn main() { + if let Err(e) = doit() { + println!("error: {}", e); + std::process::exit(1); + } +} + +const SEPARATOR: &str = "///////////////////////////////////////////////////////////"; + +fn doit() -> Result<(), Box> { + let filename = std::env::args().nth(1).unwrap_or_else(|| { + Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../src/doc/src/reference/semver.md") + .to_str() + .unwrap() + .to_string() + }); + let contents = fs::read_to_string(filename)?; + let mut lines = contents.lines().enumerate(); + + loop { + // Find a rust block. + let (block_start, run_program, deny_warnings) = loop { + match lines.next() { + Some((lineno, line)) => { + if line.trim().starts_with("```rust") && !line.contains("skip") { + break ( + lineno + 1, + line.contains("run-fail"), + !line.contains("dont-deny"), + ); + } + } + None => return Ok(()), + } + }; + // Read in the code block. + let mut block = Vec::new(); + loop { + match lines.next() { + Some((_, line)) => { + if line.trim() == "```" { + break; + } + block.push(line); + } + None => { + return Err(format!( + "rust block did not end for example starting on line {}", + block_start + ) + .into()); + } + } + } + // Split it into the separate source files. + let parts: Vec<_> = block.split(|line| line.trim() == SEPARATOR).collect(); + if parts.len() != 4 { + return Err(format!( + "expected 4 sections in example starting on line {}, got {}:\n{:?}", + block_start, + parts.len(), + parts + ) + .into()); + } + let join = |part: &[&str]| { + let mut result = String::new(); + result.push_str("#![allow(unused)]\n"); + if deny_warnings { + result.push_str("#![deny(warnings)]\n"); + } + result.push_str(&part.join("\n")); + if !result.ends_with('\n') { + result.push('\n'); + } + result + }; + let expect_success = parts[0][0].contains("MINOR"); + println!("Running test from line {}", block_start); + + let result = run_test( + join(parts[1]), + join(parts[2]), + join(parts[3]), + expect_success, + run_program, + ); + + if let Err(e) = result { + return Err(format!( + "test failed for example starting on line {}: {}", + block_start, e + ) + .into()); + } + } +} + +const CRATE_NAME: &str = "updated_crate"; + +fn run_test( + before: String, + after: String, + example: String, + expect_success: bool, + run_program: bool, +) -> Result<(), Box> { + let tempdir = tempfile::TempDir::new()?; + let before_p = tempdir.path().join("before.rs"); + let after_p = tempdir.path().join("after.rs"); + let example_p = tempdir.path().join("example.rs"); + + let check_fn = if run_program { + run_check + } else { + compile_check + }; + + compile_check(before, &before_p, CRATE_NAME, false, true)?; + check_fn(example.clone(), &example_p, "example", true, true)?; + compile_check(after, &after_p, CRATE_NAME, false, true)?; + check_fn(example, &example_p, "example", true, expect_success)?; + Ok(()) +} + +fn check_formatting(path: &Path) -> Result<(), Box> { + match Command::new("rustfmt") + .args(&["--edition=2018", "--check"]) + .arg(path) + .status() + { + Ok(status) => { + if !status.success() { + return Err(format!("failed to run rustfmt: {}", status).into()); + } + Ok(()) + } + Err(e) => Err(format!("failed to run rustfmt: {}", e).into()), + } +} + +fn compile( + contents: &str, + path: &Path, + crate_name: &str, + extern_path: bool, +) -> Result> { + let crate_type = if contents.contains("fn main()") { + "bin" + } else { + "rlib" + }; + + fs::write(path, &contents)?; + check_formatting(path)?; + let out_dir = path.parent().unwrap(); + let mut cmd = Command::new("rustc"); + cmd.args(&[ + "--edition=2021", + "--crate-type", + crate_type, + "--crate-name", + crate_name, + "--out-dir", + ]); + cmd.arg(&out_dir); + if extern_path { + let epath = out_dir.join(format!("lib{}.rlib", CRATE_NAME)); + cmd.arg("--extern") + .arg(format!("{}={}", CRATE_NAME, epath.display())); + } + cmd.arg(path); + cmd.output().map_err(Into::into) +} + +fn compile_check( + mut contents: String, + path: &Path, + crate_name: &str, + extern_path: bool, + expect_success: bool, +) -> Result<(), Box> { + // If the example has an error message, remove it so that it can be + // compared with the actual output, and also to avoid issues with rustfmt + // moving it around. + let expected_error = match contents.find("// Error:") { + Some(index) => { + let start = contents[..index].rfind(|ch| ch != ' ').unwrap(); + let end = contents[index..].find('\n').unwrap(); + let error = contents[index + 9..index + end].trim().to_string(); + contents.replace_range(start + 1..index + end, ""); + Some(error) + } + None => None, + }; + + let output = compile(&contents, path, crate_name, extern_path)?; + + let stderr = std::str::from_utf8(&output.stderr).unwrap(); + match (output.status.success(), expect_success) { + (true, true) => Ok(()), + (true, false) => Err(format!( + "expected failure, got success {}\n===== Contents:\n{}\n===== Output:\n{}\n", + path.display(), + contents, + stderr + ) + .into()), + (false, true) => Err(format!( + "expected success, got error {}\n===== Contents:\n{}\n===== Output:\n{}\n", + path.display(), + contents, + stderr + ) + .into()), + (false, false) => { + if expected_error.is_none() { + return Err("failing test should have an \"// Error:\" annotation ".into()); + } + let expected_error = expected_error.unwrap(); + if !stderr.contains(&expected_error) { + Err(format!( + "expected error message not found in compiler output\nExpected: {}\nGot:\n{}\n", + expected_error, stderr + ) + .into()) + } else { + Ok(()) + } + } + } +} + +fn run_check( + contents: String, + path: &Path, + crate_name: &str, + extern_path: bool, + expect_success: bool, +) -> Result<(), Box> { + let compile_output = compile(&contents, path, crate_name, extern_path)?; + + if !compile_output.status.success() { + let stderr = std::str::from_utf8(&compile_output.stderr).unwrap(); + return Err(format!( + "expected success, got error {}\n===== Contents:\n{}\n===== Output:\n{}\n", + path.display(), + contents, + stderr + ) + .into()); + } + + let binary_path = path.parent().unwrap().join(crate_name); + + let output = Command::new(binary_path).output()?; + + let stderr = std::str::from_utf8(&output.stderr).unwrap(); + + match (output.status.success(), expect_success) { + (true, false) => Err(format!( + "expected panic, got success {}\n===== Contents:\n{}\n===== Output:\n{}\n", + path.display(), + contents, + stderr + ) + .into()), + (false, true) => Err(format!( + "expected success, got panic {}\n===== Contents:\n{}\n===== Output:\n{}\n", + path.display(), + contents, + stderr, + ) + .into()), + (_, _) => Ok(()), + } +} diff --git a/src/tools/cargo/crates/xtask-build-man/Cargo.toml b/src/tools/cargo/crates/xtask-build-man/Cargo.toml new file mode 100644 index 000000000..6d02aa2c3 --- /dev/null +++ b/src/tools/cargo/crates/xtask-build-man/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "xtask-build-man" +version = "0.0.0" +edition = "2021" +publish = false + +[dependencies] diff --git a/src/tools/cargo/crates/xtask-build-man/src/main.rs b/src/tools/cargo/crates/xtask-build-man/src/main.rs new file mode 100644 index 000000000..6680c3783 --- /dev/null +++ b/src/tools/cargo/crates/xtask-build-man/src/main.rs @@ -0,0 +1,108 @@ +//! ```text +//! NAME +//! build-man +//! +//! SYNOPSIS +//! build-man +//! +//! DESCRIPTION +//! Build the man pages for packages `mdman` and `cargo`. +//! For more, read their doc comments. +//! ``` + +use std::fs; +use std::io; +use std::path::PathBuf; +use std::process; +use std::process::Command; + +fn main() -> io::Result<()> { + build_mdman()?; + build_cargo()?; + Ok(()) +} + +/// Builds the man pages for `mdman`. +fn build_mdman() -> io::Result<()> { + cwd_to_workspace_root()?; + + let src_paths = &["crates/mdman/doc/mdman.md".into()]; + let dst_path = "crates/mdman/doc/out"; + let outs = [("md", dst_path), ("txt", dst_path), ("man", dst_path)]; + + build_man("mdman", src_paths, &outs, &[]) +} + +/// Builds the man pages for Cargo. +/// +/// The source for the man pages are located in src/doc/man/ in markdown format. +/// These also are handlebars templates, see crates/mdman/README.md for details. +/// +/// The generated man pages are placed in the src/etc/man/ directory. The pages +/// are also expanded into markdown (after being expanded by handlebars) and +/// saved in the src/doc/src/commands/ directory. These are included in the +/// Cargo book, which is converted to HTML by mdbook. +fn build_cargo() -> io::Result<()> { + // Find all `src/doc/man/cargo-*.md` + let src_paths = { + let mut src_paths = Vec::new(); + for entry in fs::read_dir("src/doc/man")? { + let entry = entry?; + let file_name = entry.file_name(); + let file_name = file_name.to_str().unwrap(); + if file_name.starts_with("cargo-") && file_name.ends_with(".md") { + src_paths.push(entry.path()); + } + } + src_paths + }; + let outs = [ + ("md", "src/doc/src/commands"), + ("txt", "src/doc/man/generated_txt"), + ("man", "src/etc/man"), + ]; + let args = [ + "--url", + "https://doc.rust-lang.org/cargo/commands/", + "--man", + "rustc:1=https://doc.rust-lang.org/rustc/index.html", + "--man", + "rustdoc:1=https://doc.rust-lang.org/rustdoc/index.html", + ]; + build_man("cargo", &src_paths[..], &outs, &args) +} + +/// Change to workspace root. +/// +/// Assumed this xtask is located in `[WORKSPACE]/crates/xtask-build-man`. +fn cwd_to_workspace_root() -> io::Result<()> { + let pkg_root = std::env!("CARGO_MANIFEST_DIR"); + let ws_root = format!("{pkg_root}/../.."); + std::env::set_current_dir(ws_root) +} + +/// Builds the man pages. +fn build_man( + pkg_name: &str, + src_paths: &[PathBuf], + outs: &[(&str, &str)], + extra_args: &[&str], +) -> io::Result<()> { + for (format, dst_path) in outs { + eprintln!("Start converting `{format}` for package `{pkg_name}`..."); + let mut cmd = Command::new(std::env!("CARGO")); + cmd.args(["run", "--package", "mdman", "--"]) + .args(["-t", format, "-o", dst_path]) + .args(src_paths) + .args(extra_args); + + let status = cmd.status()?; + if !status.success() { + eprintln!("failed to build the man pages for package `{pkg_name}`"); + eprintln!("failed command: `{cmd:?}`"); + process::exit(status.code().unwrap_or(1)); + } + } + + Ok(()) +} diff --git a/src/tools/cargo/crates/xtask-stale-label/Cargo.toml b/src/tools/cargo/crates/xtask-stale-label/Cargo.toml new file mode 100644 index 000000000..af3218e96 --- /dev/null +++ b/src/tools/cargo/crates/xtask-stale-label/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "xtask-stale-label" +version = "0.0.0" +edition = "2021" +publish = false + +[dependencies] +toml_edit.workspace = true diff --git a/src/tools/cargo/crates/xtask-stale-label/src/main.rs b/src/tools/cargo/crates/xtask-stale-label/src/main.rs new file mode 100644 index 000000000..37675979c --- /dev/null +++ b/src/tools/cargo/crates/xtask-stale-label/src/main.rs @@ -0,0 +1,91 @@ +//! ```text +//! NAME +//! stale-label +//! +//! SYNOPSIS +//! stale-label +//! +//! DESCRIPTION +//! Detect stale paths in autolabel definitions in triagebot.toml. +//! Probably autofix them in the future. +//! ``` + +use std::fmt::Write as _; +use std::path::PathBuf; +use std::process; +use toml_edit::Document; + +fn main() { + let pkg_root = std::env!("CARGO_MANIFEST_DIR"); + let ws_root = PathBuf::from(format!("{pkg_root}/../..")); + let path = { + let path = ws_root.join("triagebot.toml"); + path.canonicalize().unwrap_or(path) + }; + + eprintln!("Checking file {path:?}\n"); + + let mut failed = 0; + let mut passed = 0; + + let toml = std::fs::read_to_string(path).expect("read from file"); + let doc = toml.parse::().expect("a toml"); + let autolabel = doc["autolabel"].as_table().expect("a toml table"); + + for (label, value) in autolabel.iter() { + let Some(trigger_files) = value.get("trigger_files") else { + continue + }; + let trigger_files = trigger_files.as_array().expect("an array"); + let missing_files: Vec<_> = trigger_files + .iter() + // Hey TOML content is strict UTF-8. + .map(|v| v.as_str().unwrap()) + .filter(|f| { + // triagebot checks with `starts_with` only. + // See https://github.com/rust-lang/triagebot/blob/0e4b48ca86ffede9cc70fb1611e658e4d013bce2/src/handlers/autolabel.rs#L45 + let path = ws_root.join(f); + if path.exists() { + return false; + } + let Some(mut read_dir) = path.parent().and_then(|p| p.read_dir().ok()) else { + return true; + }; + !read_dir.any(|e| { + e.unwrap() + .path() + .strip_prefix(&ws_root) + .unwrap() + .to_str() + .unwrap() + .starts_with(f) + }) + }) + .collect(); + + failed += missing_files.len(); + passed += trigger_files.len() - missing_files.len(); + + if missing_files.is_empty() { + continue; + } + + let mut msg = String::new(); + writeln!( + &mut msg, + "missing files defined in `autolabel.{label}.trigger_files`:" + ) + .unwrap(); + for f in missing_files.iter() { + writeln!(&mut msg, "\t {f}").unwrap(); + } + eprintln!("{msg}"); + } + + let result = if failed == 0 { "ok" } else { "FAILED" }; + eprintln!("test result: {result}. {passed} passed; {failed} failed;"); + + if failed > 0 { + process::exit(1); + } +} diff --git a/src/tools/cargo/crates/xtask-unpublished/Cargo.toml b/src/tools/cargo/crates/xtask-unpublished/Cargo.toml new file mode 100644 index 000000000..541a34dea --- /dev/null +++ b/src/tools/cargo/crates/xtask-unpublished/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "xtask-unpublished" +version = "0.0.0" +edition = "2021" +publish = false + +[dependencies] +anyhow.workspace = true +cargo.workspace = true +clap.workspace = true +env_logger.workspace = true +log.workspace = true diff --git a/src/tools/cargo/crates/xtask-unpublished/src/main.rs b/src/tools/cargo/crates/xtask-unpublished/src/main.rs new file mode 100644 index 000000000..1942a3621 --- /dev/null +++ b/src/tools/cargo/crates/xtask-unpublished/src/main.rs @@ -0,0 +1,15 @@ +mod xtask; + +fn main() { + env_logger::init_from_env("CARGO_LOG"); + let cli = xtask::cli(); + let matches = cli.get_matches(); + + let mut config = cargo::util::config::Config::default().unwrap_or_else(|e| { + let mut eval = cargo::core::shell::Shell::new(); + cargo::exit_with_error(e.into(), &mut eval) + }); + if let Err(e) = xtask::exec(&matches, &mut config) { + cargo::exit_with_error(e, &mut config.shell()) + } +} diff --git a/src/tools/cargo/crates/xtask-unpublished/src/xtask.rs b/src/tools/cargo/crates/xtask-unpublished/src/xtask.rs new file mode 100644 index 000000000..f1086951f --- /dev/null +++ b/src/tools/cargo/crates/xtask-unpublished/src/xtask.rs @@ -0,0 +1,200 @@ +//! `xtask-unpublished` outputs a table with publish status --- a local version +//! and a version on crates.io for comparisons. +//! +//! This aims to help developers check if there is any crate required a new +//! publish, as well as detect if a version bump is needed in CI pipeline. + +use std::collections::HashSet; + +use cargo::core::registry::PackageRegistry; +use cargo::core::QueryKind; +use cargo::core::Registry; +use cargo::core::SourceId; +use cargo::ops::Packages; +use cargo::util::command_prelude::*; + +pub fn cli() -> clap::Command { + clap::Command::new("xtask-unpublished") + .arg_package_spec_simple("Package to inspect the published status") + .arg( + opt( + "verbose", + "Use verbose output (-vv very verbose/build.rs output)", + ) + .short('v') + .action(ArgAction::Count) + .global(true), + ) + .arg_quiet() + .arg( + opt("color", "Coloring: auto, always, never") + .value_name("WHEN") + .global(true), + ) + .arg(flag("frozen", "Require Cargo.lock and cache are up to date").global(true)) + .arg(flag("locked", "Require Cargo.lock is up to date").global(true)) + .arg(flag("offline", "Run without accessing the network").global(true)) + .arg(multi_opt("config", "KEY=VALUE", "Override a configuration value").global(true)) + .arg( + Arg::new("unstable-features") + .help("Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details") + .short('Z') + .value_name("FLAG") + .action(ArgAction::Append) + .global(true), + ) +} + +pub fn exec(args: &clap::ArgMatches, config: &mut cargo::util::Config) -> cargo::CliResult { + config_configure(config, args)?; + + unpublished(args, config)?; + + Ok(()) +} + +fn config_configure(config: &mut Config, args: &ArgMatches) -> CliResult { + let verbose = args.verbose(); + // quiet is unusual because it is redefined in some subcommands in order + // to provide custom help text. + let quiet = args.flag("quiet"); + let color = args.get_one::("color").map(String::as_str); + let frozen = args.flag("frozen"); + let locked = args.flag("locked"); + let offline = args.flag("offline"); + let mut unstable_flags = vec![]; + if let Some(values) = args.get_many::("unstable-features") { + unstable_flags.extend(values.cloned()); + } + let mut config_args = vec![]; + if let Some(values) = args.get_many::("config") { + config_args.extend(values.cloned()); + } + config.configure( + verbose, + quiet, + color, + frozen, + locked, + offline, + &None, + &unstable_flags, + &config_args, + )?; + Ok(()) +} + +fn unpublished(args: &clap::ArgMatches, config: &mut cargo::util::Config) -> cargo::CliResult { + let ws = args.workspace(config)?; + + let members_to_inspect: HashSet<_> = { + let pkgs = args.packages_from_flags()?; + if let Packages::Packages(_) = pkgs { + HashSet::from_iter(pkgs.get_packages(&ws)?) + } else { + HashSet::from_iter(ws.members()) + } + }; + + let mut results = Vec::new(); + { + let mut registry = PackageRegistry::new(config)?; + let _lock = config.acquire_package_cache_lock()?; + registry.lock_patches(); + let source_id = SourceId::crates_io(config)?; + + for member in members_to_inspect { + let name = member.name(); + let current = member.version(); + if member.publish() == &Some(vec![]) { + log::trace!("skipping {name}, `publish = false`"); + continue; + } + + let version_req = format!("<={current}"); + let query = + cargo::core::dependency::Dependency::parse(name, Some(&version_req), source_id)?; + let possibilities = loop { + // Exact to avoid returning all for path/git + match registry.query_vec(&query, QueryKind::Exact) { + std::task::Poll::Ready(res) => { + break res?; + } + std::task::Poll::Pending => registry.block_until_ready()?, + } + }; + let (last, published) = possibilities + .iter() + .map(|s| s.version()) + .max() + .map(|last| (last.to_string(), last == current)) + .unwrap_or(("-".to_string(), false)); + + results.push(vec![ + name.to_string(), + last, + current.to_string(), + if published { "yes" } else { "no" }.to_string(), + ]); + } + } + results.sort(); + + if results.is_empty() { + return Ok(()); + } + + results.insert( + 0, + vec![ + "name".to_owned(), + "crates.io".to_owned(), + "local".to_owned(), + "published?".to_owned(), + ], + ); + + output_table(results); + + Ok(()) +} + +/// Outputs a markdown table like this. +/// +/// ```text +/// | name | crates.io | local | published? | +/// |------------------|-----------|--------|------------| +/// | cargo | 0.70.1 | 0.72.0 | no | +/// | cargo-platform | 0.1.2 | 0.1.2 | yes | +/// | cargo-util | - | 0.2.4 | no | +/// | crates-io | 0.36.0 | 0.36.0 | yes | +/// | home | - | 0.5.6 | no | +/// ``` +fn output_table(table: Vec>) { + let header = table.first().unwrap(); + let paddings = table.iter().fold(vec![0; header.len()], |mut widths, row| { + for (width, field) in widths.iter_mut().zip(row) { + *width = usize::max(*width, field.len()); + } + widths + }); + + let print = |row: &[_]| { + for (field, pad) in row.iter().zip(&paddings) { + print!("| {field:pad$} "); + } + println!("|"); + }; + + print(header); + + paddings.iter().for_each(|fill| print!("|-{:-