From 5fe80f06dd8720f024e1b42310bde7ce55311dbb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 22 Sep 2022 15:16:17 -0700 Subject: [PATCH] Support inheriting edition from workspace manifest --- Cargo.toml | 4 +-- src/dependencies.rs | 66 ++++++++++++++++++++++++++++++++++++++++++--- src/error.rs | 2 ++ src/inherit.rs | 41 ++++++++++++++++++++++++++++ src/lib.rs | 1 + src/run.rs | 19 +++++++++---- 6 files changed, 123 insertions(+), 10 deletions(-) create mode 100644 src/inherit.rs diff --git a/Cargo.toml b/Cargo.toml index bb655a9..f00a36e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,8 +22,8 @@ diff = ["dissimilar"] dissimilar = { version = "1.0", optional = true } glob = "0.3" once_cell = "1.9" -serde = "1.0.103" -serde_derive = "1.0.103" +serde = "1.0.139" +serde_derive = "1.0.139" serde_json = "1.0" termcolor = "1.0.4" toml = "0.5.2" diff --git a/src/dependencies.rs b/src/dependencies.rs index 36b49c2..7bdc79e 100644 --- a/src/dependencies.rs +++ b/src/dependencies.rs @@ -1,9 +1,10 @@ use crate::directory::Directory; use crate::error::Error; +use crate::inherit::InheritEdition; use crate::manifest::Edition; use serde::de::value::MapAccessDeserializer; -use serde::de::{self, Visitor}; -use serde::de::{Deserialize, Deserializer}; +use serde::de::value::StrDeserializer; +use serde::de::{self, Deserialize, Deserializer, Visitor}; use serde::ser::{Serialize, Serializer}; use serde_derive::{Deserialize, Serialize}; use std::collections::BTreeMap as Map; @@ -71,12 +72,25 @@ fn fix_replacements(replacements: &mut Map, dir: &Directory) { #[derive(Deserialize, Default, Debug)] pub struct WorkspaceManifest { + #[serde(default)] + pub workspace: WorkspaceWorkspace, #[serde(default)] pub patch: Map, #[serde(default)] pub replace: Map, } +#[derive(Deserialize, Default, Debug)] +pub struct WorkspaceWorkspace { + #[serde(default)] + pub package: WorkspacePackage, +} + +#[derive(Deserialize, Default, Debug)] +pub struct WorkspacePackage { + pub edition: Option, +} + #[derive(Deserialize, Default, Debug)] pub struct Manifest { #[serde(default)] @@ -95,10 +109,16 @@ pub struct Manifest { pub struct Package { pub name: String, #[serde(default)] - pub edition: Edition, + pub edition: EditionOrInherit, pub resolver: Option, } +#[derive(Debug)] +pub enum EditionOrInherit { + Edition(Edition), + Inherit, +} + #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(remote = "Self")] pub struct Dependency { @@ -168,6 +188,46 @@ fn is_true(boolean: &bool) -> bool { *boolean } +impl Default for EditionOrInherit { + fn default() -> Self { + EditionOrInherit::Edition(Edition::default()) + } +} + +impl<'de> Deserialize<'de> for EditionOrInherit { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct EditionOrInheritVisitor; + + impl<'de> Visitor<'de> for EditionOrInheritVisitor { + type Value = EditionOrInherit; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("edition") + } + + fn visit_str(self, s: &str) -> Result + where + E: de::Error, + { + Edition::deserialize(StrDeserializer::new(s)).map(EditionOrInherit::Edition) + } + + fn visit_map(self, map: M) -> Result + where + M: de::MapAccess<'de>, + { + InheritEdition::deserialize(MapAccessDeserializer::new(map))?; + Ok(EditionOrInherit::Inherit) + } + } + + deserializer.deserialize_any(EditionOrInheritVisitor) + } +} + impl Serialize for Dependency { fn serialize(&self, serializer: S) -> Result where diff --git a/src/error.rs b/src/error.rs index f6bfdea..3158312 100644 --- a/src/error.rs +++ b/src/error.rs @@ -13,6 +13,7 @@ pub enum Error { Io(io::Error), Metadata(serde_json::Error), Mismatch, + NoWorkspaceManifest, Open(PathBuf, io::Error), Pattern(PatternError), ProjectDir, @@ -39,6 +40,7 @@ impl Display for Error { Io(e) => write!(f, "{}", e), Metadata(e) => write!(f, "failed to read cargo metadata: {}", e), Mismatch => write!(f, "compiler error does not match expected error"), + NoWorkspaceManifest => write!(f, "Cargo.toml uses edition.workspace=true, but no edition found in workspace's manifest"), Open(path, e) => write!(f, "{}: {}", path.display(), e), Pattern(e) => write!(f, "{}", e), ProjectDir => write!(f, "failed to determine name of project dir"), diff --git a/src/inherit.rs b/src/inherit.rs new file mode 100644 index 0000000..056e28d --- /dev/null +++ b/src/inherit.rs @@ -0,0 +1,41 @@ +use serde::de::{self, Deserialize, Deserializer, Visitor}; +use serde_derive::Deserialize; +use std::fmt; + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +pub struct InheritEdition { + pub workspace: True, +} + +pub struct True; + +impl<'de> Deserialize<'de> for True { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_bool(True) + } +} + +impl<'de> Visitor<'de> for True { + type Value = True; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("bool") + } + + fn visit_bool(self, b: bool) -> Result + where + E: de::Error, + { + if b { + Ok(True) + } else { + Err(de::Error::custom( + "workspace=false is unsupported for package.edition", + )) + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 9693f5b..f883f69 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -245,6 +245,7 @@ mod error; mod expand; mod features; mod flock; +mod inherit; mod manifest; mod message; mod normalize; diff --git a/src/run.rs b/src/run.rs index 9c675dd..38d3681 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,5 +1,5 @@ use crate::cargo::{self, Metadata}; -use crate::dependencies::{self, Dependency}; +use crate::dependencies::{self, Dependency, EditionOrInherit}; use crate::directory::Directory; use crate::env::Update; use crate::error::{Error, Result}; @@ -157,7 +157,7 @@ impl Runner { &source_dir, tests, source_manifest, - ); + )?; if let Some(enabled_features) = &mut features { enabled_features.retain(|feature| manifest.features.contains_key(feature)); @@ -207,10 +207,19 @@ impl Runner { source_dir: &Directory, tests: &[ExpandedTest], source_manifest: dependencies::Manifest, - ) -> Manifest { + ) -> Result { let crate_name = source_manifest.package.name; let workspace_manifest = dependencies::get_workspace_manifest(workspace); + let edition = match source_manifest.package.edition { + EditionOrInherit::Edition(edition) => edition, + EditionOrInherit::Inherit => workspace_manifest + .workspace + .package + .edition + .ok_or(Error::NoWorkspaceManifest)?, + }; + let features = source_manifest .features .keys() @@ -224,7 +233,7 @@ impl Runner { package: Package { name: project_name.to_owned(), version: "0.0.0".to_owned(), - edition: source_manifest.package.edition, + edition, resolver: source_manifest.package.resolver, publish: false, }, @@ -276,7 +285,7 @@ impl Runner { } } - manifest + Ok(manifest) } fn make_config(&self) -> Config {