Repository: oli-obk/cargo_metadata
Pull request #37 (2018-05-31 10:44:48)

Add support for package.metadata table
Since https://github.com/rust-lang/cargo/pull/5360 `cargo metadata` outputs the contents of the [optional `package.metadata` table](https://doc.rust-lang.org/cargo/reference/manifest.html#the-metadata-table-optional). This PR adds support for the table to this crate. Since the table can contain arbitrary content, the return type is a dynamic `serde_json::Map`.

Unresolved questions:

- Is it ok to expose types of the serde_json dependency in the public API? Or do we need to make the `serde_json` dependency public?
- The test doesn't work on stable yet. Is there a way to conditionally execute the test only on nightly and beta?


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 5a2dc8a..9afb7a5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,3 +20,7 @@ version = "0.9"
 [dev-dependencies]
 clap = "2.26.0"
 docopt = "0.8.1"
+
+[package.metadata.cargo_metadata_test]
+some_field = true
+other_field = "foo"
diff --git a/src/lib.rs b/src/lib.rs
index 8f54130..80c9837 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -155,6 +155,33 @@ pub struct Package {
     pub features: HashMap<String, Vec<String>>,
     /// Path containing the `Cargo.toml`
     pub manifest_path: String,
+    /// Contents of the free form package.metadata section
+    ///
+    /// This contents can be serialized to a struct using serde:
+    ///
+    /// ```rust
+    /// #[macro_use]
+    /// extern crate serde_json;
+    /// #[macro_use]
+    /// extern crate serde_derive;
+    ///
+    /// #[derive(Debug, Deserialize)]
+    /// struct SomePackageMetadata {
+    ///     some_value: i32,
+    /// }
+    ///
+    /// fn main() {
+    ///     let value = json!({
+    ///         "some_value": 42,
+    ///     });
+    ///
+    ///     let package_metadata: SomePackageMetadata = serde_json::from_value(value).unwrap();
+    ///     assert_eq!(package_metadata.some_value, 42);
+    /// }
+    ///
+    /// ```
+    #[serde(default)]
+    pub metadata: serde_json::Value,
     #[doc(hidden)]
     #[serde(skip)]
     __do_not_match_exhaustively: (),


Test patch:
diff --git a/tests/selftest.rs b/tests/selftest.rs
index eb30b47..602326c 100644
--- a/tests/selftest.rs
+++ b/tests/selftest.rs
@@ -2,13 +2,23 @@ extern crate cargo_metadata;
 extern crate semver;
 extern crate serde_json;
 
+#[macro_use]
+extern crate serde_derive;
+
 use std::env::current_dir;
 use std::path::Path;
+use std::env;
 
 use semver::Version;
 
 use cargo_metadata::{Error, ErrorKind};
 
+#[derive(Debug, PartialEq, Eq, Deserialize)]
+struct TestPackageMetadata {
+    some_field: bool,
+    other_field: String,
+}
+
 #[test]
 fn metadata() {
     let metadata = cargo_metadata::metadata(None).unwrap();
@@ -28,6 +38,22 @@ fn metadata() {
     assert_eq!(metadata.packages[0].targets[1].name, "selftest");
     assert_eq!(metadata.packages[0].targets[1].kind[0], "test");
     assert_eq!(metadata.packages[0].targets[1].crate_types[0], "bin");
+
+    // Hack until the package metadata field reaches the stable channel (in version 1.27).
+    if env::var("TRAVIS_RUST_VERSION") != Ok("stable".into()) {
+        let package_metadata = &metadata.packages[0].metadata.as_object()
+            .expect("package.metadata must be a table. \
+            NOTE: This test currently only works on the beta and nightly channel.");
+        assert_eq!(package_metadata.len(), 1);
+
+        let value = package_metadata.get("cargo_metadata_test").unwrap();
+        let test_package_metadata: TestPackageMetadata = serde_json::from_value(value.clone())
+            .unwrap();
+        assert_eq!(test_package_metadata, TestPackageMetadata {
+            some_field: true,
+            other_field: "foo".into(),
+        });
+    }
 }
 
 #[test]