Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions crates/rattler_conda_types/src/match_spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ pub struct MatchSpec {
pub url: Option<Url>,
/// The license of the package
pub license: Option<String>,
/// The license family of the package (e.g. `MIT`, `GPL`, `BSD`)
pub license_family: Option<String>,
/// The features of the package (deprecated conda field, e.g. `blas`)
pub features: Option<String>,
/// The condition under which this match spec applies.
pub condition: Option<MatchSpecCondition>,
/// The track features of the package
Expand Down Expand Up @@ -229,6 +233,14 @@ impl Display for MatchSpec {
keys.push(format!("license=\"{license}\""));
}

if let Some(license_family) = &self.license_family {
keys.push(format!("license_family=\"{license_family}\""));
}

if let Some(features) = &self.features {
keys.push(format!("features=\"{features}\""));
}

if let Some(track_features) = &self.track_features {
keys.push(format!(
"track_features=\"{}\"",
Expand Down Expand Up @@ -267,6 +279,8 @@ impl MatchSpec {
sha256: self.sha256,
url: self.url,
license: self.license,
license_family: self.license_family,
features: self.features,
condition: self.condition,
track_features: self.track_features,
},
Expand Down Expand Up @@ -329,6 +343,10 @@ pub struct NamelessMatchSpec {
pub url: Option<Url>,
/// The license of the package
pub license: Option<String>,
/// The license family of the package (e.g. `MIT`, `GPL`, `BSD`)
pub license_family: Option<String>,
/// The features of the package
pub features: Option<String>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Features are not used anymore so this is not needed.

/// The condition under which this match spec applies.
pub condition: Option<MatchSpecCondition>,
/// The track features of the package
Expand Down Expand Up @@ -356,6 +374,14 @@ impl Display for NamelessMatchSpec {
keys.push(format!("sha256=\"{sha256:x}\""));
}

if let Some(license_family) = &self.license_family {
keys.push(format!("license_family=\"{license_family}\""));
}

if let Some(features) = &self.features {
keys.push(format!("features=\"{features}\""));
}

if let Some(condition) = &self.condition {
let condition_str = condition.to_string();
keys.push(format!("when=\"{}\"", escape_bracket_value(&condition_str)));
Expand Down Expand Up @@ -384,6 +410,8 @@ impl From<MatchSpec> for NamelessMatchSpec {
sha256: spec.sha256,
url: spec.url,
license: spec.license,
license_family: spec.license_family,
features: spec.features,
condition: spec.condition,
track_features: spec.track_features,
}
Expand All @@ -407,6 +435,8 @@ impl MatchSpec {
sha256: spec.sha256,
url: spec.url,
license: spec.license,
license_family: spec.license_family,
features: spec.features,
condition: spec.condition,
track_features: spec.track_features,
}
Expand Down Expand Up @@ -482,6 +512,18 @@ impl Matches<PackageRecord> for NamelessMatchSpec {
}
}

if let Some(license_family) = self.license_family.as_ref() {
if Some(license_family) != other.license_family.as_ref() {
return false;
}
}

if let Some(features) = self.features.as_ref() {
if Some(features) != other.features.as_ref() {
return false;
}
}

if let Some(track_features) = self.track_features.as_ref() {
for feature in track_features {
if !other.track_features.contains(feature) {
Expand Down Expand Up @@ -537,6 +579,18 @@ impl Matches<PackageRecord> for MatchSpec {
}
}

if let Some(license_family) = self.license_family.as_ref() {
if Some(license_family) != other.license_family.as_ref() {
return false;
}
}

if let Some(features) = self.features.as_ref() {
if Some(features) != other.features.as_ref() {
return false;
}
}

if let Some(track_features) = self.track_features.as_ref() {
for feature in track_features {
if !other.track_features.contains(feature) {
Expand Down
51 changes: 49 additions & 2 deletions crates/rattler_conda_types/src/match_spec/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,8 +520,8 @@ fn parse_bracket_vec_into_components(
return Err(ParseMatchSpecError::InvalidBracketKey("when".to_string()));
}
}
// TODO: Still need to add `features` and `license_family`
// to the match spec.
"license_family" => match_spec.license_family = Some(value.to_string()),
"features" => match_spec.features = Some(value.to_string()),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Features have been deprecated and can be removed here.

_ => Err(ParseMatchSpecError::InvalidBracketKey(key.to_owned()))?,
}
}
Expand Down Expand Up @@ -1610,6 +1610,51 @@ mod tests {
assert_eq!(spec.license, Some("MIT".into()));
}

#[test]
fn test_parsing_license_family() {
let spec = MatchSpec::from_str("python[license_family=MIT]", Strict).unwrap();
assert_eq!(spec.license_family, Some("MIT".into()));

// Roundtrip: Display -> parse must produce the same spec.
let reparsed = MatchSpec::from_str(&spec.to_string(), Strict).unwrap();
assert_eq!(reparsed.license_family, Some("MIT".into()));
}

#[test]
fn test_parsing_features() {
let spec = MatchSpec::from_str("numpy[features=blas]", Strict).unwrap();
assert_eq!(spec.features, Some("blas".into()));

// Roundtrip: Display -> parse must produce the same spec.
let reparsed = MatchSpec::from_str(&spec.to_string(), Strict).unwrap();
assert_eq!(reparsed.features, Some("blas".into()));
}

#[test]
fn test_features_and_license_family_matching() {
use crate::{match_spec::Matches, PackageName, PackageRecord, Version};

let mut record = PackageRecord::new(
PackageName::from_str("numpy").unwrap(),
Version::from_str("1.24.0").unwrap(),
"py310h1234_0".to_string(),
);
record.features = Some("blas".to_string());
record.license_family = Some("MIT".to_string());

// Both fields match.
let spec = MatchSpec::from_str("numpy[license_family=MIT, features=blas]", Strict).unwrap();
assert!(spec.matches(&record));

// license_family mismatch.
let spec = MatchSpec::from_str("numpy[license_family=GPL]", Strict).unwrap();
assert!(!spec.matches(&record));

// features mismatch.
let spec = MatchSpec::from_str("numpy[features=openblas]", Strict).unwrap();
assert!(!spec.matches(&record));
}

#[test]
fn test_parsing_track_features() {
let cases = vec![
Expand Down Expand Up @@ -1714,6 +1759,8 @@ mod tests {
.unwrap(),
),
license: Some("MIT".into()),
license_family: Some("MIT".into()),
features: Some("blas".into()),
condition: None,
track_features: None,
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
---
source: crates/rattler_conda_types/src/match_spec/parse.rs
assertion_line: 1770
expression: vec_strings
---
[
"foo 1.0.*[build_number=\">6\"]",
"conda-forge/linux-64:foospace:foo 1.0.* py27_0*[md5=\"8b1a9953c4611296a827abf8c47804d7\", sha256=\"315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3\", build_number=\">=6\", fn=\"foo-1.0-py27_0.tar.bz2\", url=\"https://conda.anaconda.org/conda-forge/linux-64/foo-1.0-py27_0.tar.bz2\", license=\"MIT\"]",
"conda-forge/linux-64:foospace:foo 1.0.* py27_0*[md5=\"8b1a9953c4611296a827abf8c47804d7\", sha256=\"315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3\", build_number=\">=6\", fn=\"foo-1.0-py27_0.tar.bz2\", url=\"https://conda.anaconda.org/conda-forge/linux-64/foo-1.0-py27_0.tar.bz2\", license=\"MIT\", license_family=\"MIT\", features=\"blas\"]",
]
1 change: 1 addition & 0 deletions py-rattler/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading