From ea1bdc2f57463173cf3588e3a581744004142bf1 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra Date: Tue, 13 Aug 2024 15:57:46 +0200 Subject: [PATCH] feat: parse `channel` key and consolidate `NamelessMatchSpec` (#810) Fixes https://github.com/conda-incubator/rattler/issues/793 Continuation of https://github.com/conda-incubator/rattler/pull/792 that adds support for `1.2.3[channel=...]` and consolidates logic of parsing matchspec and namelessmatchspec some more. --------- Co-authored-by: Ruben Arts --- .../src/match_spec/parse.rs | 42 +++++++++++++++++++ ...arse__tests__test_from_string_Lenient.snap | 25 +++++++++++ ...parse__tests__test_from_string_Strict.snap | 25 +++++++++++ 3 files changed, 92 insertions(+) diff --git a/crates/rattler_conda_types/src/match_spec/parse.rs b/crates/rattler_conda_types/src/match_spec/parse.rs index 2ddfa6e31..7ab06807e 100644 --- a/crates/rattler_conda_types/src/match_spec/parse.rs +++ b/crates/rattler_conda_types/src/match_spec/parse.rs @@ -263,6 +263,11 @@ fn parse_bracket_vec_into_components( match_spec.url = Some(url); } "subdir" => match_spec.subdir = Some(value.to_string()), + "channel" => { + let (channel, subdir) = parse_channel_and_subdir(value)?; + match_spec.channel = match_spec.channel.or(channel.map(Arc::new)); + match_spec.subdir = match_spec.subdir.or(subdir); + } // TODO: Still need to add `track_features`, `features`, `license` and `license_family` // to the match spec. _ => Err(ParseMatchSpecError::InvalidBracketKey(key.to_owned()))?, @@ -454,6 +459,26 @@ impl NamelessMatchSpec { let mut match_spec = parse_bracket_vec_into_components(brackets, NamelessMatchSpec::default(), strictness)?; + // 5. Strip of ':' to find channel and namespace + // This assumes the [*] portions is stripped off, and then strip reverse to + // ignore the first colon As that might be in the channel url. + let mut input_split = input.rsplitn(3, ':').fuse(); + let input = input_split.next().unwrap_or(""); + let namespace = input_split.next(); + let channel_str = input_split.next(); + + match_spec.namespace = namespace + .map(str::trim) + .filter(|namespace| !namespace.is_empty()) + .map(ToOwned::to_owned) + .or(match_spec.namespace); + + if let Some(channel_str) = channel_str { + let (channel, subdir) = parse_channel_and_subdir(channel_str)?; + match_spec.channel = match_spec.channel.or(channel.map(Arc::new)); + match_spec.subdir = match_spec.subdir.or(subdir); + } + // Get the version and optional build string if !input.is_empty() { let (version, build) = parse_version_and_build(input, strictness)?; @@ -744,6 +769,9 @@ mod tests { NamelessMatchSpec::from_str("3.8.* *_cpython", Strict).unwrap(), NamelessMatchSpec::from_str("1.0 py27_0[fn=\"bla\"]", Strict).unwrap(), NamelessMatchSpec::from_str("=1.0 py27_0", Strict).unwrap(), + NamelessMatchSpec::from_str("*cpu*", Strict).unwrap(), + NamelessMatchSpec::from_str("conda-forge::foobar", Strict).unwrap(), + NamelessMatchSpec::from_str("foobar[channel=conda-forge]", Strict).unwrap(), ], @r###" --- @@ -754,6 +782,16 @@ mod tests { file_name: bla - version: 1.0.* build: py27_0 + - version: "*" + build: cpu* + - version: "==foobar" + channel: + base_url: "https://conda.anaconda.org/conda-forge/" + name: conda-forge + - version: "==foobar" + channel: + base_url: "https://conda.anaconda.org/conda-forge/" + name: conda-forge "###); } @@ -966,10 +1004,14 @@ mod tests { // subdir in brackets take precedence "conda-forge/linux-32::python[version=3.9, subdir=linux-64]", "conda-forge/linux-32::python ==3.9[subdir=linux-64, build_number=\"0\"]", + "python ==3.9[channel=conda-forge]", + "python ==3.9[channel=conda-forge/linux-64]", "rust ~=1.2.3", "~/channel/dir::package", "~\\windows_channel::package", "./relative/channel::package", + "python[channel=https://conda.anaconda.org/python/conda,version=3.9]", + "channel/win-64::foobar[channel=conda-forge, subdir=linux-64]", ]; let evaluated: IndexMap<_, _> = specs diff --git a/crates/rattler_conda_types/src/match_spec/snapshots/rattler_conda_types__match_spec__parse__tests__test_from_string_Lenient.snap b/crates/rattler_conda_types/src/match_spec/snapshots/rattler_conda_types__match_spec__parse__tests__test_from_string_Lenient.snap index 226af1a8c..9ae8f69c5 100644 --- a/crates/rattler_conda_types/src/match_spec/snapshots/rattler_conda_types__match_spec__parse__tests__test_from_string_Lenient.snap +++ b/crates/rattler_conda_types/src/match_spec/snapshots/rattler_conda_types__match_spec__parse__tests__test_from_string_Lenient.snap @@ -98,6 +98,19 @@ python=*: base_url: "https://conda.anaconda.org/conda-forge/" name: conda-forge subdir: linux-64 +"python ==3.9[channel=conda-forge]": + name: python + version: "==3.9" + channel: + base_url: "https://conda.anaconda.org/conda-forge/" + name: conda-forge +"python ==3.9[channel=conda-forge/linux-64]": + name: python + version: "==3.9" + channel: + base_url: "https://conda.anaconda.org/conda-forge/" + name: conda-forge + subdir: linux-64 rust ~=1.2.3: name: rust version: ~=1.2.3 @@ -113,3 +126,15 @@ rust ~=1.2.3: channel: base_url: "file:///relative/channel/" name: "./relative/channel" +"python[channel=https://conda.anaconda.org/python/conda,version=3.9]": + name: python + version: "==3.9" + channel: + base_url: "https://conda.anaconda.org/python/conda/" + name: python/conda +"channel/win-64::foobar[channel=conda-forge, subdir=linux-64]": + name: foobar + channel: + base_url: "https://conda.anaconda.org/conda-forge/" + name: conda-forge + subdir: linux-64 diff --git a/crates/rattler_conda_types/src/match_spec/snapshots/rattler_conda_types__match_spec__parse__tests__test_from_string_Strict.snap b/crates/rattler_conda_types/src/match_spec/snapshots/rattler_conda_types__match_spec__parse__tests__test_from_string_Strict.snap index 221d2dc94..4249bf4e1 100644 --- a/crates/rattler_conda_types/src/match_spec/snapshots/rattler_conda_types__match_spec__parse__tests__test_from_string_Strict.snap +++ b/crates/rattler_conda_types/src/match_spec/snapshots/rattler_conda_types__match_spec__parse__tests__test_from_string_Strict.snap @@ -90,6 +90,19 @@ python=*: base_url: "https://conda.anaconda.org/conda-forge/" name: conda-forge subdir: linux-64 +"python ==3.9[channel=conda-forge]": + name: python + version: "==3.9" + channel: + base_url: "https://conda.anaconda.org/conda-forge/" + name: conda-forge +"python ==3.9[channel=conda-forge/linux-64]": + name: python + version: "==3.9" + channel: + base_url: "https://conda.anaconda.org/conda-forge/" + name: conda-forge + subdir: linux-64 rust ~=1.2.3: name: rust version: ~=1.2.3 @@ -105,3 +118,15 @@ rust ~=1.2.3: channel: base_url: "file:///relative/channel/" name: "./relative/channel" +"python[channel=https://conda.anaconda.org/python/conda,version=3.9]": + name: python + version: "==3.9" + channel: + base_url: "https://conda.anaconda.org/python/conda/" + name: python/conda +"channel/win-64::foobar[channel=conda-forge, subdir=linux-64]": + name: foobar + channel: + base_url: "https://conda.anaconda.org/conda-forge/" + name: conda-forge + subdir: linux-64