cargo-equip 0.20.0

A Cargo subcommand to bundle your code into one `.rs` file for competitive programming.
Documentation
use crate::process::ProcessBuilderExt as _;
use anyhow::{anyhow, ensure};
use camino::Utf8Path;
use cargo_util::ProcessBuilder;
use ra_ap_paths::AbsPathBuf;
use semver::Version;
use std::{
    env,
    path::{Path, PathBuf},
};
use tap::Pipe as _;

pub(crate) fn rustup_exe(cwd: impl AsRef<Path>) -> anyhow::Result<PathBuf> {
    which::which_in("rustup", env::var_os("PATH"), cwd).map_err(|_| anyhow!("`rustup` not found"))
}

pub(crate) fn active_toolchain(manifest_dir: &Utf8Path) -> anyhow::Result<String> {
    let output = ProcessBuilder::new(rustup_exe(manifest_dir)?)
        .args(&["show", "active-toolchain"])
        .cwd(manifest_dir)
        .read_stdout::<String>()?;
    Ok(output.split_whitespace().next().unwrap().to_owned())
}

pub(crate) fn find_rust_analyzer_proc_macro_srv(
    manifest_dir: &Utf8Path,
    toolchain: &str,
) -> anyhow::Result<AbsPathBuf> {
    use crate::ra_proc_macro::MSRV;

    let rustup_exe = &rustup_exe(manifest_dir)?;

    let version = ProcessBuilder::new(rustup_exe)
        .args(&["run", toolchain, "rustc", "-V"])
        .cwd(manifest_dir)
        .read_stdout::<String>()?
        .pipe(|output| {
            output
                .split_ascii_whitespace()
                .nth(1)
                .and_then(|output| output.parse::<Version>().ok())
                .ok_or_else(|| anyhow!("Could not parse {output:?}"))
        })?;

    ensure!(
        version >= MSRV,
        "Rust ≧{MSRV} is required for expanding procedural macros. Specify one with \
         `--toolchain-for-proc-macro-srv`",
    );

    let rust_analyzer_proc_macro_srv = ProcessBuilder::new(rustup_exe)
        .args(&["run", toolchain, "rustc", "--print", "sysroot"])
        .cwd(manifest_dir)
        .read_stdout::<String>()?
        .pipe_deref(str::trim_end)
        .pipe(Path::new)
        .join("libexec")
        .join("rust-analyzer-proc-macro-srv")
        .with_extension(env::consts::EXE_EXTENSION)
        .pipe(AbsPathBuf::assert);

    if !Path::new(rust_analyzer_proc_macro_srv.as_os_str()).try_exists()? {
        anyhow::bail!(
            "{} does not exist. Run `rustup component add rust-analyzer --toolchain {toolchain}`",
            Path::new(rust_analyzer_proc_macro_srv.as_os_str()).display(),
        );
    }

    Ok(rust_analyzer_proc_macro_srv)
}