Repository: oli-obk/cargo_metadata
Pull request #15 (2017-08-22 02:37:04)

Use error_chain; add/fix manifest_path support; run rustfmt; expose extra `Dependency` fields
This PR does a few things:
* Use `error_chain` for errors - #11 
* Add/fix support for providing manifest path
* Run the latest rustfmt-nightly
* Make public the `req` and `kind` fields on `Dependency`

(happy to split up if that would make reviewing easier).


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index ee9cb44..a4238e0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,6 +7,7 @@ description = "structured access to the output of `cargo metadata`"
 license = "MIT"
 
 [dependencies]
+error-chain = "0.10.0"
 serde = "1.0.2"
-serde_json = "1.0.1"
 serde_derive = "1.0.2"
+serde_json = "1.0.1"
diff --git a/src/dependency.rs b/src/dependency.rs
new file mode 100644
index 0000000..2f0f3b2
--- /dev/null
+++ b/src/dependency.rs
@@ -0,0 +1,48 @@
+//! This module contains `Dependency` and the types/functions it uses for deserialization.
+
+use serde::{Deserialize, Deserializer};
+
+#[derive(PartialEq, Clone, Debug, Copy, Deserialize)]
+/// Dependencies can come in three kinds
+pub enum DependencyKind {
+    #[serde(rename = "normal")]
+    /// The 'normal' kind
+    Normal,
+    #[serde(rename = "dev")]
+    /// Those used in tests only
+    Development,
+    #[serde(rename = "build")]
+    /// Those used in build scripts only
+    Build,
+}
+
+impl Default for DependencyKind {
+    fn default() -> DependencyKind {
+        DependencyKind::Normal
+    }
+}
+
+/// The `kind` can be `null`, which is interpreted as the default - `Normal`.
+fn parse_dependency_kind<'de, D>(d: D) -> Result<DependencyKind, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    Deserialize::deserialize(d).map(|x: Option<_>| x.unwrap_or_default())
+}
+
+#[derive(Clone, Deserialize, Debug)]
+/// A dependency of the main crate
+pub struct Dependency {
+    /// Name as given in the `Cargo.toml`
+    pub name: String,
+    source: Option<String>,
+    /// The required version
+    pub req: String,
+    /// The kind of dependency this is
+    #[serde(deserialize_with = "parse_dependency_kind")] pub kind: DependencyKind,
+    /// Whether this is required or optional
+    optional: bool,
+    uses_default_features: bool,
+    features: Vec<String>,
+    target: Option<String>,
+}
diff --git a/src/lib.rs b/src/lib.rs
index f54c244..786d1b8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -4,19 +4,28 @@
 //!
 //! ```rust
 //! # extern crate cargo_metadata;
-//! let manifest_path_arg = std::env::args().skip(2).find(|val| val.starts_with("--manifest-path="));
+//! let manifest_path_arg = std::env::args()
+//!     .skip(2)
+//!     .find(|val| val.starts_with("--manifest-path="));
 //! let metadata = cargo_metadata::metadata(manifest_path_arg.as_ref().map(AsRef::as_ref)).unwrap();
 //! ```
 
+#[macro_use]
+extern crate error_chain;
 extern crate serde;
+#[macro_use]
+extern crate serde_derive;
 extern crate serde_json;
-#[macro_use] extern crate serde_derive;
 
 use std::collections::HashMap;
 use std::env;
 use std::process::Command;
-use std::str::{from_utf8, Utf8Error};
-use std::io;
+use std::str::from_utf8;
+
+pub use errors::{Error, Result};
+pub use dependency::{Dependency, DependencyKind};
+
+mod dependency;
 
 #[derive(Clone, Deserialize, Debug)]
 /// Starting point for metadata returned by `cargo metadata`
@@ -66,21 +75,6 @@ pub struct Package {
     pub manifest_path: String,
 }
 
