Skip to content

Commit a0623ce

Browse files
committed
refactor(core): validate overrides up front and let PackageSpec key resolution
1 parent e0eb6e1 commit a0623ce

6 files changed

Lines changed: 214 additions & 176 deletions

File tree

‎crates/wasm-pkg-common/src/package.rs‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,19 @@ pub struct PackageSpec {
8585
pub version: Option<Version>,
8686
}
8787

88+
impl PackageSpec {
89+
/// The requirement used to fetch this package from a registry: exactly its version, or any
90+
/// version (`*`) for a package that names none.
91+
pub fn version_req(&self) -> VersionReq {
92+
match &self.version {
93+
Some(version) => format!("={version}")
94+
.parse()
95+
.expect("an exact version is always a valid requirement"),
96+
None => VersionReq::STAR,
97+
}
98+
}
99+
}
100+
88101
impl PartialEq<str> for PackageSpec {
89102
fn eq(&self, other: &str) -> bool {
90103
// clippy --fix will create a recursive callsite here if `self.to_string()` is used instead

‎crates/wasm-pkg-core/src/manifest.rs‎

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Type definitions and functions for working with `wkg.toml` files.
22
33
use std::{
4-
collections::{BTreeSet, HashMap, HashSet},
4+
collections::{BTreeSet, HashMap},
55
path::{Path, PathBuf},
66
};
77

@@ -43,7 +43,8 @@ pub struct Manifest {
4343
}
4444

4545
impl Manifest {
46-
fn from_toml(contents: &str) -> Result<Manifest> {
46+
/// Parses and validates a manifest from its TOML contents.
47+
pub fn from_toml(contents: &str) -> Result<Manifest> {
4748
let manifest: Manifest = toml::from_str(contents)?;
4849
manifest.validate()?;
4950
Ok(manifest)
@@ -87,44 +88,52 @@ impl Manifest {
8788

8889
/// Checks that override keys parse and that no package is covered by both a bare and a
8990
/// versioned key.
90-
9191
pub(crate) fn validate_override_keys(&self) -> Result<()> {
9292
let Some(overrides) = self.overrides.as_ref() else {
9393
return Ok(());
9494
};
9595
// `overrides` is a map, so walk it in a stable order
9696
let sorted_keys: BTreeSet<&str> = overrides.keys().map(String::as_str).collect();
9797

98-
let mut bare: HashSet<PackageRef> = HashSet::new();
99-
let mut versioned: HashMap<PackageRef, Vec<&str>> = HashMap::new();
98+
// A bare key (`ns:pkg`) covers every version of its package, a versioned key
99+
// (`ns:pkg@1.2.3`) exactly one, so a package may be named by one kind or the other but
100+
// not both
101+
let mut bare: BTreeSet<PackageRef> = BTreeSet::new();
102+
let mut versioned: Vec<(&str, PackageRef)> = Vec::new();
100103
for key in sorted_keys {
101104
let spec: PackageSpec = key
102105
.parse()
103106
.with_context(|| format!("invalid override key `{key}`"))?;
104107
match spec.version {
105-
Some(_) => versioned.entry(spec.package).or_default().push(key),
108+
Some(_) => versioned.push((key, spec.package)),
106109
None => {
107110
bare.insert(spec.package);
108111
}
109112
}
110113
}
111-
112-
let mut conflicts: Vec<String> = versioned
114+
let conflicts: Vec<String> = bare
113115
.iter()
114-
.filter(|(package, _)| bare.contains(*package))
115-
.map(|(package, keys)| {
116-
format!(
117-
"override `{package}` applies to every version of the package, so it \
118-
conflicts with the versioned override(s) `{}`",
119-
keys.join("`, `")
120-
)
116+
.filter_map(|package| {
117+
let keys: Vec<&str> = versioned
118+
.iter()
119+
.filter(|(_, p)| p == package)
120+
.map(|(key, _)| *key)
121+
.collect();
122+
(!keys.is_empty()).then(|| {
123+
format!(
124+
"`{package}` covers every version and overlaps `{}`",
125+
keys.join("`, `")
126+
)
127+
})
121128
})
122129
.collect();
123130
if conflicts.is_empty() {
124131
return Ok(());
125132
}
126-
conflicts.sort_unstable();
127-
anyhow::bail!("{} - remove one or the other", conflicts.join("; "));
133+
anyhow::bail!(
134+
"overrides must not overlap: {} - remove one or the other",
135+
conflicts.join("; ")
136+
);
128137
}
129138

130139
// no overrides or top-level metadata when workspace is present

0 commit comments

Comments
 (0)