cargo-udeps 0.1.21

Find unused dependencies in Cargo.toml
Documentation
use std::ffi::OsString;
use std::path::PathBuf;
use std::process::{Command, Output};
use std::sync::atomic::{self, AtomicBool};
use std::{env, fs, io, str};

use anyhow::Context;
use cargo::core::shell::Shell;
use cargo::{CargoResult, CliError};
use tempfile::TempDir;

static DEFAULT_TOOLCHAIN :&str = "nightly";

static RUSTC_ENV_SET :AtomicBool = AtomicBool::new(false);

pub(crate) struct Runner {
	cwd :TempDir,
	cargo_home :PathBuf,
	args :Vec<OsString>,
}

impl Runner {
	pub(crate) fn new(prefix :&str) -> CargoResult<Self> {
		if !RUSTC_ENV_SET.swap(true, atomic::Ordering::SeqCst) {
			let toolchain =
				env::var("CARGO_UDEPS_TEST_TOOLCHAIN").unwrap_or_else(|_| DEFAULT_TOOLCHAIN.to_owned());
			let Output { status, stdout, .. } = Command::new("rustup")
				.args(&["which", "rustc"])
				.env("RUSTUP_TOOLCHAIN", &toolchain)
				.output()?;
			if !status.success() {
				return Err(anyhow::anyhow!("{}", status))
					.with_context(|| format!("could not get the {} rustc", toolchain));
			}
			env::set_var("RUSTC", str::from_utf8(&stdout)?.trim());
		}
		let cwd = tempfile::Builder::new().prefix(prefix).tempdir()?;
		let cargo_home = cargo::util::config::homedir(cwd.as_ref())
			.with_context(|| "couldn't find the \"Cargo home\"")?;
		let args = vec!["".into(), "udeps".into()];
		Ok(Self {
			cwd,
			cargo_home,
			args,
		})
	}

	pub(crate) fn cargo_toml(self, content :&str) -> io::Result<Self> {
		self.file("Cargo.toml", content)
	}

	pub(crate) fn file(self, file_name :&str, content :&str) -> io::Result<Self> {
		let path = self.cwd.path().join(file_name);
		fs::write(path, content)?;
		Ok(self)
	}

	pub(crate) fn dir(self, name :&str) -> io::Result<Self> {
		let path = self.cwd.path().join(name);
		fs::create_dir_all(path)?;
		Ok(self)
	}

	pub(crate) fn arg<S: Into<OsString>>(mut self, arg: S) -> Self {
		self.args.push(arg.into());
		self
	}

	pub(crate) fn run(self) -> CargoResult<(i32, String)> {
		let mut stdout = vec![];
		let stderr = Shell::from_write(Box::new(vec![]));
		let mut config = cargo::Config::new(stderr, self.cwd.path().to_owned(), self.cargo_home.clone());
		let code = match cargo_udeps::run(self.args.clone(), &mut config, &mut stdout) {
			Ok(()) => 0,
			Err(CliError {
				error, exit_code, ..
			}) => match error {
				None => exit_code,
				Some(error) => return Err(error),
			}
		};
		let cwd_lossy = self.cwd.path().to_string_lossy();
		let stdout = str::from_utf8(&stdout)?.replace(&*cwd_lossy, "██████████");
		Ok((code, stdout))
	}
}