use std::{
env,
ffi::OsStr,
path::PathBuf,
process::{Command, Output},
};
use which::which;
pub mod error;
#[cfg(feature = "json")]
pub mod metadata;
#[cfg(feature = "json")]
pub mod unit_graph;
pub mod version;
pub use error::{ParsingError, Result};
#[cfg(feature = "json")]
pub use metadata::{Features, Metadata, MetadataConfig};
#[cfg(feature = "json")]
pub use unit_graph::UnitGraph;
pub use version::Version;
pub struct Cargo {
path: PathBuf,
frozen: bool,
locked: bool,
offline: bool,
}
impl Cargo {
pub fn new() -> Self {
let path = env::var("CARGO")
.map(PathBuf::from)
.unwrap_or_else(|_| which("cargo").unwrap_or_else(|_| PathBuf::from("cargo")));
Self {
path,
frozen: false,
locked: false,
offline: false,
}
}
pub fn frozen(&mut self, frozen: bool) -> &mut Self {
self.frozen = frozen;
self
}
pub fn locked(&mut self, locked: bool) -> &mut Self {
self.locked = locked;
self
}
pub fn offline(&mut self, offline: bool) -> &mut Self {
self.offline = offline;
self
}
pub fn path<P: Into<PathBuf>>(&mut self, path: P) -> &mut Self {
self.path = path.into();
self
}
pub fn command<I, S>(&self, args: I) -> Command
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let mut command = Command::new(&self.path);
if self.frozen {
command.arg("--frozen");
}
if self.locked {
command.arg("--locked");
}
if self.offline {
command.arg("--offline");
}
command.args(args);
command
}
fn exec(&self, command: &mut Command) -> Result<Vec<u8>> {
let Output {
status,
stdout,
stderr,
} = command.output()?;
if status.success() {
Ok(stdout)
} else {
Err(ParsingError::Exec { stderr })
}
}
pub fn version(&mut self) -> Result<Version> {
let mut command = self.command(&["-Vv"]);
std::str::from_utf8(&self.exec(&mut command)?)?.parse()
}
#[cfg(feature = "json")]
#[doc(hidden)]
pub fn _build(&mut self) -> Result<UnitGraph> {
let mut command = self.command(&["build", "-Zunstable-options", "--unit-graph"]);
let stdout = self.exec(&mut command)?;
Ok(serde_json::from_slice(&stdout)?)
}
#[cfg(feature = "json")]
pub fn metadata(&mut self, config: MetadataConfig) -> Result<Metadata> {
let mut command = self.command(&["metadata", "--format-version", "1"]);
if let Some(features) = &config.features {
match features {
Features::AllFeatures => {
command.arg("--all-features");
}
Features::NoDefaultFeatures => {
command.arg("--no-default-features");
}
Features::SomeFeatures(features) => {
command.arg("--features").arg(features.join(","));
}
}
}
if let Some(filter_platform) = &config.filter_platform {
command
.arg("--filter-platform")
.arg(&filter_platform.to_string());
}
if let Some(manifest_path) = &config.manifest_path {
command.arg("--manifest-path").arg(manifest_path);
}
let stdout = self.exec(&mut command)?;
Ok(serde_json::from_slice(&stdout)?)
}
}
impl Default for Cargo {
fn default() -> Self {
Self::new()
}
}