From a5a298f1fd5b5ccf03ccb71c0cb6b97867e26d18 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 10 Feb 2017 12:01:52 -0800 Subject: [PATCH] Migrate from rustc-serialize to Serde This commit migrates Cargo as much as possible from rustc-serialize to Serde. This not only provides an excellent testing ground for the toml 0.3 release but it also is a big boost to the speed of parsing the JSON bits of the registry. This doesn't completely excise the dependency just yet as docopt still requires it along with handlebars. I'm sure though that in time those crates will migrate to serde! --- Cargo.lock | 101 +++++++++- Cargo.toml | 6 +- src/bin/cargo.rs | 3 + src/bin/locate_project.rs | 2 +- src/bin/verify_project.rs | 11 +- src/cargo/core/dependency.rs | 30 +-- src/cargo/core/manifest.rs | 78 ++++---- src/cargo/core/package.rs | 12 +- src/cargo/core/package_id.rs | 34 ++-- src/cargo/core/resolver/encode.rs | 33 ++-- src/cargo/core/source.rs | 25 +-- src/cargo/lib.rs | 12 +- src/cargo/ops/cargo_install.rs | 17 +- src/cargo/ops/cargo_output_metadata.rs | 47 ++--- src/cargo/ops/cargo_rustc/fingerprint.rs | 112 +++++------ src/cargo/ops/cargo_rustc/mod.rs | 4 +- src/cargo/ops/lockfile.rs | 40 ++-- src/cargo/sources/directory.rs | 6 +- src/cargo/sources/git/utils.rs | 72 +++---- src/cargo/sources/registry/index.rs | 4 +- src/cargo/sources/registry/mod.rs | 6 +- src/cargo/sources/registry/remote.rs | 4 +- src/cargo/util/config.rs | 19 +- src/cargo/util/errors.rs | 20 +- src/cargo/util/machine_message.rs | 24 +-- src/cargo/util/toml.rs | 229 +++++++++++++---------- src/crates-io/Cargo.toml | 4 +- src/crates-io/lib.rs | 94 ++++------ tests/bad-config.rs | 12 +- tests/build.rs | 57 +++--- tests/cargotest/Cargo.toml | 2 + tests/cargotest/lib.rs | 4 + tests/cargotest/support/mod.rs | 62 +++--- tests/cargotest/support/registry.rs | 35 ++-- tests/registry.rs | 16 +- 35 files changed, 673 insertions(+), 564 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8617962f3f9..e51ec1605f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,11 +28,15 @@ dependencies = [ "psapi-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_ignored 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", "shell-escape 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "tar 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "term 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -86,6 +90,8 @@ dependencies = [ "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", "tar 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "term 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -111,7 +117,9 @@ name = "crates-io" version = "0.7.0" dependencies = [ "curl 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -156,6 +164,11 @@ dependencies = [ "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "dtoa" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "env_logger" version = "0.4.0" @@ -272,6 +285,11 @@ dependencies = [ "unicode-normalization 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "itoa" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "kernel32-sys" version = "0.2.2" @@ -521,6 +539,11 @@ name = "quick-error" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "quote" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "rand" version = "0.3.15" @@ -581,6 +604,48 @@ name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "serde" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_codegen_internals" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "syn 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_codegen_internals 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_ignored" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_json" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "shell-escape" version = "0.1.3" @@ -591,6 +656,15 @@ name = "strsim" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "syn" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "tar" version = "0.4.10" @@ -656,8 +730,13 @@ dependencies = [ name = "toml" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "toml" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -673,6 +752,11 @@ name = "unicode-normalization" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode-xid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unreachable" version = "0.1.1" @@ -745,6 +829,7 @@ dependencies = [ "checksum curl 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "654c5b772f9e62a46a99cc9ee7442e80a139027889a6305cdd59bc1e5eb3e3e8" "checksum curl-sys 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "a89415561199ca2ac6de2519674739159c1583bc0a9b46ec30fd3814999d5ef5" "checksum docopt 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab32ea6e284d87987066f21a9e809a73c14720571ef34516f0890b3d355ccfd8" +"checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90" "checksum env_logger 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "99971fb1b635fe7a0ee3c4d065845bb93cca80a23b5613b5613391ece5de4144" "checksum error-chain 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "318cb3c71ee4cdea69fdc9e15c173b245ed6063e1709029e8fd32525a881120f" "checksum filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "5363ab8e4139b8568a6237db5248646e5a8a2f89bd5ccb02092182b11fd3e922" @@ -758,6 +843,7 @@ dependencies = [ "checksum hamcrest 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bf088f042a467089e9baa4972f57f9247e42a0cc549ba264c7a04fbb8ecb89d4" "checksum handlebars 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b930077f1422bf853008047b55896efc1409744bfc9903f1eec1a58fcc7edeff" "checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11" +"checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6abe0ee2e758cd6bc8a2cd56726359007748fbf4128da998b65d0b70f881e19b" "checksum libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "684f330624d8c3784fb9558ca46c4ce488073a8d22450415c5eb4f4cfb0d11b5" @@ -787,6 +873,7 @@ dependencies = [ "checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" "checksum psapi-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "abcd5d1a07d360e29727f757a9decb3ce8bc6e0efa8969cfaad669a8317a2478" "checksum quick-error 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0aad603e8d7fb67da22dbdf1f4b826ce8829e406124109e73cf1b2454b93a71c" +"checksum quote 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "e7b44fd83db28b83c1c58187159934906e5e955c812e211df413b76b03c909a5" "checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d" "checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f" "checksum regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4278c17d0f6d62dfef0ab00028feb45bd7d2102843f80763474eeb1be8a10c01" @@ -795,8 +882,14 @@ dependencies = [ "checksum rustc-serialize 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "237546c689f20bb44980270c73c3b9edd0891c1be49cc1274406134a66d3957b" "checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0ae9a3c8b07c09dbe43022486d55a18c629a0618d2241e49829aaef9b6d862f9" +"checksum serde_codegen_internals 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c3172bf2940b975c0e4f6ab42a511c0a4407d4f46ccef87a9d3615db5c26fa96" +"checksum serde_derive 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ecc6e0379ca933ece58302d2d3034443f06fbf38fd535857c1dc516195cbc3bf" +"checksum serde_ignored 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4b3f5576874721d14690657e9f0ed286e72a52be2f6fdc0cf2f024182bd8f64" +"checksum serde_json 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e095e4e94e7382b76f48e93bd845ffddda62df8dfd4c163b1bfa93d40e22e13a" "checksum shell-escape 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "dd5cc96481d54583947bfe88bf30c23d53f883c6cd0145368b69989d97b84ef8" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" +"checksum syn 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f94368aae82bb29656c98443a7026ca931a659e8d19dcdc41d6e273054e820" "checksum tar 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "1eb3bf6ec92843ca93f4fcfb5fc6dfe30534815b147885db4b5759b8e2ff7d52" "checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6" "checksum term 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3deff8a2b3b6607d6d7cc32ac25c0b33709453ca9cceac006caac51e963cf94a" @@ -805,8 +898,10 @@ dependencies = [ "checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5" "checksum thread_local 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7793b722f0f77ce716e7f1acf416359ca32ff24d04ffbac4269f44a4a83be05d" "checksum toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "736b60249cb25337bc196faa43ee12c705e426f3d55c214d73a4e7be06f92cb4" +"checksum toml 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08272367dd2e766db3fa38f068067d17aa6a9dfd7259af24b3927db92f1e0c2f" "checksum unicode-bidi 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b61814f3e7fd0e0f15370f767c7c943e08bc2e3214233ae8f88522b334ceb778" "checksum unicode-normalization 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e28fa37426fceeb5cf8f41ee273faa7c82c47dc8fba5853402841e665fcd86ff" +"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91" "checksum url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5ba8a749fb4479b043733416c244fa9d1d3af3d7c23804944651c8a448cb87e" "checksum user32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ef4711d107b21b410a3a974b1204d9accc8b10dad75d8324b5d755de1617d47" diff --git a/Cargo.toml b/Cargo.toml index 99e0f1e6eff..d7120460b50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,11 +35,15 @@ log = "0.3" num_cpus = "1.0" rustc-serialize = "0.3" semver = "0.6.0" +serde = "0.9" +serde_derive = "0.9" +serde_json = "0.9" +serde_ignored = "0.0.2" shell-escape = "0.1" tar = { version = "0.4", default-features = false } tempdir = "0.3" term = "0.4.4" -toml = "0.2" +toml = "0.3" url = "1.1" [target.'cfg(unix)'.dependencies] diff --git a/src/bin/cargo.rs b/src/bin/cargo.rs index 792c9002d1b..0d0171ad2e8 100644 --- a/src/bin/cargo.rs +++ b/src/bin/cargo.rs @@ -6,6 +6,9 @@ extern crate rustc_serialize; extern crate toml; #[macro_use] extern crate log; +#[macro_use] +extern crate serde_derive; +extern crate serde_json; use std::collections::BTreeSet; use std::collections::HashMap; diff --git a/src/bin/locate_project.rs b/src/bin/locate_project.rs index d89f6526920..810be467c0a 100644 --- a/src/bin/locate_project.rs +++ b/src/bin/locate_project.rs @@ -18,7 +18,7 @@ Options: -h, --help Print this message "; -#[derive(RustcEncodable)] +#[derive(Serialize)] pub struct ProjectLocation { root: String } diff --git a/src/bin/verify_project.rs b/src/bin/verify_project.rs index 882873d1f1e..26e11f96644 100644 --- a/src/bin/verify_project.rs +++ b/src/bin/verify_project.rs @@ -6,7 +6,7 @@ use std::process; use cargo; use cargo::util::important_paths::{find_root_manifest_for_wd}; use cargo::util::{CliResult, Config}; -use rustc_serialize::json; +use serde_json; use toml; #[derive(RustcDecodable)] @@ -55,10 +55,9 @@ pub fn execute(args: Flags, config: &Config) -> CliResult { Ok(_) => {}, Err(e) => fail("invalid", &format!("error reading file: {}", e)) }; - match toml::Parser::new(&contents).parse() { - None => fail("invalid", "invalid-format"), - Some(..) => {} - }; + if contents.parse::().is_err() { + fail("invalid", "invalid-format"); + } let mut h = HashMap::new(); h.insert("success".to_string(), "true".to_string()); @@ -69,6 +68,6 @@ pub fn execute(args: Flags, config: &Config) -> CliResult { fn fail(reason: &str, value: &str) -> ! { let mut h = HashMap::new(); h.insert(reason.to_string(), value.to_string()); - println!("{}", json::encode(&h).unwrap()); + println!("{}", serde_json::to_string(&h).unwrap()); process::exit(1) } diff --git a/src/cargo/core/dependency.rs b/src/cargo/core/dependency.rs index 377f21dee06..9401eeccbb0 100644 --- a/src/cargo/core/dependency.rs +++ b/src/cargo/core/dependency.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use semver::VersionReq; use semver::ReqParseError; -use rustc_serialize::{Encoder, Encodable}; +use serde::ser; use core::{SourceId, Summary, PackageId}; use util::{CargoError, CargoResult, Cfg, CfgExpr, ChainError, human, Config}; @@ -41,7 +41,7 @@ pub enum Platform { Cfg(CfgExpr), } -#[derive(RustcEncodable)] +#[derive(Serialize)] struct SerializedDependency<'a> { name: &'a str, source: &'a SourceId, @@ -54,8 +54,10 @@ struct SerializedDependency<'a> { target: Option<&'a Platform>, } -impl Encodable for Dependency { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { +impl ser::Serialize for Dependency { + fn serialize(&self, s: S) -> Result + where S: ser::Serializer, + { SerializedDependency { name: self.name(), source: &self.source_id(), @@ -65,7 +67,7 @@ impl Encodable for Dependency { uses_default_features: self.uses_default_features(), features: self.features(), target: self.platform(), - }.encode(s) + }.serialize(s) } } @@ -76,13 +78,15 @@ pub enum Kind { Build, } -impl Encodable for Kind { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { +impl ser::Serialize for Kind { + fn serialize(&self, s: S) -> Result + where S: ser::Serializer, + { match *self { Kind::Normal => None, Kind::Development => Some("dev"), Kind::Build => Some("build"), - }.encode(s) + }.serialize(s) } } @@ -136,7 +140,7 @@ This will soon become a hard error, so it's either recommended to update to a fixed version or contact the upstream maintainer about this warning. ", - req, inside.name(), inside.version(), requirement); + req, inside.name(), inside.version(), requirement); config.shell().warn(&msg)?; Ok(requirement) @@ -348,9 +352,11 @@ impl Platform { } } -impl Encodable for Platform { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - self.to_string().encode(s) +impl ser::Serialize for Platform { + fn serialize(&self, s: S) -> Result + where S: ser::Serializer, + { + self.to_string().serialize(s) } } diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index 4ab5ea4f9f1..24cfd4d42f4 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -3,7 +3,7 @@ use std::fmt; use std::path::{PathBuf, Path}; use semver::Version; -use rustc_serialize::{Encoder, Encodable}; +use serde::ser; use core::{Dependency, PackageId, Summary, SourceId, PackageIdSpec}; use core::WorkspaceConfig; @@ -113,72 +113,68 @@ pub enum TargetKind { } impl TargetKind { - /// Returns a vector of crate types as specified in a manifest with one difference. - /// For ExampleLib it returns "example" instead of crate types - pub fn kinds(&self) -> Vec<&str> { + /// Returns a vector of crate types as specified in a manifest + pub fn crate_types(&self) -> Vec<&str> { use self::TargetKind::*; match *self { - Lib(ref kinds) => kinds.iter().map(LibKind::crate_type).collect(), + Lib(ref kinds) | ExampleLib(ref kinds) => { + kinds.iter().map(LibKind::crate_type).collect() + } Bin => vec!["bin"], - ExampleBin | ExampleLib(_) => vec!["example"], + ExampleBin => vec!["example"], Test => vec!["test"], CustomBuild => vec!["custom-build"], Bench => vec!["bench"] } } +} - /// Returns a vector of crate types as specified in a manifest - pub fn crate_types(&self) -> Vec<&str> { +impl ser::Serialize for TargetKind { + fn serialize(&self, s: S) -> Result + where S: ser::Serializer, + { use self::TargetKind::*; match *self { - Lib(ref kinds) | ExampleLib(ref kinds) => { - kinds.iter().map(LibKind::crate_type).collect() - } + Lib(ref kinds) => kinds.iter().map(LibKind::crate_type).collect(), Bin => vec!["bin"], - ExampleBin => vec!["example"], + ExampleBin | ExampleLib(_) => vec!["example"], Test => vec!["test"], CustomBuild => vec!["custom-build"], Bench => vec!["bench"] - } + }.serialize(s) } } -#[derive(Clone, PartialEq, Eq, Debug, Hash)] + +// Note that most of the fields here are skipped when serializing because we +// don't want to export them just yet (becomes a public API of Cargo). Others +// though are definitely needed! +#[derive(Clone, PartialEq, Eq, Debug, Hash, Serialize)] pub struct Profile { pub opt_level: String, + #[serde(skip_serializing)] pub lto: bool, + #[serde(skip_serializing)] pub codegen_units: Option, // None = use rustc default + #[serde(skip_serializing)] pub rustc_args: Option>, + #[serde(skip_serializing)] pub rustdoc_args: Option>, pub debuginfo: Option, pub debug_assertions: bool, + #[serde(skip_serializing)] pub rpath: bool, pub test: bool, + #[serde(skip_serializing)] pub doc: bool, + #[serde(skip_serializing)] pub run_custom_build: bool, + #[serde(skip_serializing)] pub check: bool, + #[serde(skip_serializing)] pub panic: Option, } -#[derive(RustcEncodable)] -struct SerializedProfile<'a> { - opt_level: &'a str, - debuginfo: Option, - debug_assertions: bool, - test: bool, -} - -impl Encodable for Profile { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - SerializedProfile { - opt_level: &self.opt_level, - debuginfo: self.debuginfo, - debug_assertions: self.debug_assertions, - test: self.test, - }.encode(s) - } -} - #[derive(Default, Clone, Debug, PartialEq, Eq)] pub struct Profiles { pub release: Profile, @@ -209,22 +205,22 @@ pub struct Target { for_host: bool, } -#[derive(RustcEncodable)] +#[derive(Serialize)] struct SerializedTarget<'a> { - kind: Vec<&'a str>, + kind: &'a TargetKind, crate_types: Vec<&'a str>, name: &'a str, - src_path: &'a str, + src_path: &'a PathBuf, } -impl Encodable for Target { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { +impl ser::Serialize for Target { + fn serialize(&self, s: S) -> Result { SerializedTarget { - kind: self.kind.kinds(), + kind: &self.kind, crate_types: self.kind.crate_types(), name: &self.name, - src_path: &self.src_path.display().to_string(), - }.encode(s) + src_path: &self.src_path, + }.serialize(s) } } diff --git a/src/cargo/core/package.rs b/src/cargo/core/package.rs index 6b4cd2835d9..898745460f9 100644 --- a/src/cargo/core/package.rs +++ b/src/cargo/core/package.rs @@ -5,12 +5,12 @@ use std::hash; use std::path::{Path, PathBuf}; use semver::Version; +use serde::ser; use core::{Dependency, Manifest, PackageId, SourceId, Target}; use core::{Summary, SourceMap}; use ops; use util::{CargoResult, Config, LazyCell, ChainError, internal, human, lev_distance}; -use rustc_serialize::{Encoder,Encodable}; /// Information about a package that is available somewhere in the file system. /// @@ -24,7 +24,7 @@ pub struct Package { manifest_path: PathBuf, } -#[derive(RustcEncodable)] +#[derive(Serialize)] struct SerializedPackage<'a> { name: &'a str, version: &'a str, @@ -39,8 +39,10 @@ struct SerializedPackage<'a> { manifest_path: &'a str, } -impl Encodable for Package { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { +impl ser::Serialize for Package { + fn serialize(&self, s: S) -> Result + where S: ser::Serializer, + { let summary = self.manifest.summary(); let package_id = summary.package_id(); let manmeta = self.manifest.metadata(); @@ -60,7 +62,7 @@ impl Encodable for Package { targets: &self.manifest.targets(), features: summary.features(), manifest_path: &self.manifest_path.display().to_string(), - }.encode(s) + }.serialize(s) } } diff --git a/src/cargo/core/package_id.rs b/src/cargo/core/package_id.rs index 12e6fae78ff..e2bd95c081a 100644 --- a/src/cargo/core/package_id.rs +++ b/src/cargo/core/package_id.rs @@ -5,8 +5,9 @@ use std::hash::Hash; use std::hash; use std::sync::Arc; -use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; use semver; +use serde::de; +use serde::ser; use util::{CargoResult, CargoError, ToSemver}; use core::source::SourceId; @@ -24,40 +25,41 @@ struct PackageIdInner { source_id: SourceId, } -impl Encodable for PackageId { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { +impl ser::Serialize for PackageId { + fn serialize(&self, s: S) -> Result + where S: ser::Serializer + { let source = self.inner.source_id.to_url(); let encoded = format!("{} {} ({})", self.inner.name, self.inner.version, source); - encoded.encode(s) + encoded.serialize(s) } } -impl Decodable for PackageId { - fn decode(d: &mut D) -> Result { - let string: String = Decodable::decode(d)?; +impl de::Deserialize for PackageId { + fn deserialize(d: D) -> Result + where D: de::Deserializer + { + let string = String::deserialize(d)?; let mut s = string.splitn(3, ' '); let name = s.next().unwrap(); let version = match s.next() { Some(s) => s, - None => return Err(d.error("invalid serialized PackageId")), + None => return Err(de::Error::custom("invalid serialized PackageId")), }; - let version = semver::Version::parse(version).map_err(|_| { - d.error("invalid version") - })?; + let version = semver::Version::parse(version) + .map_err(de::Error::custom)?; let url = match s.next() { Some(s) => s, - None => return Err(d.error("invalid serialized PackageId")), + None => return Err(de::Error::custom("invalid serialized PackageId")), }; let url = if url.starts_with("(") && url.ends_with(")") { &url[1..url.len() - 1] } else { - return Err(d.error("invalid serialized PackageId")) + return Err(de::Error::custom("invalid serialized PackageId")) }; - let source_id = SourceId::from_url(url).map_err(|e| { - d.error(&e.to_string()) - })?; + let source_id = SourceId::from_url(url).map_err(de::Error::custom)?; Ok(PackageId { inner: Arc::new(PackageIdInner { diff --git a/src/cargo/core/resolver/encode.rs b/src/cargo/core/resolver/encode.rs index 1971801bcab..801fc474084 100644 --- a/src/cargo/core/resolver/encode.rs +++ b/src/cargo/core/resolver/encode.rs @@ -2,14 +2,15 @@ use std::collections::{HashMap, HashSet, BTreeMap}; use std::fmt; use std::str::FromStr; -use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; +use serde::ser; +use serde::de; use core::{Package, PackageId, SourceId, Workspace}; use util::{CargoResult, Graph, Config, internal, ChainError, CargoError}; use super::Resolve; -#[derive(RustcEncodable, RustcDecodable, Debug)] +#[derive(Serialize, Deserialize, Debug)] pub struct EncodableResolve { package: Option>, /// `root` is optional to allow forward compatibility. @@ -206,7 +207,7 @@ fn build_path_deps(ws: &Workspace) -> HashMap { } } -#[derive(RustcEncodable, RustcDecodable, Debug, PartialOrd, Ord, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, PartialOrd, Ord, PartialEq, Eq)] pub struct EncodableDependency { name: String, version: String, @@ -260,17 +261,21 @@ impl FromStr for EncodablePackageId { } } -impl Encodable for EncodablePackageId { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - self.to_string().encode(s) +impl ser::Serialize for EncodablePackageId { + fn serialize(&self, s: S) -> Result + where S: ser::Serializer, + { + self.to_string().serialize(s) } } -impl Decodable for EncodablePackageId { - fn decode(d: &mut D) -> Result { - String::decode(d).and_then(|string| { +impl de::Deserialize for EncodablePackageId { + fn deserialize(d: D) -> Result + where D: de::Deserializer, + { + String::deserialize(d).and_then(|string| { string.parse::() - .map_err(|e| d.error(&e.to_string())) + .map_err(de::Error::custom) }) } } @@ -281,8 +286,10 @@ pub struct WorkspaceResolve<'a, 'cfg: 'a> { pub use_root_key: bool, } -impl<'a, 'cfg> Encodable for WorkspaceResolve<'a, 'cfg> { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { +impl<'a, 'cfg> ser::Serialize for WorkspaceResolve<'a, 'cfg> { + fn serialize(&self, s: S) -> Result + where S: ser::Serializer, + { let mut ids: Vec<&PackageId> = self.resolve.graph.iter().collect(); ids.sort(); @@ -320,7 +327,7 @@ impl<'a, 'cfg> Encodable for WorkspaceResolve<'a, 'cfg> { package: Some(encodable), root: root, metadata: metadata, - }.encode(s) + }.serialize(s) } } diff --git a/src/cargo/core/source.rs b/src/cargo/core/source.rs index dc334535f73..142adfdb69d 100644 --- a/src/cargo/core/source.rs +++ b/src/cargo/core/source.rs @@ -7,7 +7,8 @@ use std::sync::Arc; use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT}; use std::sync::atomic::Ordering::SeqCst; -use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; +use serde::ser; +use serde::de; use url::Url; use core::{Package, PackageId, Registry}; @@ -342,22 +343,24 @@ impl Ord for SourceId { } } -impl Encodable for SourceId { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { +impl ser::Serialize for SourceId { + fn serialize(&self, s: S) -> Result + where S: ser::Serializer, + { if self.is_path() { - s.emit_option_none() + None::.serialize(s) } else { - self.to_url().encode(s) + Some(self.to_url()).serialize(s) } } } -impl Decodable for SourceId { - fn decode(d: &mut D) -> Result { - let string: String = Decodable::decode(d)?; - SourceId::from_url(&string).map_err(|e| { - d.error(&e.to_string()) - }) +impl de::Deserialize for SourceId { + fn deserialize(d: D) -> Result + where D: de::Deserializer, + { + let string = String::deserialize(d)?; + SourceId::from_url(&string).map_err(de::Error::custom) } } diff --git a/src/cargo/lib.rs b/src/cargo/lib.rs index f63cd57cd5c..a844359e746 100755 --- a/src/cargo/lib.rs +++ b/src/cargo/lib.rs @@ -3,6 +3,8 @@ #[cfg(test)] extern crate hamcrest; #[macro_use] extern crate log; +#[macro_use] extern crate serde_derive; +#[macro_use] extern crate serde_json; extern crate crates_io as registry; extern crate crossbeam; extern crate curl; @@ -18,6 +20,8 @@ extern crate libgit2_sys; extern crate num_cpus; extern crate rustc_serialize; extern crate semver; +extern crate serde; +extern crate serde_ignored; extern crate shell_escape; extern crate tar; extern crate tempdir; @@ -27,8 +31,8 @@ extern crate url; use std::io; use std::fmt; -use rustc_serialize::{Decodable, Encodable}; -use rustc_serialize::json; +use rustc_serialize::Decodable; +use serde::ser; use docopt::Docopt; use core::{Shell, MultiShell, ShellConfig, Verbosity, ColorConfig}; @@ -121,8 +125,8 @@ pub fn call_main_without_stdin( exec(flags, config) } -pub fn print_json(obj: &T) { - let encoded = json::encode(&obj).unwrap(); +pub fn print_json(obj: &T) { + let encoded = serde_json::to_string(&obj).unwrap(); println!("{}", encoded); } diff --git a/src/cargo/ops/cargo_install.rs b/src/cargo/ops/cargo_install.rs index 02daac2ab6d..5ad84e376ff 100644 --- a/src/cargo/ops/cargo_install.rs +++ b/src/cargo/ops/cargo_install.rs @@ -19,13 +19,18 @@ use sources::{GitSource, PathSource, SourceConfigMap}; use util::{CargoResult, ChainError, Config, human, internal}; use util::{Filesystem, FileLock}; -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Deserialize, Serialize)] +#[serde(untagged)] enum CrateListing { V1(CrateListingV1), - Empty, + Empty(Empty), } -#[derive(RustcDecodable, RustcEncodable)] +#[derive(Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +struct Empty {} + +#[derive(Deserialize, Serialize)] struct CrateListingV1 { v1: BTreeMap>, } @@ -412,12 +417,12 @@ fn read_crate_list(mut file: &File) -> CargoResult { (|| -> CargoResult<_> { let mut contents = String::new(); file.read_to_string(&mut contents)?; - let listing = toml::decode_str(&contents).chain_error(|| { + let listing = toml::from_str(&contents).chain_error(|| { internal("invalid TOML found for metadata") })?; match listing { CrateListing::V1(v1) => Ok(v1), - CrateListing::Empty => { + CrateListing::Empty(_) => { Ok(CrateListingV1 { v1: BTreeMap::new() }) } } @@ -430,7 +435,7 @@ fn write_crate_list(mut file: &File, listing: CrateListingV1) -> CargoResult<()> (|| -> CargoResult<_> { file.seek(SeekFrom::Start(0))?; file.set_len(0)?; - let data = toml::encode_str::(&CrateListing::V1(listing)); + let data = toml::to_string(&CrateListing::V1(listing))?; file.write_all(data.as_bytes())?; Ok(()) }).chain_error(|| { diff --git a/src/cargo/ops/cargo_output_metadata.rs b/src/cargo/ops/cargo_output_metadata.rs index e86a4de4189..bd48cb0a4b9 100644 --- a/src/cargo/ops/cargo_output_metadata.rs +++ b/src/cargo/ops/cargo_output_metadata.rs @@ -1,4 +1,4 @@ -use rustc_serialize::{Encodable, Encoder}; +use serde::ser::{self, Serialize}; use core::resolver::Resolve; use core::{Package, PackageId, Workspace}; @@ -67,7 +67,7 @@ fn metadata_full(ws: &Workspace, }) } -#[derive(RustcEncodable)] +#[derive(Serialize)] pub struct ExportInfo { packages: Vec, workspace_members: Vec, @@ -75,38 +75,29 @@ pub struct ExportInfo { version: u32, } -/// Newtype wrapper to provide a custom `Encodable` implementation. +/// Newtype wrapper to provide a custom `Serialize` implementation. /// The one from lockfile does not fit because it uses a non-standard /// format for `PackageId`s -struct MetadataResolve{ +#[derive(Serialize)] +struct MetadataResolve { + #[serde(rename = "nodes", serialize_with = "serialize_resolve")] resolve: Resolve, root: Option, } -impl Encodable for MetadataResolve { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - #[derive(RustcEncodable)] - struct EncodableResolve<'a> { - root: Option<&'a PackageId>, - nodes: Vec>, - } +fn serialize_resolve(resolve: &Resolve, s: S) -> Result + where S: ser::Serializer, +{ + #[derive(Serialize)] + struct Node<'a> { + id: &'a PackageId, + dependencies: Vec<&'a PackageId>, + } - #[derive(RustcEncodable)] - struct Node<'a> { - id: &'a PackageId, - dependencies: Vec<&'a PackageId>, + resolve.iter().map(|id| { + Node { + id: id, + dependencies: resolve.deps(id).collect(), } - - let encodable = EncodableResolve { - root: self.root.as_ref(), - nodes: self.resolve.iter().map(|id| { - Node { - id: id, - dependencies: self.resolve.deps(id).collect(), - } - }).collect(), - }; - - encodable.encode(s) - } + }).collect::>().serialize(s) } diff --git a/src/cargo/ops/cargo_rustc/fingerprint.rs b/src/cargo/ops/cargo_rustc/fingerprint.rs index 9ada0fb748d..a3af64f4570 100644 --- a/src/cargo/ops/cargo_rustc/fingerprint.rs +++ b/src/cargo/ops/cargo_rustc/fingerprint.rs @@ -6,7 +6,9 @@ use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; use filetime::FileTime; -use rustc_serialize::{json, Encodable, Decodable, Encoder, Decoder}; +use serde::ser::{self, Serialize}; +use serde::de::{self, Deserialize}; +use serde_json; use core::{Package, TargetKind}; use util; @@ -125,18 +127,48 @@ pub fn prepare_target<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, /// `DependencyQueue`, but it also needs to be retained here because Cargo can /// be interrupted while executing, losing the state of the `DependencyQueue` /// graph. +#[derive(Serialize, Deserialize)] pub struct Fingerprint { rustc: u64, features: String, target: u64, profile: u64, + #[serde(serialize_with = "serialize_deps", deserialize_with = "deserialize_deps")] deps: Vec<(String, Arc)>, local: LocalFingerprint, + #[serde(skip_serializing, skip_deserializing)] memoized_hash: Mutex>, rustflags: Vec, } -#[derive(RustcEncodable, RustcDecodable, Hash)] +fn serialize_deps(deps: &Vec<(String, Arc)>, ser: S) + -> Result + where S: ser::Serializer, +{ + deps.iter().map(|&(ref a, ref b)| { + (a, b.hash()) + }).collect::>().serialize(ser) +} + +fn deserialize_deps(d: D) -> Result)>, D::Error> + where D: de::Deserializer, +{ + let decoded = >::deserialize(d)?; + Ok(decoded.into_iter().map(|(name, hash)| { + (name, Arc::new(Fingerprint { + rustc: 0, + target: 0, + profile: 0, + local: LocalFingerprint::Precalculated(String::new()), + features: String::new(), + deps: Vec::new(), + memoized_hash: Mutex::new(Some(hash)), + rustflags: Vec::new(), + })) + }).collect()) +} + +#[derive(Serialize, Deserialize, Hash)] enum LocalFingerprint { Precalculated(String), MtimeBased(MtimeSlot, PathBuf), @@ -242,79 +274,27 @@ impl hash::Hash for Fingerprint { } } -impl Encodable for Fingerprint { - fn encode(&self, e: &mut E) -> Result<(), E::Error> { - e.emit_struct("Fingerprint", 6, |e| { - e.emit_struct_field("rustc", 0, |e| self.rustc.encode(e))?; - e.emit_struct_field("target", 1, |e| self.target.encode(e))?; - e.emit_struct_field("profile", 2, |e| self.profile.encode(e))?; - e.emit_struct_field("local", 3, |e| self.local.encode(e))?; - e.emit_struct_field("features", 4, |e| { - self.features.encode(e) - })?; - e.emit_struct_field("deps", 5, |e| { - self.deps.iter().map(|&(ref a, ref b)| { - (a, b.hash()) - }).collect::>().encode(e) - })?; - e.emit_struct_field("rustflags", 6, |e| self.rustflags.encode(e))?; - Ok(()) - }) - } -} - -impl Decodable for Fingerprint { - fn decode(d: &mut D) -> Result { - fn decode(d: &mut D) -> Result { - Decodable::decode(d) - } - d.read_struct("Fingerprint", 6, |d| { - Ok(Fingerprint { - rustc: d.read_struct_field("rustc", 0, decode)?, - target: d.read_struct_field("target", 1, decode)?, - profile: d.read_struct_field("profile", 2, decode)?, - local: d.read_struct_field("local", 3, decode)?, - features: d.read_struct_field("features", 4, decode)?, - memoized_hash: Mutex::new(None), - deps: { - let decode = decode::, D>; - let v = d.read_struct_field("deps", 5, decode)?; - v.into_iter().map(|(name, hash)| { - (name, Arc::new(Fingerprint { - rustc: 0, - target: 0, - profile: 0, - local: LocalFingerprint::Precalculated(String::new()), - features: String::new(), - deps: Vec::new(), - memoized_hash: Mutex::new(Some(hash)), - rustflags: Vec::new(), - })) - }).collect() - }, - rustflags: d.read_struct_field("rustflags", 6, decode)?, - }) - }) - } -} - impl hash::Hash for MtimeSlot { fn hash(&self, h: &mut H) { self.0.lock().unwrap().hash(h) } } -impl Encodable for MtimeSlot { - fn encode(&self, e: &mut E) -> Result<(), E::Error> { +impl ser::Serialize for MtimeSlot { + fn serialize(&self, s: S) -> Result + where S: ser::Serializer, + { self.0.lock().unwrap().map(|ft| { (ft.seconds_relative_to_1970(), ft.nanoseconds()) - }).encode(e) + }).serialize(s) } } -impl Decodable for MtimeSlot { - fn decode(e: &mut D) -> Result { - let kind: Option<(u64, u32)> = Decodable::decode(e)?; +impl de::Deserialize for MtimeSlot { + fn deserialize(d: D) -> Result + where D: de::Deserializer, + { + let kind: Option<(u64, u32)> = de::Deserialize::deserialize(d)?; Ok(MtimeSlot(Mutex::new(kind.map(|(s, n)| { FileTime::from_seconds_since_1970(s, n) })))) @@ -507,7 +487,7 @@ fn write_fingerprint(loc: &Path, fingerprint: &Fingerprint) -> CargoResult<()> { debug!("write fingerprint: {}", loc.display()); paths::write(&loc, util::to_hex(hash).as_bytes())?; paths::write(&loc.with_extension("json"), - json::encode(&fingerprint).unwrap().as_bytes())?; + &serde_json::to_vec(&fingerprint).unwrap())?; Ok(()) } @@ -536,7 +516,7 @@ fn compare_old_fingerprint(loc: &Path, new_fingerprint: &Fingerprint) } let old_fingerprint_json = paths::read(&loc.with_extension("json"))?; - let old_fingerprint = json::decode(&old_fingerprint_json).chain_error(|| { + let old_fingerprint = serde_json::from_str(&old_fingerprint_json).chain_error(|| { internal(format!("failed to deserialize json")) })?; new_fingerprint.compare(&old_fingerprint) diff --git a/src/cargo/ops/cargo_rustc/mod.rs b/src/cargo/ops/cargo_rustc/mod.rs index a8cfa13b72d..b8d6f07d2c3 100644 --- a/src/cargo/ops/cargo_rustc/mod.rs +++ b/src/cargo/ops/cargo_rustc/mod.rs @@ -6,7 +6,7 @@ use std::io::{self, Write}; use std::path::{self, PathBuf}; use std::sync::Arc; -use rustc_serialize::json; +use serde_json; use core::{Package, PackageId, PackageSet, Target, Resolve}; use core::{Profile, Profiles, Workspace}; @@ -346,7 +346,7 @@ fn rustc(cx: &mut Context, unit: &Unit, exec: Arc) -> CargoResult CargoResult> { })?; (|| { - let table = cargo_toml::parse(&s, f.path(), ws.config())?; - let table = toml::Value::Table(table); - let mut d = toml::Decoder::new(table); - let v: resolver::EncodableResolve = Decodable::decode(&mut d)?; + let resolve = cargo_toml::parse(&s, f.path(), ws.config())?; + let v: resolver::EncodableResolve = resolve.try_into()?; Ok(Some(v.into_resolve(ws)?)) }).chain_error(|| { human(format!("failed to parse lock file at: {}", f.path().display())) @@ -50,24 +47,23 @@ pub fn write_pkg_lockfile(ws: &Workspace, resolve: &Resolve) -> CargoResult<()> true }; - let mut e = Encoder::new(); - WorkspaceResolve { + let toml = toml::Value::try_from(WorkspaceResolve { ws: ws, resolve: resolve, use_root_key: use_root_key, - }.encode(&mut e).unwrap(); + }).unwrap(); let mut out = String::new(); // Note that we do not use e.toml.to_string() as we want to control the // exact format the toml is in to ensure pretty diffs between updates to the // lockfile. - if let Some(root) = e.toml.get(&"root".to_string()) { + if let Some(root) = toml.get("root") { out.push_str("[root]\n"); emit_package(root.as_table().unwrap(), &mut out); } - let deps = e.toml[&"package".to_string()].as_slice().unwrap(); + let deps = toml["package"].as_array().unwrap(); for dep in deps.iter() { let dep = dep.as_table().unwrap(); @@ -75,9 +71,9 @@ pub fn write_pkg_lockfile(ws: &Workspace, resolve: &Resolve) -> CargoResult<()> emit_package(dep, &mut out); } - if let Some(metadata) = e.toml.get(&"metadata".to_string()) { + if let Some(meta) = toml.get("metadata") { out.push_str("[metadata]\n"); - out.push_str(&metadata.to_string()); + out.push_str(&meta.to_string()); } // If the lockfile contents haven't changed so don't rewrite it. This is @@ -117,16 +113,16 @@ fn has_crlf_line_endings(s: &str) -> bool { } } -fn emit_package(dep: &toml::Table, out: &mut String) { - out.push_str(&format!("name = {}\n", lookup(dep, "name"))); - out.push_str(&format!("version = {}\n", lookup(dep, "version"))); +fn emit_package(dep: &toml::value::Table, out: &mut String) { + out.push_str(&format!("name = {}\n", &dep["name"])); + out.push_str(&format!("version = {}\n", &dep["version"])); if dep.contains_key("source") { - out.push_str(&format!("source = {}\n", lookup(dep, "source"))); + out.push_str(&format!("source = {}\n", &dep["source"])); } - if let Some(s) = dep.get("dependencies") { - let slice = Value::as_slice(s).unwrap(); + if let Some(ref s) = dep.get("dependencies") { + let slice = s.as_array().unwrap(); if !slice.is_empty() { out.push_str("dependencies = [\n"); @@ -139,10 +135,6 @@ fn emit_package(dep: &toml::Table, out: &mut String) { } out.push_str("\n"); } else if dep.contains_key("replace") { - out.push_str(&format!("replace = {}\n\n", lookup(dep, "replace"))); + out.push_str(&format!("replace = {}\n\n", &dep["replace"])); } } - -fn lookup<'a>(table: &'a toml::Table, key: &str) -> &'a toml::Value { - table.get(key).expect(&format!("didn't find {}", key)) -} diff --git a/src/cargo/sources/directory.rs b/src/cargo/sources/directory.rs index c55fc3669a7..1cf8b27550b 100644 --- a/src/cargo/sources/directory.rs +++ b/src/cargo/sources/directory.rs @@ -5,7 +5,7 @@ use std::io::Read; use std::path::{Path, PathBuf}; use rustc_serialize::hex::ToHex; -use rustc_serialize::json; +use serde_json; use core::{Package, PackageId, Summary, SourceId, Source, Dependency, Registry}; use sources::PathSource; @@ -19,7 +19,7 @@ pub struct DirectorySource<'cfg> { config: &'cfg Config, } -#[derive(RustcDecodable)] +#[derive(Deserialize)] struct Checksum { package: String, files: HashMap, @@ -93,7 +93,7 @@ impl<'cfg> Source for DirectorySource<'cfg> { pkg.package_id().version())) })?; - let cksum: Checksum = json::decode(&cksum).chain_error(|| { + let cksum: Checksum = serde_json::from_str(&cksum).chain_error(|| { human(format!("failed to decode `.cargo-checksum.json` of \ {} v{}", pkg.package_id().name(), diff --git a/src/cargo/sources/git/utils.rs b/src/cargo/sources/git/utils.rs index 877752cead4..97c43118c34 100644 --- a/src/cargo/sources/git/utils.rs +++ b/src/cargo/sources/git/utils.rs @@ -3,9 +3,9 @@ use std::fmt; use std::fs::{self, File}; use std::path::{Path, PathBuf}; -use rustc_serialize::{Encodable, Encoder}; -use url::Url; use git2::{self, ObjectType}; +use serde::ser::{self, Serialize}; +use url::Url; use core::GitReference; use util::{CargoResult, ChainError, human, ToUrl, internal, Config, network}; @@ -13,6 +13,19 @@ use util::{CargoResult, ChainError, human, ToUrl, internal, Config, network}; #[derive(PartialEq, Clone, Debug)] pub struct GitRevision(git2::Oid); +impl ser::Serialize for GitRevision { + fn serialize(&self, s: S) -> Result { + serialize_str(self, s) + } +} + +fn serialize_str(t: &T, s: S) -> Result + where T: fmt::Display, + S: ser::Serializer, +{ + t.to_string().serialize(s) +} + impl fmt::Display for GitRevision { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) @@ -29,77 +42,34 @@ impl GitShortID { /// GitRemote represents a remote repository. It gets cloned into a local /// GitDatabase. -#[derive(PartialEq,Clone,Debug)] +#[derive(PartialEq, Clone, Debug, Serialize)] pub struct GitRemote { + #[serde(serialize_with = "serialize_str")] url: Url, } -#[derive(PartialEq,Clone,RustcEncodable)] -struct EncodableGitRemote { - url: String, -} - -impl Encodable for GitRemote { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - EncodableGitRemote { - url: self.url.to_string() - }.encode(s) - } -} - /// GitDatabase is a local clone of a remote repository's database. Multiple /// GitCheckouts can be cloned from this GitDatabase. +#[derive(Serialize)] pub struct GitDatabase { remote: GitRemote, path: PathBuf, + #[serde(skip_serializing)] repo: git2::Repository, } -#[derive(RustcEncodable)] -pub struct EncodableGitDatabase { - remote: GitRemote, - path: String, -} - -impl Encodable for GitDatabase { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - EncodableGitDatabase { - remote: self.remote.clone(), - path: self.path.display().to_string() - }.encode(s) - } -} - /// GitCheckout is a local checkout of a particular revision. Calling /// `clone_into` with a reference will resolve the reference into a revision, /// and return a CargoError if no revision for that reference was found. +#[derive(Serialize)] pub struct GitCheckout<'a> { database: &'a GitDatabase, location: PathBuf, revision: GitRevision, + #[serde(skip_serializing)] repo: git2::Repository, } -#[derive(RustcEncodable)] -pub struct EncodableGitCheckout { - database: EncodableGitDatabase, - location: String, - revision: String, -} - -impl<'a> Encodable for GitCheckout<'a> { - fn encode(&self, s: &mut S) -> Result<(), S::Error> { - EncodableGitCheckout { - location: self.location.display().to_string(), - revision: self.revision.to_string(), - database: EncodableGitDatabase { - remote: self.database.remote.clone(), - path: self.database.path.display().to_string(), - }, - }.encode(s) - } -} - // Implementations impl GitRemote { diff --git a/src/cargo/sources/registry/index.rs b/src/cargo/sources/registry/index.rs index fffdd5702c8..bbd7e226ca7 100644 --- a/src/cargo/sources/registry/index.rs +++ b/src/cargo/sources/registry/index.rs @@ -3,7 +3,7 @@ use std::io::prelude::*; use std::fs::File; use std::path::Path; -use rustc_serialize::json; +use serde_json; use core::dependency::{Dependency, DependencyInner, Kind}; use core::{SourceId, Summary, PackageId, Registry}; @@ -116,7 +116,7 @@ impl<'cfg> RegistryIndex<'cfg> { -> CargoResult<(Summary, bool)> { let RegistryPackage { name, vers, cksum, deps, features, yanked - } = json::decode::(line)?; + } = serde_json::from_str::(line)?; let pkgid = PackageId::new(&name, &vers, &self.source_id)?; let deps: CargoResult> = deps.into_iter().map(|dep| { self.parse_registry_dependency(dep) diff --git a/src/cargo/sources/registry/mod.rs b/src/cargo/sources/registry/mod.rs index 0f188a00870..2044c30ebfe 100644 --- a/src/cargo/sources/registry/mod.rs +++ b/src/cargo/sources/registry/mod.rs @@ -184,7 +184,7 @@ pub struct RegistrySource<'cfg> { index_locked: bool, } -#[derive(RustcDecodable)] +#[derive(Deserialize)] pub struct RegistryConfig { /// Download endpoint for all crates. This will be appended with /// `///download` and then will be hit with an HTTP GET @@ -196,7 +196,7 @@ pub struct RegistryConfig { pub api: String, } -#[derive(RustcDecodable)] +#[derive(Deserialize)] struct RegistryPackage { name: String, vers: String, @@ -206,7 +206,7 @@ struct RegistryPackage { yanked: Option, } -#[derive(RustcDecodable)] +#[derive(Deserialize)] struct RegistryDependency { name: String, req: String, diff --git a/src/cargo/sources/registry/remote.rs b/src/cargo/sources/registry/remote.rs index e3c9b9c7b41..67e60a074c9 100644 --- a/src/cargo/sources/registry/remote.rs +++ b/src/cargo/sources/registry/remote.rs @@ -5,7 +5,7 @@ use std::path::Path; use curl::easy::{Easy, List}; use git2; use rustc_serialize::hex::ToHex; -use rustc_serialize::json; +use serde_json; use url::Url; use core::{PackageId, SourceId}; @@ -49,7 +49,7 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> { "the registry index")?; let path = lock.path().parent().unwrap(); let contents = paths::read(&path.join("config.json"))?; - let config = json::decode(&contents)?; + let config = serde_json::from_str(&contents)?; Ok(Some(config)) } diff --git a/src/cargo/util/config.rs b/src/cargo/util/config.rs index abee2381713..08af70451c8 100644 --- a/src/cargo/util/config.rs +++ b/src/cargo/util/config.rs @@ -372,14 +372,13 @@ impl Config { walk_tree(&self.cwd, |mut file, path| { let mut contents = String::new(); file.read_to_string(&mut contents)?; - let table = cargo_toml::parse(&contents, - path, - self).chain_error(|| { + let toml = cargo_toml::parse(&contents, + &path, + self).chain_error(|| { human(format!("could not parse TOML configuration in `{}`", path.display())) })?; - let toml = toml::Value::Table(table); - let value = CV::from_toml(path, toml).chain_error(|| { + let value = CV::from_toml(&path, toml).chain_error(|| { human(format!("failed to load TOML configuration from `{}`", path.display())) })?; @@ -409,13 +408,13 @@ impl Config { } } -#[derive(Eq, PartialEq, Clone, RustcEncodable, RustcDecodable, Copy)] +#[derive(Eq, PartialEq, Clone, Copy)] pub enum Location { Project, Global } -#[derive(Eq,PartialEq,Clone,RustcDecodable)] +#[derive(Eq,PartialEq,Clone,Deserialize)] pub enum ConfigValue { Integer(i64, PathBuf), String(String, PathBuf), @@ -743,9 +742,11 @@ pub fn set_config(cfg: &Config, let mut contents = String::new(); let _ = file.read_to_string(&mut contents); let mut toml = cargo_toml::parse(&contents, file.path(), cfg)?; - toml.insert(key.to_string(), value.into_toml()); + toml.as_table_mut() + .unwrap() + .insert(key.to_string(), value.into_toml()); - let contents = toml::Value::Table(toml).to_string(); + let contents = toml.to_string(); file.seek(SeekFrom::Start(0))?; file.write_all(contents.as_bytes())?; file.file().set_len(contents.len() as u64)?; diff --git a/src/cargo/util/errors.rs b/src/cargo/util/errors.rs index c7babd0b77f..14697ad8ea8 100644 --- a/src/cargo/util/errors.rs +++ b/src/cargo/util/errors.rs @@ -11,8 +11,8 @@ use std::string; use curl; use git2; use handlebars; -use rustc_serialize::json; use semver; +use serde_json; use term; use toml; use url; @@ -334,13 +334,12 @@ from_error! { io::Error, ProcessError, git2::Error, - json::DecoderError, - json::EncoderError, + serde_json::Error, curl::Error, CliError, - toml::Error, url::ParseError, - toml::DecodeError, + toml::ser::Error, + toml::de::Error, ffi::NulError, term::Error, num::ParseIntError, @@ -363,14 +362,17 @@ impl From> for Box { impl CargoError for semver::ReqParseError {} impl CargoError for io::Error {} impl CargoError for git2::Error {} -impl CargoError for json::DecoderError {} -impl CargoError for json::EncoderError {} +impl CargoError for serde_json::Error {} impl CargoError for curl::Error {} impl CargoError for ProcessError {} impl CargoError for CargoTestError {} impl CargoError for CliError {} -impl CargoError for toml::Error {} -impl CargoError for toml::DecodeError {} +impl CargoError for toml::ser::Error { + fn is_human(&self) -> bool { true } +} +impl CargoError for toml::de::Error { + fn is_human(&self) -> bool { true } +} impl CargoError for url::ParseError {} impl CargoError for ffi::NulError {} impl CargoError for term::Error {} diff --git a/src/cargo/util/machine_message.rs b/src/cargo/util/machine_message.rs index 64e6c35f160..9626494cd3d 100644 --- a/src/cargo/util/machine_message.rs +++ b/src/cargo/util/machine_message.rs @@ -1,27 +1,23 @@ -use rustc_serialize::Encodable; -use rustc_serialize::json::{self, Json}; +use serde::ser; +use serde_json::{self, Value}; use core::{PackageId, Target, Profile}; -pub trait Message: Encodable { +pub trait Message: ser::Serialize { fn reason(&self) -> &str; } pub fn emit(t: T) { - let json = json::encode(&t).unwrap(); - let mut map = match json.parse().unwrap() { - Json::Object(obj) => obj, - _ => panic!("not a json object"), - }; - map.insert("reason".to_string(), Json::String(t.reason().to_string())); - println!("{}", Json::Object(map)); + let mut json: Value = serde_json::to_value(&t).unwrap(); + json["reason"] = json!(t.reason()); + println!("{}", json); } -#[derive(RustcEncodable)] +#[derive(Serialize)] pub struct FromCompiler<'a> { pub package_id: &'a PackageId, pub target: &'a Target, - pub message: json::Json, + pub message: serde_json::Value, } impl<'a> Message for FromCompiler<'a> { @@ -30,7 +26,7 @@ impl<'a> Message for FromCompiler<'a> { } } -#[derive(RustcEncodable)] +#[derive(Serialize)] pub struct Artifact<'a> { pub package_id: &'a PackageId, pub target: &'a Target, @@ -45,7 +41,7 @@ impl<'a> Message for Artifact<'a> { } } -#[derive(RustcEncodable)] +#[derive(Serialize)] pub struct BuildScript<'a> { pub package_id: &'a PackageId, pub linked_libs: &'a [String], diff --git a/src/cargo/util/toml.rs b/src/cargo/util/toml.rs index 8c06e30ca87..d44dafe56be 100644 --- a/src/cargo/util/toml.rs +++ b/src/cargo/util/toml.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::{HashMap, HashSet, BTreeSet}; use std::default::Default; use std::fmt; use std::fs; @@ -7,7 +7,8 @@ use std::str; use toml; use semver::{self, VersionReq}; -use rustc_serialize::{Decodable, Decoder}; +use serde::de::{self, Deserialize}; +use serde_ignored; use core::{SourceId, Profiles, PackageIdSpec, GitReference, WorkspaceConfig}; use core::{Summary, Manifest, Target, Dependency, DependencyInner, PackageId}; @@ -29,7 +30,6 @@ pub struct Layout { examples: Vec, tests: Vec, benches: Vec, - } impl Layout { @@ -102,15 +102,19 @@ pub fn to_manifest(contents: &str, None => manifest.clone(), }; let root = parse(contents, &manifest, config)?; - let mut d = toml::Decoder::new(toml::Value::Table(root)); - let manifest: TomlManifest = Decodable::decode(&mut d).map_err(|e| { - human(e.to_string()) + let mut unused = BTreeSet::new(); + let manifest: TomlManifest = serde_ignored::deserialize(root, |path| { + let mut key = String::new(); + stringify(&mut key, &path); + if !key.starts_with("package.metadata") { + unused.insert(key); + } })?; return match manifest.to_real_manifest(source_id, &layout, config) { Ok((mut manifest, paths)) => { - if let Some(ref toml) = d.toml { - add_unused_keys(&mut manifest, toml, String::new()); + for key in unused { + manifest.add_warning(format!("unused manifest key: {}", key)); } if !manifest.targets().iter().any(|t| !t.is_custom_build()) { bail!("no targets specified in the manifest\n \ @@ -127,41 +131,43 @@ pub fn to_manifest(contents: &str, } }; - fn add_unused_keys(m: &mut Manifest, toml: &toml::Value, key: String) { - if key == "package.metadata" { - return - } - match *toml { - toml::Value::Table(ref table) => { - for (k, v) in table.iter() { - add_unused_keys(m, v, if key.is_empty() { - k.clone() - } else { - key.clone() + "." + k - }) + fn stringify(dst: &mut String, path: &serde_ignored::Path) { + use serde_ignored::Path; + + match *path { + Path::Root => {} + Path::Seq { parent, index } => { + stringify(dst, parent); + if dst.len() > 0 { + dst.push_str("."); } + dst.push_str(&index.to_string()); } - toml::Value::Array(ref arr) => { - for v in arr.iter() { - add_unused_keys(m, v, key.clone()); + Path::Map { parent, ref key } => { + stringify(dst, parent); + if dst.len() > 0 { + dst.push_str("."); } + dst.push_str(key); } - _ => m.add_warning(format!("unused manifest key: {}", key)), + Path::Some { parent } | + Path::NewtypeVariant { parent } | + Path::NewtypeStruct { parent } => stringify(dst, parent), } } } pub fn parse(toml: &str, file: &Path, - config: &Config) -> CargoResult { - let mut first_parser = toml::Parser::new(toml); - if let Some(toml) = first_parser.parse() { - return Ok(toml); - } + config: &Config) -> CargoResult { + let first_error = match toml.parse() { + Ok(ret) => return Ok(ret), + Err(e) => e, + }; - let mut second_parser = toml::Parser::new(toml); + let mut second_parser = toml::de::Deserializer::new(toml); second_parser.set_require_newline_after_table(false); - if let Some(toml) = second_parser.parse() { + if let Ok(ret) = toml::Value::deserialize(&mut second_parser) { let msg = format!("\ TOML file found which contains invalid syntax and will soon not parse at `{}`. @@ -171,25 +177,12 @@ invalid), but this file has a table header which does not have a newline after it. A newline needs to be added and this warning will soon become a hard error in the future.", file.display()); config.shell().warn(&msg)?; - return Ok(toml) + return Ok(ret) } - let mut error_str = "could not parse input as TOML\n".to_string(); - for error in first_parser.errors.iter() { - let (loline, locol) = first_parser.to_linecol(error.lo); - let (hiline, hicol) = first_parser.to_linecol(error.hi); - error_str.push_str(&format!("{}:{}:{}{} {}\n", - file.display(), - loline + 1, locol + 1, - if loline != hiline || locol != hicol { - format!("-{}:{}", hiline + 1, - hicol + 1) - } else { - "".to_string() - }, - error.desc)); - } - Err(human(error_str)) + Err(first_error).chain_error(|| { + human("could not parse input as TOML") + }) } type TomlLibTarget = TomlTarget; @@ -198,14 +191,15 @@ type TomlExampleTarget = TomlTarget; type TomlTestTarget = TomlTarget; type TomlBenchTarget = TomlTarget; -#[derive(RustcDecodable)] +#[derive(Deserialize)] +#[serde(untagged)] pub enum TomlDependency { Simple(String), Detailed(DetailedTomlDependency) } -#[derive(RustcDecodable, Clone, Default)] +#[derive(Deserialize, Clone, Default)] pub struct DetailedTomlDependency { version: Option, path: Option, @@ -215,10 +209,11 @@ pub struct DetailedTomlDependency { rev: Option, features: Option>, optional: Option, + #[serde(rename = "default-features")] default_features: Option, } -#[derive(RustcDecodable)] +#[derive(Deserialize)] pub struct TomlManifest { package: Option>, project: Option>, @@ -229,7 +224,9 @@ pub struct TomlManifest { test: Option>, bench: Option>, dependencies: Option>, + #[serde(rename = "dev-dependencies")] dev_dependencies: Option>, + #[serde(rename = "build-dependencies")] build_dependencies: Option>, features: Option>>, target: Option>, @@ -238,7 +235,7 @@ pub struct TomlManifest { badges: Option>>, } -#[derive(RustcDecodable, Clone, Default)] +#[derive(Deserialize, Clone, Default)] pub struct TomlProfiles { test: Option, doc: Option, @@ -250,50 +247,74 @@ pub struct TomlProfiles { #[derive(Clone)] pub struct TomlOptLevel(String); -impl Decodable for TomlOptLevel { - fn decode(d: &mut D) -> Result { - match d.read_u32() { - Ok(i) => Ok(TomlOptLevel(i.to_string())), - Err(_) => { - match d.read_str() { - Ok(ref s) if s == "s" || s == "z" => - Ok(TomlOptLevel(s.to_string())), - Ok(_) | Err(_) => - Err(d.error("expected an integer, a string \"z\" or a string \"s\"")) +impl de::Deserialize for TomlOptLevel { + fn deserialize(d: D) -> Result + where D: de::Deserializer + { + struct Visitor; + + impl de::Visitor for Visitor { + type Value = TomlOptLevel; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an optimization level") + } + + fn visit_i64(self, value: i64) -> Result + where E: de::Error + { + Ok(TomlOptLevel(value.to_string())) + } + + fn visit_str(self, value: &str) -> Result + where E: de::Error + { + if value == "s" || value == "z" { + Ok(TomlOptLevel(value.to_string())) + } else { + Err(E::custom(format!("must be an integer, `z`, or `s`, \ + but found: {}", value))) } } } + + d.deserialize_u32(Visitor) } } -#[derive(RustcDecodable, Clone)] +#[derive(Deserialize, Clone)] +#[serde(untagged)] pub enum U32OrBool { U32(u32), Bool(bool), } -#[derive(RustcDecodable, Clone, Default)] +#[derive(Deserialize, Clone, Default)] pub struct TomlProfile { + #[serde(rename = "opt-level")] opt_level: Option, lto: Option, + #[serde(rename = "codegen-units")] codegen_units: Option, debug: Option, + #[serde(rename = "debug-assertions")] debug_assertions: Option, rpath: Option, panic: Option, } -#[derive(RustcDecodable, Clone, Debug)] +#[derive(Deserialize, Clone, Debug)] +#[serde(untagged)] pub enum StringOrBool { String(String), Bool(bool), } -#[derive(RustcDecodable)] +#[derive(Deserialize)] pub struct TomlProject { name: String, version: TomlVersion, - authors: Vec, + authors: Option>, build: Option, links: Option, exclude: Option>, @@ -309,11 +330,12 @@ pub struct TomlProject { keywords: Option>, categories: Option>, license: Option, + #[serde(rename = "license-file")] license_file: Option, repository: Option, } -#[derive(RustcDecodable)] +#[derive(Deserialize)] pub struct TomlWorkspace { members: Option>, } @@ -322,13 +344,30 @@ pub struct TomlVersion { version: semver::Version, } -impl Decodable for TomlVersion { - fn decode(d: &mut D) -> Result { - let s = d.read_str()?; - match s.to_semver() { - Ok(s) => Ok(TomlVersion { version: s }), - Err(e) => Err(d.error(&e)), +impl de::Deserialize for TomlVersion { + fn deserialize(d: D) -> Result + where D: de::Deserializer + { + struct Visitor; + + impl de::Visitor for Visitor { + type Value = TomlVersion; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a semver version") + } + + fn visit_str(self, value: &str) -> Result + where E: de::Error + { + match value.to_semver() { + Ok(s) => Ok(TomlVersion { version: s}), + Err(e) => Err(E::custom(e)), + } + } } + + d.deserialize_str(Visitor) } } @@ -630,7 +669,7 @@ impl TomlManifest { homepage: project.homepage.clone(), documentation: project.documentation.clone(), readme: project.readme.clone(), - authors: project.authors.clone(), + authors: project.authors.clone().unwrap_or(Vec::new()), license: project.license.clone(), license_file: project.license_file.clone(), repository: project.repository.clone(), @@ -918,50 +957,50 @@ impl TomlDependency { } } -#[derive(RustcDecodable, Debug, Clone)] +#[derive(Default, Deserialize, Debug, Clone)] struct TomlTarget { name: Option, + + // The intention was to only accept `crate-type` here but historical + // versions of Cargo also accepted `crate_type`, so look for both. + #[serde(rename = "crate-type")] crate_type: Option>, + #[serde(rename = "crate_type")] + crate_type2: Option>, + path: Option, test: Option, doctest: Option, bench: Option, doc: Option, plugin: Option, + #[serde(rename = "proc-macro")] proc_macro: Option, harness: Option, + #[serde(rename = "required-features")] required_features: Option>, } -#[derive(RustcDecodable, Clone)] +#[derive(Deserialize, Clone)] +#[serde(untagged)] enum PathValue { String(String), Path(PathBuf), } /// Corresponds to a `target` entry, but `TomlTarget` is already used. -#[derive(RustcDecodable)] +#[derive(Deserialize)] struct TomlPlatform { dependencies: Option>, + #[serde(rename = "build-dependencies")] build_dependencies: Option>, + #[serde(rename = "dev-dependencies")] dev_dependencies: Option>, } impl TomlTarget { fn new() -> TomlTarget { - TomlTarget { - name: None, - crate_type: None, - path: None, - test: None, - doctest: None, - bench: None, - doc: None, - plugin: None, - proc_macro: None, - harness: None, - required_features: None - } + TomlTarget::default() } fn name(&self) -> String { @@ -1100,7 +1139,8 @@ fn normalize(package_root: &Path, let path = l.path.clone().unwrap_or_else( || PathValue::Path(Path::new("src").join(&format!("{}.rs", l.name()))) ); - let crate_types = match l.crate_type.clone() { + let crate_types = l.crate_type.as_ref().or(l.crate_type2.as_ref()); + let crate_types = match crate_types { Some(kinds) => kinds.iter().map(|s| LibKind::from_str(s)).collect(), None => { vec![ if l.plugin == Some(true) {LibKind::Dylib} @@ -1148,8 +1188,9 @@ fn normalize(package_root: &Path, PathValue::Path(default(ex)) }); - let crate_types = match ex.crate_type { - Some(ref kinds) => kinds.iter().map(|s| LibKind::from_str(s)).collect(), + let crate_types = ex.crate_type.as_ref().or(ex.crate_type2.as_ref()); + let crate_types = match crate_types { + Some(kinds) => kinds.iter().map(|s| LibKind::from_str(s)).collect(), None => Vec::new() }; diff --git a/src/crates-io/Cargo.toml b/src/crates-io/Cargo.toml index 54a54e1dea0..21db0467fe9 100644 --- a/src/crates-io/Cargo.toml +++ b/src/crates-io/Cargo.toml @@ -14,5 +14,7 @@ path = "lib.rs" [dependencies] curl = "0.4" +serde = "0.9" +serde_derive = "0.9" +serde_json = "0.9" url = "1.0" -rustc-serialize = "0.3" diff --git a/src/crates-io/lib.rs b/src/crates-io/lib.rs index 915f44363a3..df032f349f2 100644 --- a/src/crates-io/lib.rs +++ b/src/crates-io/lib.rs @@ -1,6 +1,8 @@ extern crate curl; extern crate url; -extern crate rustc_serialize; +extern crate serde_json; +#[macro_use] +extern crate serde_derive; use std::collections::HashMap; use std::fmt; @@ -10,7 +12,6 @@ use std::io::{self, Cursor}; use std::result; use curl::easy::{Easy, List}; -use rustc_serialize::json::{self, Json}; use url::percent_encoding::{percent_encode, QUERY_ENCODE_SET}; @@ -37,26 +38,12 @@ pub enum Error { TokenMissing, Io(io::Error), NotFound, - JsonEncodeError(json::EncoderError), - JsonDecodeError(json::DecoderError), - JsonParseError(json::ParserError), + Json(serde_json::Error), } -impl From for Error { - fn from(err: json::EncoderError) -> Error { - Error::JsonEncodeError(err) - } -} - -impl From for Error { - fn from(err: json::DecoderError) -> Error { - Error::JsonDecodeError(err) - } -} - -impl From for Error { - fn from(err: json::ParserError) -> Error { - Error::JsonParseError(err) +impl From for Error { + fn from(err: serde_json::Error) -> Error { + Error::Json(err) } } @@ -66,14 +53,14 @@ impl From for Error { } } -#[derive(RustcDecodable)] +#[derive(Deserialize)] pub struct Crate { pub name: String, pub description: Option, pub max_version: String } -#[derive(RustcEncodable)] +#[derive(Serialize)] pub struct NewCrate { pub name: String, pub vers: String, @@ -92,7 +79,7 @@ pub struct NewCrate { pub badges: HashMap>, } -#[derive(RustcEncodable)] +#[derive(Serialize)] pub struct NewCrateDependency { pub optional: bool, pub default_features: bool, @@ -103,7 +90,7 @@ pub struct NewCrateDependency { pub kind: String, } -#[derive(RustcDecodable)] +#[derive(Deserialize)] pub struct User { pub id: u32, pub login: String, @@ -117,13 +104,13 @@ pub struct Warnings { pub invalid_badges: Vec, } -#[derive(RustcDecodable)] struct R { ok: bool } -#[derive(RustcDecodable)] struct ApiErrorList { errors: Vec } -#[derive(RustcDecodable)] struct ApiError { detail: String } -#[derive(RustcEncodable)] struct OwnersReq<'a> { users: &'a [&'a str] } -#[derive(RustcDecodable)] struct Users { users: Vec } -#[derive(RustcDecodable)] struct TotalCrates { total: u32 } -#[derive(RustcDecodable)] struct Crates { crates: Vec, meta: TotalCrates } +#[derive(Deserialize)] struct R { ok: bool } +#[derive(Deserialize)] struct ApiErrorList { errors: Vec } +#[derive(Deserialize)] struct ApiError { detail: String } +#[derive(Serialize)] struct OwnersReq<'a> { users: &'a [&'a str] } +#[derive(Deserialize)] struct Users { users: Vec } +#[derive(Deserialize)] struct TotalCrates { total: u32 } +#[derive(Deserialize)] struct Crates { crates: Vec, meta: TotalCrates } impl Registry { pub fn new(host: String, token: Option) -> Registry { Registry::new_handle(host, token, Easy::new()) @@ -140,29 +127,29 @@ impl Registry { } pub fn add_owners(&mut self, krate: &str, owners: &[&str]) -> Result<()> { - let body = json::encode(&OwnersReq { users: owners })?; + let body = serde_json::to_string(&OwnersReq { users: owners })?; let body = self.put(format!("/crates/{}/owners", krate), body.as_bytes())?; - assert!(json::decode::(&body)?.ok); + assert!(serde_json::from_str::(&body)?.ok); Ok(()) } pub fn remove_owners(&mut self, krate: &str, owners: &[&str]) -> Result<()> { - let body = json::encode(&OwnersReq { users: owners })?; + let body = serde_json::to_string(&OwnersReq { users: owners })?; let body = self.delete(format!("/crates/{}/owners", krate), Some(body.as_bytes()))?; - assert!(json::decode::(&body)?.ok); + assert!(serde_json::from_str::(&body)?.ok); Ok(()) } pub fn list_owners(&mut self, krate: &str) -> Result> { let body = self.get(format!("/crates/{}/owners", krate))?; - Ok(json::decode::(&body)?.users) + Ok(serde_json::from_str::(&body)?.users) } pub fn publish(&mut self, krate: &NewCrate, tarball: &File) -> Result { - let json = json::encode(krate)?; + let json = serde_json::to_string(krate)?; // Prepare the body. The format of the upload request is: // // @@ -208,28 +195,27 @@ impl Registry { body.read(buf).unwrap_or(0) })?; - // Can't derive RustcDecodable because JSON has a key named "crate" :( let response = if body.len() > 0 { - Json::from_str(&body)? + body.parse::()? } else { - Json::from_str("{}")? + "{}".parse()? }; let invalid_categories: Vec = - response - .find_path(&["warnings", "invalid_categories"]) - .and_then(Json::as_array) + response.get("warnings") + .and_then(|j| j.get("invalid_categories")) + .and_then(|j| j.as_array()) .map(|x| { - x.iter().flat_map(Json::as_string).map(Into::into).collect() + x.iter().flat_map(|j| j.as_str()).map(Into::into).collect() }) .unwrap_or_else(Vec::new); let invalid_badges: Vec = - response - .find_path(&["warnings", "invalid_badges"]) - .and_then(Json::as_array) + response.get("warnings") + .and_then(|j| j.get("invalid_badges")) + .and_then(|j| j.as_array()) .map(|x| { - x.iter().flat_map(Json::as_string).map(Into::into).collect() + x.iter().flat_map(|j| j.as_str()).map(Into::into).collect() }) .unwrap_or_else(Vec::new); @@ -246,21 +232,21 @@ impl Registry { None, Auth::Unauthorized )?; - let crates = json::decode::(&body)?; + let crates = serde_json::from_str::(&body)?; Ok((crates.crates, crates.meta.total)) } pub fn yank(&mut self, krate: &str, version: &str) -> Result<()> { let body = self.delete(format!("/crates/{}/{}/yank", krate, version), None)?; - assert!(json::decode::(&body)?.ok); + assert!(serde_json::from_str::(&body)?.ok); Ok(()) } pub fn unyank(&mut self, krate: &str, version: &str) -> Result<()> { let body = self.put(format!("/crates/{}/{}/unyank", krate, version), &[])?; - assert!(json::decode::(&body)?.ok); + assert!(serde_json::from_str::(&body)?.ok); Ok(()) } @@ -337,7 +323,7 @@ fn handle(handle: &mut Easy, Ok(body) => body, Err(..) => return Err(Error::NonUtf8Body), }; - match json::decode::(&body) { + match serde_json::from_str::(&body) { Ok(errors) => { return Err(Error::Api(errors.errors.into_iter().map(|s| s.detail) .collect())) @@ -369,9 +355,7 @@ impl fmt::Display for Error { Error::TokenMissing => write!(f, "no upload token found, please run `cargo login`"), Error::Io(ref e) => write!(f, "io error: {}", e), Error::NotFound => write!(f, "cannot find crate"), - Error::JsonEncodeError(ref e) => write!(f, "json encode error: {}", e), - Error::JsonDecodeError(ref e) => write!(f, "json decode error: {}", e), - Error::JsonParseError(ref e) => write!(f, "json parse error: {}", e), + Error::Json(ref e) => write!(f, "json error: {}", e), } } } diff --git a/tests/bad-config.rs b/tests/bad-config.rs index 63aae50dfa4..9db8606d86c 100644 --- a/tests/bad-config.rs +++ b/tests/bad-config.rs @@ -206,12 +206,13 @@ fn invalid_global_config() { [ERROR] Couldn't load Cargo configuration Caused by: - could not parse TOML configuration in `[..]config` + could not parse TOML configuration in `[..]` Caused by: could not parse input as TOML -[..]config:1:2 expected `=`, but found eof +Caused by: + expected an equals, found eof at line 1 ")); } @@ -232,7 +233,7 @@ fn bad_cargo_lock() { [ERROR] failed to parse lock file at: [..]Cargo.lock Caused by: - expected a value of type `string` for the key `package.name` + missing field `name` for key `package` ")); } @@ -315,7 +316,7 @@ fn bad_source_in_cargo_lock() { [ERROR] failed to parse lock file at: [..] Caused by: - invalid source `You shall not parse` for the key `package.source` + invalid source `You shall not parse` for key `package.source` ")); } @@ -421,8 +422,9 @@ fn malformed_override() { Caused by: could not parse input as TOML -Cargo.toml:[..] +Caused by: + expected a table key, found a newline at line 8 ")); } diff --git a/tests/build.rs b/tests/build.rs index 721ec3478b7..c937f0487e4 100644 --- a/tests/build.rs +++ b/tests/build.rs @@ -100,8 +100,9 @@ fn cargo_compile_with_invalid_manifest2() { Caused by: could not parse input as TOML -Cargo.toml:3:19-3:20 expected a value +Caused by: + invalid number at line 3 ")) } @@ -124,8 +125,11 @@ fn cargo_compile_with_invalid_manifest3() { [ERROR] failed to parse manifest at `[..]` Caused by: - could not parse input as TOML\n\ -src[/]Cargo.toml:1:5-1:6 expected a value\n\n")) + could not parse input as TOML + +Caused by: + invalid number at line 1 +")) } #[test] @@ -175,7 +179,7 @@ fn cargo_compile_with_invalid_version() { [ERROR] failed to parse manifest at `[..]` Caused by: - cannot parse '1.0' as a semver for the key `project.version` + cannot parse '1.0' as a semver for key `project.version` ")) } @@ -876,39 +880,39 @@ suffix = env::consts::DLL_SUFFIX, fn crate_env_vars() { let p = project("foo") .file("Cargo.toml", r#" - [project] - name = "foo" - version = "0.5.1-alpha.1" - description = "This is foo" - homepage = "http://example.com" - authors = ["wycats@example.com"] + [project] + name = "foo" + version = "0.5.1-alpha.1" + description = "This is foo" + homepage = "http://example.com" + authors = ["wycats@example.com"] "#) .file("src/main.rs", r#" extern crate foo; - static VERSION_MAJOR: &'static str = env!("CARGO_PKG_VERSION_MAJOR"); - static VERSION_MINOR: &'static str = env!("CARGO_PKG_VERSION_MINOR"); - static VERSION_PATCH: &'static str = env!("CARGO_PKG_VERSION_PATCH"); - static VERSION_PRE: &'static str = env!("CARGO_PKG_VERSION_PRE"); - static VERSION: &'static str = env!("CARGO_PKG_VERSION"); - static CARGO_MANIFEST_DIR: &'static str = env!("CARGO_MANIFEST_DIR"); - static PKG_NAME: &'static str = env!("CARGO_PKG_NAME"); - static HOMEPAGE: &'static str = env!("CARGO_PKG_HOMEPAGE"); - static DESCRIPTION: &'static str = env!("CARGO_PKG_DESCRIPTION"); + static VERSION_MAJOR: &'static str = env!("CARGO_PKG_VERSION_MAJOR"); + static VERSION_MINOR: &'static str = env!("CARGO_PKG_VERSION_MINOR"); + static VERSION_PATCH: &'static str = env!("CARGO_PKG_VERSION_PATCH"); + static VERSION_PRE: &'static str = env!("CARGO_PKG_VERSION_PRE"); + static VERSION: &'static str = env!("CARGO_PKG_VERSION"); + static CARGO_MANIFEST_DIR: &'static str = env!("CARGO_MANIFEST_DIR"); + static PKG_NAME: &'static str = env!("CARGO_PKG_NAME"); + static HOMEPAGE: &'static str = env!("CARGO_PKG_HOMEPAGE"); + static DESCRIPTION: &'static str = env!("CARGO_PKG_DESCRIPTION"); fn main() { let s = format!("{}-{}-{} @ {} in {}", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_PRE, CARGO_MANIFEST_DIR); - assert_eq!(s, foo::version()); - println!("{}", s); - assert_eq!("foo", PKG_NAME); - assert_eq!("http://example.com", HOMEPAGE); - assert_eq!("This is foo", DESCRIPTION); + assert_eq!(s, foo::version()); + println!("{}", s); + assert_eq!("foo", PKG_NAME); + assert_eq!("http://example.com", HOMEPAGE); + assert_eq!("This is foo", DESCRIPTION); let s = format!("{}.{}.{}-{}", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_PRE); - assert_eq!(s, VERSION); + assert_eq!(s, VERSION); } "#) .file("src/lib.rs", r#" @@ -1755,8 +1759,9 @@ Caused by: Caused by: could not parse input as TOML -[..].cargo[..]config:2:20-2:21 expected `=`, but found `i` +Caused by: + expected an equals, found an identifier at line 2 ")); } diff --git a/tests/cargotest/Cargo.toml b/tests/cargotest/Cargo.toml index 55ef9643f1b..233a378d3ee 100644 --- a/tests/cargotest/Cargo.toml +++ b/tests/cargotest/Cargo.toml @@ -17,6 +17,8 @@ kernel32-sys = "0.2" libc = "0.2" log = "0.3" rustc-serialize = "0.3" +serde = "0.9" +serde_json = "0.9" tar = { version = "0.4", default-features = false } tempdir = "0.3" term = "0.4.4" diff --git a/tests/cargotest/lib.rs b/tests/cargotest/lib.rs index affb7863795..11df25fdd13 100644 --- a/tests/cargotest/lib.rs +++ b/tests/cargotest/lib.rs @@ -8,6 +8,9 @@ extern crate git2; extern crate hamcrest; extern crate libc; extern crate rustc_serialize; +extern crate serde; +#[macro_use] +extern crate serde_json; extern crate tar; extern crate tempdir; extern crate term; @@ -49,6 +52,7 @@ fn _process(t: &OsStr) -> cargo::util::ProcessBuilder { .env("CARGO_HOME", support::paths::home().join(".cargo")) .env_remove("RUSTC") .env_remove("RUSTFLAGS") + .env_remove("CARGO_INCREMENTAL") .env_remove("XDG_CONFIG_HOME") // see #2345 .env("GIT_CONFIG_NOSYSTEM", "1") // keep trying to sandbox ourselves .env_remove("CARGO_TARGET_DIR") // we assume 'target' diff --git a/tests/cargotest/support/mod.rs b/tests/cargotest/support/mod.rs index ecc3e017de6..88b5c396437 100644 --- a/tests/cargotest/support/mod.rs +++ b/tests/cargotest/support/mod.rs @@ -11,7 +11,7 @@ use std::process::Output; use std::str; use std::usize; -use rustc_serialize::json::Json; +use serde_json::{self, Value}; use url::Url; use hamcrest as ham; use cargo::util::ProcessBuilder; @@ -335,7 +335,7 @@ pub struct Execs { expect_stderr_contains: Vec, expect_stdout_not_contains: Vec, expect_stderr_not_contains: Vec, - expect_json: Option>, + expect_json: Option>, } impl Execs { @@ -376,7 +376,7 @@ impl Execs { pub fn with_json(mut self, expected: &str) -> Execs { self.expect_json = Some(expected.split("\n\n").map(|obj| { - Json::from_str(obj).unwrap() + obj.parse().unwrap() }).collect()); self } @@ -500,8 +500,8 @@ impl Execs { } } - fn match_json(&self, expected: &Json, line: &str) -> ham::MatchResult { - let actual = match Json::from_str(line) { + fn match_json(&self, expected: &Value, line: &str) -> ham::MatchResult { + let actual = match line.parse() { Err(e) => return Err(format!("invalid json, {}:\n`{}`", e, line)), Ok(actual) => actual, }; @@ -509,8 +509,10 @@ impl Execs { match find_mismatch(expected, &actual) { Some((expected_part, actual_part)) => Err(format!( "JSON mismatch\nExpected:\n{}\nWas:\n{}\nExpected part:\n{}\nActual part:\n{}\n", - expected.pretty(), actual.pretty(), - expected_part.pretty(), actual_part.pretty() + serde_json::to_string_pretty(expected).unwrap(), + serde_json::to_string_pretty(&actual).unwrap(), + serde_json::to_string_pretty(expected_part).unwrap(), + serde_json::to_string_pretty(actual_part).unwrap(), )), None => Ok(()), } @@ -583,32 +585,42 @@ fn lines_match_works() { } // Compares JSON object for approximate equality. -// You can use `[..]` wildcard in strings (useful for OS dependent things such as paths). -// You can use a `"{...}"` string literal as a wildcard for arbitrary nested JSON (useful -// for parts of object emitted by other programs (e.g. rustc) rather than Cargo itself). -// Arrays are sorted before comparison. -fn find_mismatch<'a>(expected: &'a Json, actual: &'a Json) -> Option<(&'a Json, &'a Json)> { - use rustc_serialize::json::Json::*; +// You can use `[..]` wildcard in strings (useful for OS dependent things such +// as paths). You can use a `"{...}"` string literal as a wildcard for +// arbitrary nested JSON (useful for parts of object emitted by other programs +// (e.g. rustc) rather than Cargo itself). Arrays are sorted before comparison. +fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) + -> Option<(&'a Value, &'a Value)> { + use serde_json::Value::*; match (expected, actual) { - (&I64(l), &I64(r)) if l == r => None, - (&F64(l), &F64(r)) if l == r => None, - (&U64(l), &U64(r)) if l == r => None, - (&Boolean(l), &Boolean(r)) if l == r => None, + (&Number(ref l), &Number(ref r)) if l == r => None, + (&Bool(l), &Bool(r)) if l == r => None, (&String(ref l), &String(ref r)) if lines_match(l, r) => None, (&Array(ref l), &Array(ref r)) => { if l.len() != r.len() { return Some((expected, actual)); } - fn sorted(xs: &Vec) -> Vec<&Json> { - let mut result = xs.iter().collect::>(); - result.sort_by(|x, y| x.partial_cmp(y).expect("JSON spec does not allow NaNs")); - result - } + let mut l = l.iter().collect::>(); + let mut r = r.iter().collect::>(); - sorted(l).iter().zip(sorted(r)) - .filter_map(|(l, r)| find_mismatch(l, r)) - .nth(0) + l.retain(|l| { + match r.iter().position(|r| find_mismatch(l, r).is_none()) { + Some(i) => { + r.remove(i); + false + } + None => true + } + }); + + if l.len() > 0 { + assert!(r.len() > 0); + Some((&l[0], &r[0])) + } else { + assert!(r.len() == 0); + None + } } (&Object(ref l), &Object(ref r)) => { let same_keys = l.len() == r.len() && l.keys().all(|k| r.contains_key(k)); diff --git a/tests/cargotest/support/registry.rs b/tests/cargotest/support/registry.rs index a57dfa26327..3eae9432992 100644 --- a/tests/cargotest/support/registry.rs +++ b/tests/cargotest/support/registry.rs @@ -7,7 +7,6 @@ use flate2::Compression::Default; use flate2::write::GzEncoder; use git2; use rustc_serialize::hex::ToHex; -use rustc_serialize::json::ToJson; use tar::{Builder, Header}; use url::Url; @@ -137,29 +136,29 @@ impl Package { // Figure out what we're going to write into the index let deps = self.deps.iter().map(|dep| { - let mut map = HashMap::new(); - map.insert("name".to_string(), dep.name.to_json()); - map.insert("req".to_string(), dep.vers.to_json()); - map.insert("features".to_string(), dep.features.to_json()); - map.insert("default_features".to_string(), true.to_json()); - map.insert("target".to_string(), dep.target.to_json()); - map.insert("optional".to_string(), false.to_json()); - map.insert("kind".to_string(), dep.kind.to_json()); - map + json!({ + "name": dep.name, + "req": dep.vers, + "features": dep.features, + "default_features": true, + "target": dep.target, + "optional": false, + "kind": dep.kind, + }) }).collect::>(); let cksum = { let mut c = Vec::new(); t!(t!(File::open(&self.archive_dst())).read_to_end(&mut c)); cksum(&c) }; - let mut dep = HashMap::new(); - dep.insert("name".to_string(), self.name.to_json()); - dep.insert("vers".to_string(), self.vers.to_json()); - dep.insert("deps".to_string(), deps.to_json()); - dep.insert("cksum".to_string(), cksum.to_json()); - dep.insert("features".to_string(), self.features.to_json()); - dep.insert("yanked".to_string(), self.yanked.to_json()); - let line = dep.to_json().to_string(); + let line = json!({ + "name": self.name, + "vers": self.vers, + "deps": deps, + "cksum": cksum, + "features": self.features, + "yanked": self.yanked, + }).to_string(); let file = match self.name.len() { 1 => format!("1/{}", self.name), diff --git a/tests/registry.rs b/tests/registry.rs index c84d28cdd6b..1a60826cd2f 100644 --- a/tests/registry.rs +++ b/tests/registry.rs @@ -1317,17 +1317,17 @@ fn old_version_req_upstream() { p.build(); Package::new("remote", "0.3.0") - .file("Cargo.toml", r#" - [project] + .file("Cargo.toml", r#" + [project] name = "remote" version = "0.3.0" authors = [] [dependencies] bar = "0.2*" - "#) + "#) .file("src/lib.rs", "") - .publish(); + .publish(); Package::new("bar", "0.2.0").publish(); assert_that(p.cargo("build"), @@ -1356,17 +1356,17 @@ fn toml_lies_but_index_is_truth() { Package::new("foo", "0.2.0").publish(); Package::new("bar", "0.3.0") .dep("foo", "0.2.0") - .file("Cargo.toml", r#" - [project] + .file("Cargo.toml", r#" + [project] name = "bar" version = "0.3.0" authors = [] [dependencies] foo = "0.1.0" - "#) + "#) .file("src/lib.rs", "extern crate foo;") - .publish(); + .publish(); let p = project("foo") .file("Cargo.toml", r#"