Skip to content

Commit 9230e48

Browse files
committed
Auto merge of #11113 - rdimartino:maybeworkspace-deserialize, r=epage
Improve errors for TOML fields that support workspace inheritance Fixes #10997 This also addresses the issue with `MaybeWorkspace<VecStringOrBool>` mentioned in #10942: ``` Caused by: invalid type: string "foo", expected a boolean or vector of strings for key `package.publish` ``` I removed the `maybe_workspace_vec_string` deserializer in 7a50c0c because the error message from the inner `Vec<String>` is now surfaced, but I can revert that if it's preferable to keep those changes. I tried to base the `deserialize` implementation off of the derived impl for an untagged enum from `cargo expand`. This approach [ultimately led me](https://github.com/serde-rs/serde/blob/v1.0.144/serde/src/private/de.rs#L218) to adding the `serde-value` dependency.
2 parents dbd1c18 + 7b16e59 commit 9230e48

File tree

3 files changed

+68
-49
lines changed

3 files changed

+68
-49
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ semver = { version = "1.0.3", features = ["serde"] }
5252
serde = { version = "1.0.123", features = ["derive"] }
5353
serde_ignored = "0.1.0"
5454
serde_json = { version = "1.0.30", features = ["raw_value"] }
55+
serde-value = "0.7.0"
5556
shell-escape = "0.1.4"
5657
strip-ansi-escapes = "0.1.0"
5758
tar = { version = "0.4.38", default-features = false }

src/cargo/util/toml/mod.rs

Lines changed: 18 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,13 +1007,30 @@ where
10071007
/// Enum that allows for the parsing of `field.workspace = true` in a Cargo.toml
10081008
///
10091009
/// It allows for things to be inherited from a workspace or defined as needed
1010-
#[derive(Deserialize, Serialize, Clone, Debug)]
1010+
#[derive(Serialize, Clone, Debug)]
10111011
#[serde(untagged)]
10121012
pub enum MaybeWorkspace<T> {
10131013
Workspace(TomlWorkspaceField),
10141014
Defined(T),
10151015
}
10161016

1017+
impl<'de, T: Deserialize<'de>> de::Deserialize<'de> for MaybeWorkspace<T> {
1018+
fn deserialize<D>(deserializer: D) -> Result<MaybeWorkspace<T>, D::Error>
1019+
where
1020+
D: de::Deserializer<'de>,
1021+
{
1022+
let value = serde_value::Value::deserialize(deserializer)?;
1023+
if let Ok(workspace) = TomlWorkspaceField::deserialize(serde_value::ValueDeserializer::<
1024+
D::Error,
1025+
>::new(value.clone()))
1026+
{
1027+
return Ok(MaybeWorkspace::Workspace(workspace));
1028+
}
1029+
T::deserialize(serde_value::ValueDeserializer::<D::Error>::new(value))
1030+
.map(MaybeWorkspace::Defined)
1031+
}
1032+
}
1033+
10171034
impl<T> MaybeWorkspace<T> {
10181035
fn resolve<'a>(
10191036
self,
@@ -1041,43 +1058,6 @@ impl<T> MaybeWorkspace<T> {
10411058
}
10421059
}
10431060

