cargo_toolchain/lib.rs
1//! # cargo-toolchain
2//!
3//! `cargo-toolchain` is a utility to get the currently active and default
4//! [rustup toolchains](https://doc.rust-lang.org/stable/book/appendix-07-nightly-rust.html#rustup-and-the-role-of-rust-nightly).
5//!
6//! It requires that [rustup](https://rustup.rs/) is installed.
7//!
8//! ## Usage as a CLI
9//!
10//! ```shell
11//! cargo install cargo-toolchain
12//!
13//! cargo toolchain # prints the currently active cargo toolchain, e.g. 'stable'
14//!
15//! cargo toolchain -d # prints the default toolchain for the directory
16//!
17//! cargo toolchain -h # print help message
18//! ```
19
20use anyhow::Result;
21use std::env;
22use std::process::Command;
23
24const RUSTUP_TOOLCHAIN_VAR: &str = "RUSTUP_TOOLCHAIN";
25const DEFAULT_PROFILES: [&str; 3] = ["stable", "beta", "nightly"];
26
27/// Get the profile that was used to call the current executable.
28///
29/// Affected by directory overrides and command line flags (e.g. `cargo +nightly ...`)
30///
31/// # Errors
32///
33/// If the `RUSTUP_TOOLCHAIN` variable is not set, `get_active_toolchain` falls back to [`get_directory_toolchain`](./fn.get_directory_toolchain.html).
34/// That function returns an error if running `rustup` or parsing its output as utf-8 fails.
35pub fn get_active_toolchain() -> Result<String> {
36 match env::var(RUSTUP_TOOLCHAIN_VAR) {
37 Ok(toolchain) => Ok(truncate_toolchain(toolchain)),
38 _ => get_directory_toolchain(),
39 }
40}
41
42/// Get the default rustup toolchain for the current directory.
43///
44/// Affected by directory overrides, but not command line flags (e.g. `cargo +nightly ...`)
45///
46/// # Errors
47///
48/// If the `rustup` command fails or its output cannot be interpreted as utf-8,
49/// an error is returned.
50pub fn get_directory_toolchain() -> Result<String> {
51 let output = Command::new("rustup")
52 .args(&["show", "active-toolchain"])
53 .env_remove(RUSTUP_TOOLCHAIN_VAR)
54 .output()?;
55 let toolchain = String::from_utf8(output.stdout)?;
56
57 Ok(truncate_toolchain(toolchain))
58}
59
60/// Truncate the triple from a toolchain name if it's one of the defaults.
61///
62/// # Examples
63///
64/// ```rust
65/// let toolchain = cargo_toolchain::truncate_toolchain(String::from("nightly-x86_64-unknown-linux-gnu"));
66///
67/// assert_eq!(toolchain, "nightly");
68///
69/// let toolchain = cargo_toolchain::truncate_toolchain(String::from("my-custom-toolchain"));
70///
71/// assert_eq!(toolchain, "my-custom-toolchain");
72/// ```
73///
74pub fn truncate_toolchain(toolchain: String) -> String {
75 for default in &DEFAULT_PROFILES {
76 if toolchain.starts_with(default) {
77 return String::from(*default);
78 }
79 }
80
81 toolchain
82}
83
84/// Wrapper around [`get_active_toolchain`](./fn.get_active_toolchain.html)
85/// and [`get_directory_toolchain`](./fn.get_directory_toolchain.html)
86pub fn get_toolchain(directory_default: bool) -> Result<String> {
87 if directory_default {
88 return get_directory_toolchain();
89 }
90
91 get_active_toolchain()
92}