#![deny(missing_docs)]
use std::borrow::Borrow;
use std::collections::{HashMap, HashSet};
use std::env;
use std::error;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::io;
use std::path::Path;
use std::process::{Command, Output};
use std::str::{from_utf8, Utf8Error};
use serde_json;
#[derive(Clone, Deserialize, Debug)]
pub struct Metadata {
pub packages: HashSet<Package>,
version: usize,
pub workspace_root: String,
}
#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct PackageRef {
pub name: String,
pub version: Option<String>,
}
#[derive(Clone, Deserialize, Debug)]
pub struct Package {
#[serde(flatten)]
pub name_and_version: PackageRef,
id: String,
source: Option<String>,
pub dependencies: HashSet<Dependency>,
pub targets: Vec<Target>,
features: HashMap<String, Vec<String>>,
pub manifest_path: String,
}
#[derive(Clone, Deserialize, Debug)]
pub struct Dependency {
pub name: String,
source: Option<String>,
pub req: String,
kind: Option<String>,
optional: bool,
uses_default_features: bool,
features: Vec<String>,
pub target: Option<String>,
}
#[derive(Clone, Deserialize, Debug)]
pub struct Target {
pub name: String,
pub kind: Vec<String>,
#[serde(default)]
pub crate_types: Vec<String>,
pub src_path: String,
}
#[derive(Debug)]
pub enum Error {
Io(io::Error),
Metadata(Output),
Utf8(Utf8Error),
Json(serde_json::Error),
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Error::Io(err)
}
}
impl From<Utf8Error> for Error {
fn from(err: Utf8Error) -> Self {
Error::Utf8(err)
}
}
impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Self {
Error::Json(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Io(ref err) => err.fmt(f),
Error::Metadata(_) => write!(f, "Metadata error"),
Error::Utf8(ref err) => err.fmt(f),
Error::Json(ref err) => err.fmt(f),
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::Io(ref err) => Some(err),
Error::Metadata(_) => None,
Error::Utf8(ref err) => Some(err),
Error::Json(ref err) => Some(err),
}
}
}
impl Borrow<PackageRef> for Package {
fn borrow(&self) -> &PackageRef {
&self.name_and_version
}
}
impl Hash for Package {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name_and_version.hash(state);
}
}
impl PartialEq for Package {
fn eq(&self, other: &Self) -> bool {
self.name_and_version == other.name_and_version
}
}
impl Eq for Package {}
impl Borrow<str> for Dependency {
fn borrow(&self) -> &str {
&self.name
}
}
impl Hash for Dependency {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name.hash(state);
}
}
impl PartialEq for Dependency {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}
impl Eq for Dependency {}
pub fn metadata(manifest_path: &Path) -> Result<Metadata, Error> {
let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo"));
let mut cmd = Command::new(cargo);
cmd.arg("metadata");
cmd.arg("--all-features");
cmd.arg("--format-version").arg("1");
cmd.arg("--manifest-path");
cmd.arg(manifest_path.to_str().unwrap());
let output = cmd.output()?;
if !output.status.success() {
return Err(Error::Metadata(output));
}
let stdout = from_utf8(&output.stdout)?;
let meta: Metadata = serde_json::from_str(stdout)?;
Ok(meta)
}