Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: make the channel in the matchspec struct an actual channel #401

Merged
2 changes: 1 addition & 1 deletion crates/rattler_conda_types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ itertools = "0.11.0"
lazy-regex = "3.0.2"
nom = "7.1.3"
regex = "1.9.6"
serde = { version = "1.0.188", features = ["derive"] }
serde = { version = "1.0.188", features = ["derive", "rc"] }
serde_json = "1.0.107"
serde_yaml = "0.9.25"
serde_with = { version = "3.3.0", features = ["indexmap_2"] }
Expand Down
47 changes: 38 additions & 9 deletions crates/rattler_conda_types/src/match_spec/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use crate::{build_spec::BuildNumberSpec, PackageName, PackageRecord, VersionSpec};
use rattler_digest::{serde::SerializableHash, Md5Hash, Sha256Hash};
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use serde_with::{serde_as, skip_serializing_none, DisplayFromStr};
use std::fmt::{Debug, Display, Formatter};
use std::hash::Hash;
use std::sync::Arc;

use crate::Channel;
use crate::ChannelConfig;

pub mod matcher;
pub mod parse;
Expand Down Expand Up @@ -64,8 +68,9 @@ use matcher::StringMatcher;
/// # Examples:
///
/// ```rust
/// use rattler_conda_types::{MatchSpec, VersionSpec, StringMatcher, PackageName};
/// use rattler_conda_types::{MatchSpec, VersionSpec, StringMatcher, PackageName, Channel};
/// use std::str::FromStr;
/// use std::sync::Arc;
///
/// let spec = MatchSpec::from_str("foo 1.0 py27_0").unwrap();
/// assert_eq!(spec.name, Some(PackageName::new_unchecked("foo")));
Expand All @@ -80,18 +85,18 @@ use matcher::StringMatcher;
/// let spec = MatchSpec::from_str(r#"conda-forge::foo[version="1.0.*"]"#).unwrap();
/// assert_eq!(spec.name, Some(PackageName::new_unchecked("foo")));
/// assert_eq!(spec.version, Some(VersionSpec::from_str("1.0.*").unwrap()));
/// assert_eq!(spec.channel, Some("conda-forge".to_string()));
/// assert_eq!(spec.channel, Some(Channel::from_str("conda-forge", &Default::default()).map(|channel| Arc::new(channel)).unwrap()));
///
/// let spec = MatchSpec::from_str("conda-forge/linux-64::foo>=1.0").unwrap();
/// assert_eq!(spec.name, Some(PackageName::new_unchecked("foo")));
/// assert_eq!(spec.version, Some(VersionSpec::from_str(">=1.0").unwrap()));
/// assert_eq!(spec.channel, Some("conda-forge".to_string()));
/// assert_eq!(spec.channel, Some(Channel::from_str("conda-forge", &Default::default()).map(|channel| Arc::new(channel)).unwrap()));
/// assert_eq!(spec.subdir, Some("linux-64".to_string()));
///
/// let spec = MatchSpec::from_str("*/linux-64::foo>=1.0").unwrap();
/// assert_eq!(spec.name, Some(PackageName::new_unchecked("foo")));
/// assert_eq!(spec.version, Some(VersionSpec::from_str(">=1.0").unwrap()));
/// assert_eq!(spec.channel, Some("*".to_string()));
/// assert_eq!(spec.channel, Some(Channel::from_str("*", &Default::default()).map(|channel| Arc::new(channel)).unwrap()));
/// assert_eq!(spec.subdir, Some("linux-64".to_string()));
///
/// let spec = MatchSpec::from_str(r#"foo[build="py2*"]"#).unwrap();
Expand Down Expand Up @@ -125,7 +130,7 @@ pub struct MatchSpec {
/// Match the specific filename of the package
pub file_name: Option<String>,
/// The channel of the package
pub channel: Option<String>,
pub channel: Option<Arc<Channel>>,
/// The subdir of the channel
pub subdir: Option<String>,
/// The namespace of the package (currently not used)
Expand All @@ -141,8 +146,10 @@ pub struct MatchSpec {
impl Display for MatchSpec {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if let Some(channel) = &self.channel {
// TODO: namespace
write!(f, "{}", channel)?;
if let Some(name) = &channel.name {
// TODO: namespace
write!(f, "{}", name)?;
}
}

if let Some(subdir) = &self.subdir {
Expand Down Expand Up @@ -264,7 +271,8 @@ pub struct NamelessMatchSpec {
/// Match the specific filename of the package
pub file_name: Option<String>,
/// The channel of the package
pub channel: Option<String>,
#[serde(deserialize_with = "deserialize_channel")]
pub channel: Option<Arc<Channel>>,
/// The subdir of the channel
pub subdir: Option<String>,
/// The namespace of the package (currently not used)
Expand Down Expand Up @@ -371,6 +379,27 @@ impl MatchSpec {
}
}

/// Deserialize channel from string
/// TODO: This should be refactored so that the front ends are the one setting the channel config,
/// and rattler only takes care of the url.
fn deserialize_channel<'de, D>(deserializer: D) -> Result<Option<Arc<Channel>>, D::Error>
where
D: Deserializer<'de>,
{
let s: Option<String> = Option::deserialize(deserializer)?;

match s {
Some(str_val) => {
let config = ChannelConfig::default();

Channel::from_str(str_val, &config)
.map(|channel| Some(Arc::new(channel)))
.map_err(serde::de::Error::custom)
}
None => Ok(None),
}
}

#[cfg(test)]
mod tests {
use std::str::FromStr;
Expand Down
39 changes: 32 additions & 7 deletions crates/rattler_conda_types/src/match_spec/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use crate::package::ArchiveType;
use crate::version_spec::version_tree::{recognize_constraint, recognize_version};
use crate::version_spec::{is_start_of_version_constraint, ParseVersionSpecError};
use crate::{
InvalidPackageNameError, NamelessMatchSpec, PackageName, ParseChannelError, VersionSpec,
Channel, InvalidPackageNameError, NamelessMatchSpec, PackageName, ParseChannelError,
VersionSpec,
};
use nom::branch::alt;
use nom::bytes::complete::{tag, take_till1, take_until, take_while, take_while1};
Expand Down Expand Up @@ -397,10 +398,12 @@ fn parse(input: &str) -> Result<MatchSpec, ParseMatchSpecError> {

if let Some(channel_str) = channel_str {
if let Some((channel, subdir)) = channel_str.rsplit_once('/') {
nameless_match_spec.channel = Some(channel.to_string());
nameless_match_spec.channel =
Some(Channel::from_str(channel, &Default::default())?.into());
nameless_match_spec.subdir = Some(subdir.to_string());
} else {
nameless_match_spec.channel = Some(channel_str.to_string());
nameless_match_spec.channel =
Some(Channel::from_str(channel_str, &Default::default())?.into());
}
}

Expand Down Expand Up @@ -468,12 +471,13 @@ mod tests {
use serde::Serialize;
use std::collections::BTreeMap;
use std::str::FromStr;
use std::sync::Arc;

use super::{
split_version_and_build, strip_brackets, BracketVec, MatchSpec, ParseMatchSpecError,
};
use crate::match_spec::parse::parse_bracket_list;
use crate::{BuildNumberSpec, NamelessMatchSpec, VersionSpec};
use crate::{BuildNumberSpec, Channel, NamelessMatchSpec, VersionSpec};
use smallvec::smallvec;

#[test]
Expand Down Expand Up @@ -573,18 +577,39 @@ mod tests {
let spec = MatchSpec::from_str("conda-forge::foo[version=\"1.0.*\"]").unwrap();
assert_eq!(spec.name, Some("foo".parse().unwrap()));
assert_eq!(spec.version, Some(VersionSpec::from_str("1.0.*").unwrap()));
assert_eq!(spec.channel, Some("conda-forge".to_string()));
assert_eq!(
spec.channel,
Some(
Channel::from_str("conda-forge", &Default::default())
.map(|channel| Arc::new(channel))
.unwrap()
)
);

let spec = MatchSpec::from_str("conda-forge::foo[version=1.0.*]").unwrap();
assert_eq!(spec.name, Some("foo".parse().unwrap()));
assert_eq!(spec.version, Some(VersionSpec::from_str("1.0.*").unwrap()));
assert_eq!(spec.channel, Some("conda-forge".to_string()));
assert_eq!(
spec.channel,
Some(
Channel::from_str("conda-forge", &Default::default())
.map(|channel| Arc::new(channel))
.unwrap()
)
);

let spec =
MatchSpec::from_str(r#"conda-forge::foo[version=1.0.*, build_number=">6"]"#).unwrap();
assert_eq!(spec.name, Some("foo".parse().unwrap()));
assert_eq!(spec.version, Some(VersionSpec::from_str("1.0.*").unwrap()));
assert_eq!(spec.channel, Some("conda-forge".to_string()));
assert_eq!(
spec.channel,
Some(
Channel::from_str("conda-forge", &Default::default())
.map(|channel| Arc::new(channel))
.unwrap()
)
);
assert_eq!(
spec.build_number,
Some(BuildNumberSpec::from_str(">6").unwrap())
Expand Down
18 changes: 11 additions & 7 deletions crates/rattler_solve/src/resolvo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,15 +259,19 @@ impl<'a> CondaDependencyProvider<'a> {
}) {
// Check if the spec has a channel, and compare it to the repodata channel
if let Some(spec_channel) = &spec.channel {
if !&record.channel.contains(spec_channel) {
if record.channel != spec_channel.base_url.to_string() {
tracing::debug!("Ignoring {} from {} because it was not requested from that channel.", &record.package_record.name.as_normalized(), &record.channel);
// Add record to the excluded with reason of being in the non requested channel.
candidates.excluded.push((
solvable_id,
pool.intern_string(format!(
"candidate not in requested channel: '{spec_channel}'"
)),
));
let message = format!(
"candidate not in requested channel: '{}'",
spec_channel
.name
.clone()
.unwrap_or(spec_channel.base_url.to_string())
);
candidates
.excluded
.push((solvable_id, pool.intern_string(message)));
continue;
}
}
Expand Down
Loading