-#[derive(Clone, Deserialize, Debug)]
-/// A dependency of the main crate
-pub struct Dependency {
-    /// Name as given in the `Cargo.toml`
-    pub name: String,
-    source: Option<String>,
-    /// Whether this is required or optional
-    pub req: String,
-    kind: Option<String>,
-    optional: bool,
-    uses_default_features: bool,
-    features: Vec<String>,
-    target: Option<String>,
-}
-
 #[derive(Clone, Deserialize, Debug)]
 /// A single target (lib, bin, example, ...) provided by a crate
 pub struct Target {
@@ -96,30 +90,17 @@ pub struct Target {
     pub src_path: String,
 }
 
-#[derive(Debug)]
-/// Possible errors that can occur during metadata parsing.
-pub enum Error {
-    /// Error during execution of `cargo metadata`
-    Io(io::Error),
-    /// Output of `cargo metadata` was not valid utf8
-    Utf8(Utf8Error),
-    /// Deserialization error (structure of json did not match expected structure)
-    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)
+mod errors {
+    //! Create the `Error`, `ErrorKind`, `ResultExt`, and `Result` types
+    error_chain!{
+        foreign_links {
+            // Error during execution of `cargo metadata`
+            Io(::std::io::Error);
+            // Output of `cargo metadata` was not valid utf8
+            Utf8(::std::str::Utf8Error);
+            // Deserialization error (structure of json did not match expected structure)
+            Json(::serde_json::Error);
+        }
     }
 }
 
@@ -128,7 +109,7 @@ impl From<serde_json::Error> for Error {
 /// # Parameters
 ///
 /// - `manifest_path_arg`: Path to the manifest.
-pub fn metadata(manifest_path_arg: Option<&str>) -> Result<Metadata, Error> {
+pub fn metadata(manifest_path_arg: Option<&str>) -> Result<Metadata> {
     metadata_deps(manifest_path_arg, false)
 }
 
@@ -138,7 +119,7 @@ pub fn metadata(manifest_path_arg: Option<&str>) -> Result<Metadata, Error> {
 ///
 /// - `manifest_path_arg`: Path to the manifest.
 /// - `deps`: Whether to include dependencies.
-pub fn metadata_deps(manifest_path_arg: Option<&str>, deps: bool) -> Result<Metadata, Error> {
+pub fn metadata_deps(manifest_path_arg: Option<&str>, deps: bool) -> Result<Metadata> {
     let cargo = env::var("CARGO").unwrap_or_else(|_| String::from("cargo"));
     let mut cmd = Command::new(cargo);
     cmd.arg("metadata");
@@ -147,12 +128,12 @@ pub fn metadata_deps(manifest_path_arg: Option<&str>, deps: bool) -> Result<Meta
         cmd.arg("--no-deps");
     }
 
-    cmd.arg("--format-version").arg("1");
-    if let Some(mani) = manifest_path_arg {
-        cmd.arg(mani);
+    cmd.args(&["--format-version", "1"]);
+    if let Some(manifest_path) = manifest_path_arg {
+        cmd.args(&["--manifest-path", manifest_path]);
     }
     let output = cmd.output()?;
     let stdout = from_utf8(&output.stdout)?;
-    let meta: Metadata = serde_json::from_str(stdout)?;
+    let meta = serde_json::from_str(stdout)?;
     Ok(meta)
 }


Test patch:
diff --git a/tests/selftest.rs b/tests/selftest.rs
index 4f7148a..36af327 100644
--- a/tests/selftest.rs
+++ b/tests/selftest.rs
@@ -18,8 +18,12 @@ fn metadata() {
 
 #[test]
 fn metadata_deps() {
-    let metadata = cargo_metadata::metadata_deps(None, true).unwrap();
-    let this = metadata.packages.iter().find(|package| package.name == "cargo_metadata").expect("Did not find ourselves");
+    let metadata = cargo_metadata::metadata_deps(Some("Cargo.toml"), true).unwrap();
+    let this = metadata
+        .packages
+        .iter()
+        .find(|package| package.name == "cargo_metadata")
+        .expect("Did not find ourselves");
 
     assert_eq!(this.name, "cargo_metadata");
     assert_eq!(this.targets.len(), 2);