Skip to content

Commit

Permalink
Fix VersionSpec free ranges (#3088)
Browse files Browse the repository at this point in the history
  • Loading branch information
AntoinePrv authored Dec 28, 2023
1 parent 4ee7531 commit 3f6484c
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 6 deletions.
3 changes: 3 additions & 0 deletions libmamba/include/mamba/specs/version_spec.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#ifndef MAMBA_SPECS_VERSION_SPEC_HPP
#define MAMBA_SPECS_VERSION_SPEC_HPP

#include <array>
#include <functional>
#include <string_view>
#include <variant>
Expand Down Expand Up @@ -128,6 +129,8 @@ namespace mamba::specs
static constexpr char left_parenthesis_token = '(';
static constexpr char right_parenthesis_token = ')';

static constexpr std::string_view prefered_free_str = "=*";
static constexpr std::array<std::string_view, 4> all_free_strs = { "", "*", "=*", "==*" };
static constexpr std::string_view starts_with_str = "=";
static constexpr std::string_view equal_str = "==";
static constexpr std::string_view not_equal_str = "!=";
Expand Down
27 changes: 21 additions & 6 deletions libmamba/src/specs/version_spec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,16 +290,17 @@ namespace mamba::specs

namespace
{
auto is_char(std::string_view str, char c) -> bool
template <typename Val, typename Range>
constexpr auto equal_any(const Val& val, const Range& range) -> bool
{
return (str.size() == 1) && (str.front() == c);
return std::find(range.cbegin(), range.cend(), val) != range.cend();
}

auto parse_op_and_version(std::string_view str) -> VersionPredicate
{
str = util::strip(str);
// WARNING order is important since some operator are prefix of others.
if (str.empty() || is_char(str, VersionSpec::glob_suffix_token))
if (str.empty() || equal_any(str, VersionSpec::all_free_strs))
{
return VersionPredicate::make_free();
}
Expand Down Expand Up @@ -331,7 +332,7 @@ namespace mamba::specs
{
auto ver = Version::parse(str.substr(VersionSpec::compatible_str.size()));
// in ``~=1.1`` level is assumed to be 1, in ``~=1.1.1`` level 2, etc.
static constexpr std::size_t one = std::size_t(1); // MSVC
static constexpr auto one = std::size_t(1); // MSVC
const std::size_t level = std::max(ver.version().size(), one) - one;
return VersionPredicate::make_compatible_with(std::move(ver), level);
}
Expand Down Expand Up @@ -381,7 +382,7 @@ namespace mamba::specs
if (util::ends_with(str, VersionSpec::glob_suffix_token))
{
// either ".*" or "*"
static constexpr std::size_t one = std::size_t(1); // MSVC
static constexpr auto one = std::size_t(1); // MSVC
const std::size_t len = str.size() - std::max(glob_len, one);
return VersionPredicate::make_starts_with(Version::parse(str.substr(0, len)));
}
Expand Down Expand Up @@ -409,6 +410,15 @@ namespace mamba::specs

auto parser = util::InfixParser<VersionPredicate, util::BoolOperator>();
str = util::lstrip(str);

// Explicit short-circuiting for "free" spec
// This case would be handled anyway but we can avoid allocating a tree in this
// likely case.
if (str.empty() || equal_any(str, VersionSpec::all_free_strs))
{
return {};
}

while (!str.empty())
{
if (str.front() == VersionSpec::and_token)
Expand Down Expand Up @@ -468,11 +478,16 @@ fmt::formatter<mamba::specs::VersionSpec>::format(
format_context& ctx
) -> decltype(ctx.out())
{
using VersionSpec = typename mamba::specs::VersionSpec;

auto out = ctx.out();
if (spec.m_tree.empty())
{
return fmt::format_to(out, "{}", VersionSpec::prefered_free_str);
}
spec.m_tree.infix_for_each(
[&](const auto& token)
{
using VersionSpec = typename mamba::specs::VersionSpec;
using tree_type = typename VersionSpec::tree_type;
using Token = std::decay_t<decltype(token)>;
if constexpr (std::is_same_v<Token, tree_type::LeftParenthesis>)
Expand Down
4 changes: 4 additions & 0 deletions libmamba/tests/src/specs/test_version_spec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ TEST_SUITE("specs::version_spec")
{
auto spec = VersionSpec();
CHECK(spec.contains(Version()));
CHECK_EQ(spec.str(), "=*");
}

SUBCASE("<2.0|(>2.3,<=2.8.0)")
Expand Down Expand Up @@ -168,6 +169,9 @@ TEST_SUITE("specs::version_spec")
CHECK(""_vs.contains("1.6"_v));
CHECK(""_vs.contains("0.6+0.7"_v));

CHECK("*"_vs.contains("1.4"_v));
CHECK("=*"_vs.contains("1.4"_v));

CHECK("1.7"_vs.contains("1.7"_v));
CHECK("1.7"_vs.contains("1.7.0.0"_v));
CHECK_FALSE("1.7"_vs.contains("1.6"_v));
Expand Down
2 changes: 2 additions & 0 deletions libmambapy/src/libmambapy/bindings/specs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,8 @@ namespace mambapy
.def_readonly_static("or_token", &VersionSpec::or_token)
.def_readonly_static("left_parenthesis_token", &VersionSpec::left_parenthesis_token)
.def_readonly_static("right_parenthesis_token", &VersionSpec::right_parenthesis_token)
.def_readonly_static("prefered_free_str", &VersionSpec::prefered_free_str)
.def_readonly_static("all_free_strs", &VersionSpec::all_free_strs)
.def_readonly_static("starts_with_str", &VersionSpec::starts_with_str)
.def_readonly_static("equal_str", &VersionSpec::equal_str)
.def_readonly_static("not_equal_str", &VersionSpec::not_equal_str)
Expand Down
2 changes: 2 additions & 0 deletions libmambapy/tests/test_specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,8 @@ def test_VersionSpec():
assert isinstance(VersionSpec.or_token, str)
assert isinstance(VersionSpec.left_parenthesis_token, str)
assert isinstance(VersionSpec.right_parenthesis_token, str)
assert isinstance(VersionSpec.prefered_free_str, str)
assert isinstance(VersionSpec.all_free_strs, list)
assert isinstance(VersionSpec.starts_with_str, str)
assert isinstance(VersionSpec.equal_str, str)
assert isinstance(VersionSpec.not_equal_str, str)
Expand Down

0 comments on commit 3f6484c

Please sign in to comment.