1044-
fn maybe_workspace_vec_string<'de, D>(
1045-
deserializer: D,
1046-
) -> Result<Option<MaybeWorkspace<Vec<String>>>, D::Error>
1047-
where
1048-
D: de::Deserializer<'de>,
1049-
{
1050-
struct Visitor;
1051-
1052-
impl<'de> de::Visitor<'de> for Visitor {
1053-
type Value = Option<MaybeWorkspace<Vec<String>>>;
1054-
1055-
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1056-
formatter.write_str("vector of strings")
1057-
}
1058-
1059-
fn visit_seq<V>(self, v: V) -> Result<Self::Value, V::Error>
1060-
where
1061-
V: de::SeqAccess<'de>,
1062-
{
1063-
let seq = de::value::SeqAccessDeserializer::new(v);
1064-
let defined = Vec::<String>::deserialize(seq).map(MaybeWorkspace::Defined)?;
1065-
Ok(Some(defined))
1066-
}
1067-
1068-
fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
1069-
where
1070-
V: de::MapAccess<'de>,
1071-
{
1072-
let mvd = de::value::MapAccessDeserializer::new(map);
1073-
let workspace = TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)?;
1074-
Ok(Some(workspace))
1075-
}
1076-
}
1077-
1078-
deserializer.deserialize_any(Visitor)
1079-
}
1080-
10811061
#[derive(Deserialize, Serialize, Clone, Debug)]
10821062
pub struct TomlWorkspaceField {
10831063
workspace: bool,
@@ -1097,8 +1077,6 @@ pub struct TomlProject {
10971077
name: InternedString,
10981078
#[serde(deserialize_with = "version_trim_whitespace")]
10991079
version: MaybeWorkspace<semver::Version>,
1100-
#[serde(default)]
1101-
#[serde(deserialize_with = "maybe_workspace_vec_string")]
11021080
authors: Option<MaybeWorkspace<Vec<String>>>,
11031081
build: Option<StringOrBool>,
11041082
metabuild: Option<StringOrVec>,
@@ -1107,11 +1085,7 @@ pub struct TomlProject {
11071085
#[serde(rename = "forced-target")]
11081086
forced_target: Option<String>,
11091087
links: Option<String>,
1110-
#[serde(default)]
1111-
#[serde(deserialize_with = "maybe_workspace_vec_string")]
11121088
exclude: Option<MaybeWorkspace<Vec<String>>>,
1113-
#[serde(default)]
1114-
#[serde(deserialize_with = "maybe_workspace_vec_string")]
11151089
include: Option<MaybeWorkspace<Vec<String>>>,
11161090
publish: Option<MaybeWorkspace<VecStringOrBool>>,
11171091
workspace: Option<String>,
@@ -1127,11 +1101,7 @@ pub struct TomlProject {
11271101
homepage: Option<MaybeWorkspace<String>>,
11281102
documentation: Option<MaybeWorkspace<String>>,
11291103
readme: Option<MaybeWorkspace<StringOrBool>>,
1130-
#[serde(default)]
1131-
#[serde(deserialize_with = "maybe_workspace_vec_string")]
11321104
keywords: Option<MaybeWorkspace<Vec<String>>>,
1133-
#[serde(default)]
1134-
#[serde(deserialize_with = "maybe_workspace_vec_string")]
11351105
categories: Option<MaybeWorkspace<Vec<String>>>,
11361106
license: Option<MaybeWorkspace<String>>,
11371107
license_file: Option<MaybeWorkspace<String>>,

tests/testsuite/metadata.rs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1725,7 +1725,55 @@ fn cargo_metadata_with_invalid_authors_field() {
17251725
r#"[ERROR] failed to parse manifest at `[..]`
17261726
17271727
Caused by:
1728-
invalid type: string "", expected vector of strings for key `package.authors`"#,
1728+
invalid type: string "", expected a sequence for key `package.authors`"#,
1729+
)
1730+
.run();
1731+
}
1732+
1733+
#[cargo_test]
1734+
fn cargo_metadata_with_invalid_version_field() {
1735+
let p = project()
1736+
.file("src/foo.rs", "")
1737+
.file(
1738+
"Cargo.toml",
1739+
r#"
1740+
[package]
1741+
version = 1
1742+
"#,
1743+
)
1744+
.build();
1745+
1746+
p.cargo("metadata")
1747+
.with_status(101)
1748+
.with_stderr(
1749+
r#"[ERROR] failed to parse manifest at `[..]`
1750+
1751+
Caused by:
1752+
invalid type: integer `1`, expected SemVer version for key `package.version`"#,
1753+
)
1754+
.run();
1755+
}
1756+
1757+
#[cargo_test]
1758+
fn cargo_metadata_with_invalid_publish_field() {
1759+
let p = project()
1760+
.file("src/foo.rs", "")
1761+
.file(
1762+
"Cargo.toml",
1763+
r#"
1764+
[package]
1765+
publish = "foo"
1766+
"#,
1767+
)
1768+
.build();
1769+
1770+
p.cargo("metadata")
1771+
.with_status(101)
1772+
.with_stderr(
1773+
r#"[ERROR] failed to parse manifest at `[..]`
1774+
1775+
Caused by:
1776+
invalid type: string "foo", expected a boolean or vector of strings for key `package.publish`"#,
17291777
)
17301778
.run();
17311779
}

0 commit comments

Comments
 (0)