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
23 changes: 21 additions & 2 deletions crates/rattler_conda_types/src/channel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::borrow::Cow;
use std::path::{Component, Path, PathBuf};
use std::str::FromStr;

use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use smallvec::SmallVec;
use thiserror::Error;
use url::Url;
Expand Down Expand Up @@ -38,7 +38,7 @@ impl Default for ChannelConfig {
}

/// `Channel`s are the primary source of package information.
#[derive(Debug, Clone, Serialize, Eq, PartialEq, Hash)]
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
ruben-arts marked this conversation as resolved.
Show resolved Hide resolved
pub struct Channel {
/// The platforms supported by this channel, or None if no explicit platforms have been
/// specified.
Expand Down Expand Up @@ -194,6 +194,25 @@ impl Channel {
pub fn canonical_name(&self) -> String {
self.base_url.to_string()
}

/// Deserialize channel from string
pub fn deserialize_optional<'de, D>(deserializer: D) -> Result<Option<Self>, 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(Some)
.map_err(serde::de::Error::custom)
}
None => Ok(None),
}
}
}

#[derive(Debug, Error, Clone, Eq, PartialEq)]
Expand Down
21 changes: 13 additions & 8 deletions crates/rattler_conda_types/src/match_spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use serde_with::{serde_as, skip_serializing_none, DisplayFromStr};
use std::fmt::{Debug, Display, Formatter};
use std::hash::Hash;

use crate::Channel;
pub mod matcher;
pub mod parse;

Expand Down Expand Up @@ -64,7 +65,7 @@ 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;
///
/// let spec = MatchSpec::from_str("foo 1.0 py27_0").unwrap();
Expand All @@ -80,18 +81,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()).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()).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()).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 +126,8 @@ pub struct MatchSpec {
/// Match the specific filename of the package
pub file_name: Option<String>,
/// The channel of the package
pub channel: Option<String>,
#[serde(deserialize_with = "Channel::deserialize_optional")]
pub channel: Option<Channel>,
/// The subdir of the channel
pub subdir: Option<String>,
/// The namespace of the package (currently not used)
Expand All @@ -141,8 +143,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 +268,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 = "Channel::deserialize_optional")]
pub channel: Option<Channel>,
/// The subdir of the channel
pub subdir: Option<String>,
/// The namespace of the package (currently not used)
Expand Down
25 changes: 18 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,11 @@ 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())?);
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())?);
}
}

Expand Down Expand Up @@ -473,7 +475,7 @@ mod tests {
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 +575,27 @@ 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()).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()).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()).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