aboutsummaryrefslogtreecommitdiff
path: root/crate_universe/src/config.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crate_universe/src/config.rs')
-rw-r--r--crate_universe/src/config.rs447
1 files changed, 309 insertions, 138 deletions
diff --git a/crate_universe/src/config.rs b/crate_universe/src/config.rs
index 46f075c6..6a5913d0 100644
--- a/crate_universe/src/config.rs
+++ b/crate_universe/src/config.rs
@@ -1,18 +1,21 @@
//! A module for configuration information
+use std::cmp::Ordering;
use std::collections::{BTreeMap, BTreeSet};
use std::convert::AsRef;
+use std::fmt::Formatter;
use std::iter::Sum;
use std::ops::Add;
use std::path::Path;
+use std::str::FromStr;
use std::{fmt, fs};
-use anyhow::Result;
+use anyhow::{Context, Result};
use cargo_lock::package::GitReference;
use cargo_metadata::Package;
use semver::VersionReq;
use serde::de::value::SeqAccessDeserializer;
-use serde::de::{Deserializer, SeqAccess, Visitor};
+use serde::de::{Deserializer, SeqAccess, Unexpected, Visitor};
use serde::{Deserialize, Serialize, Serializer};
use crate::select::{Select, Selectable};
@@ -22,7 +25,7 @@ use crate::utils::target_triple::TargetTriple;
/// Representations of different kinds of crate vendoring into workspaces.
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "lowercase")]
-pub enum VendorMode {
+pub(crate) enum VendorMode {
/// Crates having full source being vendored into a workspace
Local,
@@ -44,58 +47,58 @@ impl std::fmt::Display for VendorMode {
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(deny_unknown_fields)]
-pub struct RenderConfig {
+pub(crate) struct RenderConfig {
/// The name of the repository being rendered
- pub repository_name: String,
+ pub(crate) repository_name: String,
/// The pattern to use for BUILD file names.
/// Eg. `//:BUILD.{name}-{version}.bazel`
#[serde(default = "default_build_file_template")]
- pub build_file_template: String,
+ pub(crate) build_file_template: String,
/// The pattern to use for a crate target.
/// Eg. `@{repository}__{name}-{version}//:{target}`
#[serde(default = "default_crate_label_template")]
- pub crate_label_template: String,
+ pub(crate) crate_label_template: String,
/// The pattern to use for the `defs.bzl` and `BUILD.bazel`
/// file names used for the crates module.
/// Eg. `//:{file}`
#[serde(default = "default_crates_module_template")]
- pub crates_module_template: String,
+ pub(crate) crates_module_template: String,
/// The pattern used for a crate's repository name.
/// Eg. `{repository}__{name}-{version}`
#[serde(default = "default_crate_repository_template")]
- pub crate_repository_template: String,
+ pub(crate) crate_repository_template: String,
/// Default alias rule to use for packages. Can be overridden by annotations.
#[serde(default)]
- pub default_alias_rule: AliasRule,
+ pub(crate) default_alias_rule: AliasRule,
/// The default of the `package_name` parameter to use for the module macros like `all_crate_deps`.
/// In general, this should be be unset to allow the macros to do auto-detection in the analysis phase.
- pub default_package_name: Option<String>,
+ pub(crate) default_package_name: Option<String>,
/// Whether to generate `target_compatible_with` annotations on the generated BUILD files. This
/// catches a `target_triple`being targeted that isn't declared in `supported_platform_triples`.
#[serde(default = "default_generate_target_compatible_with")]
- pub generate_target_compatible_with: bool,
+ pub(crate) generate_target_compatible_with: bool,
/// The pattern to use for platform constraints.
/// Eg. `@rules_rust//rust/platform:{triple}`.
#[serde(default = "default_platforms_template")]
- pub platforms_template: String,
+ pub(crate) platforms_template: String,
/// The command to use for regenerating generated files.
- pub regen_command: String,
+ pub(crate) regen_command: String,
/// An optional configuration for rendering content to be rendered into repositories.
- pub vendor_mode: Option<VendorMode>,
+ pub(crate) vendor_mode: Option<VendorMode>,
/// Whether to generate package metadata
#[serde(default = "default_generate_rules_license_metadata")]
- pub generate_rules_license_metadata: bool,
+ pub(crate) generate_rules_license_metadata: bool,
}
// Default is manually implemented so that the default values match the default
@@ -150,7 +153,7 @@ fn default_generate_rules_license_metadata() -> bool {
/// A representation of some Git identifier used to represent the "revision" or "pin" of a checkout.
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
-pub enum Commitish {
+pub(crate) enum Commitish {
/// From a tag.
Tag(String),
@@ -173,7 +176,7 @@ impl From<GitReference> for Commitish {
/// Information representing deterministic identifiers for some remote asset.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
-pub enum Checksumish {
+pub(crate) enum Checksumish {
Http {
/// The sha256 digest of an http archive
sha256: Option<String>,
@@ -190,7 +193,7 @@ pub enum Checksumish {
}
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Clone)]
-pub enum AliasRule {
+pub(crate) enum AliasRule {
#[default]
#[serde(rename = "alias")]
Alias,
@@ -205,7 +208,7 @@ pub enum AliasRule {
}
impl AliasRule {
- pub fn bzl(&self) -> Option<String> {
+ pub(crate) fn bzl(&self) -> Option<String> {
match self {
AliasRule::Alias => None,
AliasRule::Dbg | AliasRule::Fastbuild | AliasRule::Opt => {
@@ -215,7 +218,7 @@ impl AliasRule {
}
}
- pub fn rule(&self) -> String {
+ pub(crate) fn rule(&self) -> String {
match self {
AliasRule::Alias => "alias".to_owned(),
AliasRule::Dbg => "transition_alias_dbg".to_owned(),
@@ -227,115 +230,115 @@ impl AliasRule {
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct CrateAnnotations {
+pub(crate) struct CrateAnnotations {
/// Which subset of the crate's bins should get produced as `rust_binary` targets.
- pub gen_binaries: Option<GenBinaries>,
+ pub(crate) gen_binaries: Option<GenBinaries>,
/// Determins whether or not Cargo build scripts should be generated for the current package
- pub gen_build_script: Option<bool>,
+ pub(crate) gen_build_script: Option<bool>,
/// Additional data to pass to
/// [deps](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-deps) attribute.
- pub deps: Option<Select<BTreeSet<Label>>>,
+ pub(crate) deps: Option<Select<BTreeSet<Label>>>,
/// Additional data to pass to
/// [proc_macro_deps](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-proc_macro_deps) attribute.
- pub proc_macro_deps: Option<Select<BTreeSet<Label>>>,
+ pub(crate) proc_macro_deps: Option<Select<BTreeSet<Label>>>,
/// Additional data to pass to the target's
/// [crate_features](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-crate_features) attribute.
- pub crate_features: Option<Select<BTreeSet<String>>>,
+ pub(crate) crate_features: Option<Select<BTreeSet<String>>>,
/// Additional data to pass to the target's
/// [data](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-data) attribute.
- pub data: Option<Select<BTreeSet<Label>>>,
+ pub(crate) data: Option<Select<BTreeSet<Label>>>,
/// An optional glob pattern to set on the
/// [data](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-data) attribute.
- pub data_glob: Option<BTreeSet<String>>,
+ pub(crate) data_glob: Option<BTreeSet<String>>,
/// Additional data to pass to
/// [compile_data](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-compile_data) attribute.
- pub compile_data: Option<Select<BTreeSet<Label>>>,
+ pub(crate) compile_data: Option<Select<BTreeSet<Label>>>,
/// An optional glob pattern to set on the
/// [compile_data](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-compile_data) attribute.
- pub compile_data_glob: Option<BTreeSet<String>>,
+ pub(crate) compile_data_glob: Option<BTreeSet<String>>,
/// If true, disables pipelining for library targets generated for this crate.
- pub disable_pipelining: bool,
+ pub(crate) disable_pipelining: bool,
/// Additional data to pass to the target's
/// [rustc_env](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-rustc_env) attribute.
- pub rustc_env: Option<Select<BTreeMap<String, String>>>,
+ pub(crate) rustc_env: Option<Select<BTreeMap<String, String>>>,
/// Additional data to pass to the target's
/// [rustc_env_files](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-rustc_env_files) attribute.
- pub rustc_env_files: Option<Select<BTreeSet<String>>>,
+ pub(crate) rustc_env_files: Option<Select<BTreeSet<String>>>,
/// Additional data to pass to the target's
/// [rustc_flags](https://bazelbuild.github.io/rules_rust/defs.html#rust_library-rustc_flags) attribute.
- pub rustc_flags: Option<Select<Vec<String>>>,
+ pub(crate) rustc_flags: Option<Select<Vec<String>>>,
/// Additional dependencies to pass to a build script's
/// [deps](https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script-deps) attribute.
- pub build_script_deps: Option<Select<BTreeSet<Label>>>,
+ pub(crate) build_script_deps: Option<Select<BTreeSet<Label>>>,
/// Additional data to pass to a build script's
/// [proc_macro_deps](https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script-proc_macro_deps) attribute.
- pub build_script_proc_macro_deps: Option<Select<BTreeSet<Label>>>,
+ pub(crate) build_script_proc_macro_deps: Option<Select<BTreeSet<Label>>>,
/// Additional data to pass to a build script's
/// [build_script_data](https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script-data) attribute.
- pub build_script_data: Option<Select<BTreeSet<Label>>>,
+ pub(crate) build_script_data: Option<Select<BTreeSet<Label>>>,
/// Additional data to pass to a build script's
/// [tools](https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script-tools) attribute.
- pub build_script_tools: Option<Select<BTreeSet<Label>>>,
+ pub(crate) build_script_tools: Option<Select<BTreeSet<Label>>>,
/// An optional glob pattern to set on the
/// [build_script_data](https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script-build_script_env) attribute.
- pub build_script_data_glob: Option<BTreeSet<String>>,
+ pub(crate) build_script_data_glob: Option<BTreeSet<String>>,
/// Additional environment variables to pass to a build script's
/// [build_script_env](https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script-rustc_env) attribute.
- pub build_script_env: Option<Select<BTreeMap<String, String>>>,
+ pub(crate) build_script_env: Option<Select<BTreeMap<String, String>>>,
/// Additional rustc_env flags to pass to a build script's
/// [rustc_env](https://bazelbuild.github.io/rules_rust/cargo.html#cargo_build_script-rustc_env) attribute.
- pub build_script_rustc_env: Option<Select<BTreeMap<String, String>>>,
+ pub(crate) build_script_rustc_env: Option<Select<BTreeMap<String, String>>>,
/// Additional labels to pass to a build script's
/// [toolchains](https://bazel.build/reference/be/common-definitions#common-attributes) attribute.
- pub build_script_toolchains: Option<BTreeSet<Label>>,
+ pub(crate) build_script_toolchains: Option<BTreeSet<Label>>,
/// Directory to run the crate's build script in. If not set, will run in the manifest directory, otherwise a directory relative to the exec root.
- pub build_script_rundir: Option<Select<String>>,
+ pub(crate) build_script_rundir: Option<Select<String>>,
/// A scratch pad used to write arbitrary text to target BUILD files.
- pub additive_build_file_content: Option<String>,
+ pub(crate) additive_build_file_content: Option<String>,
/// For git sourced crates, this is a the
/// [git_repository::shallow_since](https://docs.bazel.build/versions/main/repo/git.html#new_git_repository-shallow_since) attribute.
- pub shallow_since: Option<String>,
+ pub(crate) shallow_since: Option<String>,
/// The `patch_args` attribute of a Bazel repository rule. See
/// [http_archive.patch_args](https://docs.bazel.build/versions/main/repo/http.html#http_archive-patch_args)
- pub patch_args: Option<Vec<String>>,
+ pub(crate) patch_args: Option<Vec<String>>,
/// The `patch_tool` attribute of a Bazel repository rule. See
/// [http_archive.patch_tool](https://docs.bazel.build/versions/main/repo/http.html#http_archive-patch_tool)
- pub patch_tool: Option<String>,
+ pub(crate) patch_tool: Option<String>,
/// The `patches` attribute of a Bazel repository rule. See
/// [http_archive.patches](https://docs.bazel.build/versions/main/repo/http.html#http_archive-patches)
- pub patches: Option<BTreeSet<String>>,
+ pub(crate) patches: Option<BTreeSet<String>>,
/// Extra targets the should be aliased during rendering.
- pub extra_aliased_targets: Option<BTreeMap<String, String>>,
+ pub(crate) extra_aliased_targets: Option<BTreeMap<String, String>>,
/// Transition rule to use instead of `native.alias()`.
- pub alias_rule: Option<AliasRule>,
+ pub(crate) alias_rule: Option<AliasRule>,
}
macro_rules! joined_extra_member {
@@ -437,25 +440,28 @@ impl Sum for CrateAnnotations {
/// not specify a different value for the same annotation in their
/// crates_repository attributes.
#[derive(Debug, Deserialize)]
-pub struct AnnotationsProvidedByPackage {
- pub gen_build_script: Option<bool>,
- pub data: Option<Select<BTreeSet<Label>>>,
- pub data_glob: Option<BTreeSet<String>>,
- pub deps: Option<Select<BTreeSet<Label>>>,
- pub compile_data: Option<Select<BTreeSet<Label>>>,
- pub compile_data_glob: Option<BTreeSet<String>>,
- pub rustc_env: Option<Select<BTreeMap<String, String>>>,
- pub rustc_env_files: Option<Select<BTreeSet<String>>>,
- pub rustc_flags: Option<Select<Vec<String>>>,
- pub build_script_env: Option<Select<BTreeMap<String, String>>>,
- pub build_script_rustc_env: Option<Select<BTreeMap<String, String>>>,
- pub build_script_rundir: Option<Select<String>>,
- pub additive_build_file_content: Option<String>,
- pub extra_aliased_targets: Option<BTreeMap<String, String>>,
+pub(crate) struct AnnotationsProvidedByPackage {
+ pub(crate) gen_build_script: Option<bool>,
+ pub(crate) data: Option<Select<BTreeSet<Label>>>,
+ pub(crate) data_glob: Option<BTreeSet<String>>,
+ pub(crate) deps: Option<Select<BTreeSet<Label>>>,
+ pub(crate) compile_data: Option<Select<BTreeSet<Label>>>,
+ pub(crate) compile_data_glob: Option<BTreeSet<String>>,
+ pub(crate) rustc_env: Option<Select<BTreeMap<String, String>>>,
+ pub(crate) rustc_env_files: Option<Select<BTreeSet<String>>>,
+ pub(crate) rustc_flags: Option<Select<Vec<String>>>,
+ pub(crate) build_script_env: Option<Select<BTreeMap<String, String>>>,
+ pub(crate) build_script_rustc_env: Option<Select<BTreeMap<String, String>>>,
+ pub(crate) build_script_rundir: Option<Select<String>>,
+ pub(crate) additive_build_file_content: Option<String>,
+ pub(crate) extra_aliased_targets: Option<BTreeMap<String, String>>,
}
impl CrateAnnotations {
- pub fn apply_defaults_from_package_metadata(&mut self, pkg_metadata: &serde_json::Value) {
+ pub(crate) fn apply_defaults_from_package_metadata(
+ &mut self,
+ pkg_metadata: &serde_json::Value,
+ ) {
#[deny(unused_variables)]
let AnnotationsProvidedByPackage {
gen_build_script,
@@ -516,55 +522,21 @@ pub struct CrateId {
pub name: String,
/// The crate's semantic version
- pub version: String,
+ pub version: semver::Version,
}
impl CrateId {
/// Construct a new [CrateId]
- pub fn new(name: String, version: String) -> Self {
+ pub(crate) fn new(name: String, version: semver::Version) -> Self {
Self { name, version }
}
-
- /// Compares a [CrateId] against a [cargo_metadata::Package].
- pub fn matches(&self, package: &Package) -> bool {
- // If the package name does not match, it's obviously
- // not the right package
- if self.name != "*" && self.name != package.name {
- return false;
- }
-
- // First see if the package version matches exactly
- if package.version.to_string() == self.version {
- return true;
- }
-
- // If the version provided is the wildcard "*", it matches. Do not
- // delegate to the semver crate in this case because semver does not
- // consider "*" to match prerelease packages. That's expected behavior
- // in the context of declaring package dependencies, but not in the
- // context of declaring which versions of preselected packages an
- // annotation applies to.
- if self.version == "*" {
- return true;
- }
-
- // Next, check to see if the version provided is a semver req and
- // check if the package matches the condition
- if let Ok(semver) = VersionReq::parse(&self.version) {
- if semver.matches(&package.version) {
- return true;
- }
- }
-
- false
- }
}
impl From<&Package> for CrateId {
fn from(package: &Package) -> Self {
Self {
name: package.name.clone(),
- version: package.version.to_string(),
+ version: package.version.clone(),
}
}
}
@@ -590,16 +562,20 @@ impl<'de> Visitor<'de> for CrateIdVisitor {
where
E: serde::de::Error,
{
- v.rsplit_once(' ')
- .map(|(name, version)| CrateId {
- name: name.to_string(),
- version: version.to_string(),
- })
- .ok_or_else(|| {
- E::custom(format!(
- "Expected string value of `{{name}} {{version}}`. Got '{v}'"
- ))
- })
+ let (name, version_str) = v.rsplit_once(' ').ok_or_else(|| {
+ E::custom(format!(
+ "Expected string value of `{{name}} {{version}}`. Got '{v}'"
+ ))
+ })?;
+ let version = semver::Version::parse(version_str).map_err(|err| {
+ E::custom(format!(
+ "Couldn't parse {version_str} as a semver::Version: {err}"
+ ))
+ })?;
+ Ok(CrateId {
+ name: name.to_string(),
+ version,
+ })
}
}
@@ -619,7 +595,7 @@ impl std::fmt::Display for CrateId {
}
#[derive(Debug, Hash, Clone, PartialEq, Eq)]
-pub enum GenBinaries {
+pub(crate) enum GenBinaries {
All,
Some(BTreeSet<String>),
}
@@ -679,35 +655,207 @@ impl<'de> Visitor<'de> for GenBinariesVisitor {
/// Workspace specific settings to control how targets are generated
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
#[serde(deny_unknown_fields)]
-pub struct Config {
+pub(crate) struct Config {
/// Whether to generate `rust_binary` targets for all bins by default
- pub generate_binaries: bool,
+ pub(crate) generate_binaries: bool,
/// Whether or not to generate Cargo build scripts by default
- pub generate_build_scripts: bool,
+ pub(crate) generate_build_scripts: bool,
/// Additional settings to apply to generated crates
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
- pub annotations: BTreeMap<CrateId, CrateAnnotations>,
+ pub(crate) annotations: BTreeMap<CrateNameAndVersionReq, CrateAnnotations>,
/// Settings used to determine various render info
- pub rendering: RenderConfig,
+ pub(crate) rendering: RenderConfig,
/// The contents of a Cargo configuration file
- pub cargo_config: Option<toml::Value>,
+ pub(crate) cargo_config: Option<toml::Value>,
/// A set of platform triples to use in generated select statements
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
- pub supported_platform_triples: BTreeSet<TargetTriple>,
+ pub(crate) supported_platform_triples: BTreeSet<TargetTriple>,
}
impl Config {
- pub fn try_from_path<T: AsRef<Path>>(path: T) -> Result<Self> {
+ pub(crate) fn try_from_path<T: AsRef<Path>>(path: T) -> Result<Self> {
let data = fs::read_to_string(path)?;
Ok(serde_json::from_str(&data)?)
}
}
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct CrateNameAndVersionReq {
+ /// The name of the crate
+ pub name: String,
+
+ version_req_string: VersionReqString,
+}
+
+impl Serialize for CrateNameAndVersionReq {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ serializer.serialize_str(&format!(
+ "{} {}",
+ self.name, self.version_req_string.original
+ ))
+ }
+}
+
+struct CrateNameAndVersionReqVisitor;
+impl<'de> Visitor<'de> for CrateNameAndVersionReqVisitor {
+ type Value = CrateNameAndVersionReq;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str("Expected string value of `{name} {version}`.")
+ }
+
+ fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ let (name, version) = v.rsplit_once(' ').ok_or_else(|| {
+ E::custom(format!(
+ "Expected string value of `{{name}} {{version}}`. Got '{v}'"
+ ))
+ })?;
+ version
+ .parse()
+ .map(|version| CrateNameAndVersionReq {
+ name: name.to_string(),
+ version_req_string: version,
+ })
+ .map_err(|err| E::custom(err.to_string()))
+ }
+}
+
+impl<'de> Deserialize<'de> for CrateNameAndVersionReq {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ deserializer.deserialize_str(CrateNameAndVersionReqVisitor)
+ }
+}
+
+/// A version requirement (i.e. a semver::VersionReq) which preserves the original string it was parsed from.
+/// This means that you can report back to the user whether they wrote `1` or `1.0.0` or `^1.0.0` or `>=1,<2`,
+/// and support exact round-trip serialization and deserialization.
+#[derive(Clone, Debug)]
+pub struct VersionReqString {
+ original: String,
+
+ parsed: VersionReq,
+}
+
+impl FromStr for VersionReqString {
+ type Err = anyhow::Error;
+
+ fn from_str(original: &str) -> Result<Self, Self::Err> {
+ let parsed = VersionReq::parse(original)
+ .context("VersionReqString must be a valid semver requirement")?;
+ Ok(VersionReqString {
+ original: original.to_owned(),
+ parsed,
+ })
+ }
+}
+
+impl PartialEq for VersionReqString {
+ fn eq(&self, other: &Self) -> bool {
+ self.original == other.original
+ }
+}
+
+impl Eq for VersionReqString {}
+
+impl PartialOrd for VersionReqString {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for VersionReqString {
+ fn cmp(&self, other: &Self) -> Ordering {
+ Ord::cmp(&self.original, &other.original)
+ }
+}
+
+impl Serialize for VersionReqString {
+ fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ serializer.serialize_str(&self.original)
+ }
+}
+
+impl<'de> Deserialize<'de> for VersionReqString {
+ fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ struct StringVisitor;
+
+ impl<'de> Visitor<'de> for StringVisitor {
+ type Value = String;
+
+ fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
+ formatter.write_str("string of a semver requirement")
+ }
+ }
+
+ let original = deserializer.deserialize_str(StringVisitor)?;
+ let parsed = VersionReq::parse(&original).map_err(|_| {
+ serde::de::Error::invalid_value(
+ Unexpected::Str(&original),
+ &"a valid semver requirement",
+ )
+ })?;
+ Ok(VersionReqString { original, parsed })
+ }
+}
+
+impl CrateNameAndVersionReq {
+ #[cfg(test)]
+ pub fn new(name: String, version_req_string: VersionReqString) -> CrateNameAndVersionReq {
+ CrateNameAndVersionReq {
+ name,
+ version_req_string,
+ }
+ }
+
+ /// Compares a [CrateNameAndVersionReq] against a [cargo_metadata::Package].
+ pub fn matches(&self, package: &Package) -> bool {
+ // If the package name does not match, it's obviously
+ // not the right package
+ if self.name != "*" && self.name != package.name {
+ return false;
+ }
+
+ // First see if the package version matches exactly
+ if package.version.to_string() == self.version_req_string.original {
+ return true;
+ }
+
+ // If the version provided is the wildcard "*", it matches. Do not
+ // delegate to the semver crate in this case because semver does not
+ // consider "*" to match prerelease packages. That's expected behavior
+ // in the context of declaring package dependencies, but not in the
+ // context of declaring which versions of preselected packages an
+ // annotation applies to.
+ if self.version_req_string.original == "*" {
+ return true;
+ }
+
+ // Next, check to see if the version provided is a semver req and
+ // check if the package matches the condition
+ self.version_req_string.parsed.matches(&package.version)
+ }
+}
+
#[cfg(test)]
mod test {
use super::*;
@@ -717,21 +865,17 @@ mod test {
#[test]
fn test_crate_id_serde() {
let id: CrateId = serde_json::from_str("\"crate 0.1.0\"").unwrap();
- assert_eq!(id, CrateId::new("crate".to_owned(), "0.1.0".to_owned()));
+ assert_eq!(
+ id,
+ CrateId::new("crate".to_owned(), semver::Version::new(0, 1, 0))
+ );
assert_eq!(serde_json::to_string(&id).unwrap(), "\"crate 0.1.0\"");
}
#[test]
- fn test_crate_id_serde_semver() {
- let semver_id: CrateId = serde_json::from_str("\"crate *\"").unwrap();
- assert_eq!(semver_id, CrateId::new("crate".to_owned(), "*".to_owned()));
- assert_eq!(serde_json::to_string(&semver_id).unwrap(), "\"crate *\"");
- }
-
- #[test]
fn test_crate_id_matches() {
let mut package = mock_cargo_metadata_package();
- let id = CrateId::new("mock-pkg".to_owned(), "0.1.0".to_owned());
+ let id = CrateNameAndVersionReq::new("mock-pkg".to_owned(), "0.1.0".parse().unwrap());
package.version = cargo_metadata::semver::Version::new(0, 1, 0);
assert!(id.matches(&package));
@@ -741,19 +885,43 @@ mod test {
}
#[test]
- fn test_crate_id_semver_matches() {
+ fn test_crate_name_and_version_req_serde() {
+ let id: CrateNameAndVersionReq = serde_json::from_str("\"crate 0.1.0\"").unwrap();
+ assert_eq!(
+ id,
+ CrateNameAndVersionReq::new(
+ "crate".to_owned(),
+ VersionReqString::from_str("0.1.0").unwrap()
+ )
+ );
+ assert_eq!(serde_json::to_string(&id).unwrap(), "\"crate 0.1.0\"");
+ }
+
+ #[test]
+ fn test_crate_name_and_version_req_serde_semver() {
+ let id: CrateNameAndVersionReq = serde_json::from_str("\"crate *\"").unwrap();
+ assert_eq!(
+ id,
+ CrateNameAndVersionReq::new(
+ "crate".to_owned(),
+ VersionReqString::from_str("*").unwrap()
+ )
+ );
+ assert_eq!(serde_json::to_string(&id).unwrap(), "\"crate *\"");
+ }
+
+ #[test]
+ fn test_crate_name_and_version_req_semver_matches() {
let mut package = mock_cargo_metadata_package();
package.version = cargo_metadata::semver::Version::new(1, 0, 0);
- let mut id = CrateId::new("mock-pkg".to_owned(), "0.1.0".to_owned());
-
- id.version = "*".to_owned();
+ let id = CrateNameAndVersionReq::new("mock-pkg".to_owned(), "*".parse().unwrap());
assert!(id.matches(&package));
let mut prerelease = mock_cargo_metadata_package();
prerelease.version = cargo_metadata::semver::Version::parse("1.0.0-pre.0").unwrap();
assert!(id.matches(&prerelease));
- id.version = "<1".to_owned();
+ let id = CrateNameAndVersionReq::new("mock-pkg".to_owned(), "<1".parse().unwrap());
assert!(!id.matches(&package));
}
@@ -770,7 +938,10 @@ mod test {
// Annotations
let annotation = config
.annotations
- .get(&CrateId::new("rand".to_owned(), "0.8.5".to_owned()))
+ .get(&CrateNameAndVersionReq::new(
+ "rand".to_owned(),
+ "0.8.5".parse().unwrap(),
+ ))
.unwrap();
assert_eq!(
annotation.crate_features,