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

Add VersionSpec::str #3081

Merged
merged 3 commits into from
Dec 22, 2023
Merged
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
84 changes: 24 additions & 60 deletions libmamba/include/mamba/specs/version.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#ifndef MAMBA_SPECS_VERSION_HPP
#define MAMBA_SPECS_VERSION_HPP

#include <optional>
#include <string>
#include <string_view>
#include <vector>
Expand Down Expand Up @@ -110,8 +111,24 @@ namespace mamba::specs
[[nodiscard]] auto version() const noexcept -> const CommonVersion&;
[[nodiscard]] auto local() const noexcept -> const CommonVersion&;

/**
* A string representation of the version.
*
* May not always be the same as the parsed string (due to reconstruction) but reparsing
* this string will give the same version.
* ``v == Version::parse(v.str())``.
*/
[[nodiscard]] auto str() const -> std::string;

/**
* A string truncated of extended representation of the version.
*
* Represent the string with the desired number of parts.
* If the actual number of parts is larger, then the string is truncated.
* If the actual number of parts is smalle, then the string is expanded with zeros.
*/
[[nodiscard]] auto str(std::size_t level) const -> std::string;

[[nodiscard]] auto operator==(const Version& other) const -> bool;
[[nodiscard]] auto operator!=(const Version& other) const -> bool;
[[nodiscard]] auto operator<(const Version& other) const -> bool;
Expand Down Expand Up @@ -156,73 +173,20 @@ namespace mamba::specs
template <>
struct fmt::formatter<mamba::specs::VersionPartAtom>
{
constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin())
{
// make sure that range is empty
if (ctx.begin() != ctx.end() && *ctx.begin() != '}')
{
throw fmt::format_error("Invalid format");
}
return ctx.begin();
}
auto parse(format_parse_context& ctx) -> decltype(ctx.begin());

template <class FormatContext>
auto format(const ::mamba::specs::VersionPartAtom atom, FormatContext& ctx)
{
return fmt::format_to(ctx.out(), "{}{}", atom.numeral(), atom.literal());
}
auto format(const ::mamba::specs::VersionPartAtom atom, format_context& ctx)
-> decltype(ctx.out());
};

template <>
struct fmt::formatter<mamba::specs::Version>
{
constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin())
{
// make sure that range is empty
if (ctx.begin() != ctx.end() && *ctx.begin() != '}')
{
throw fmt::format_error("Invalid format");
}
return ctx.begin();
}
std::optional<std::size_t> m_level;

template <class FormatContext>
auto format(const ::mamba::specs::Version v, FormatContext& ctx)
{
auto out = ctx.out();
if (v.epoch() != 0)
{
out = fmt::format_to(ctx.out(), "{}!", v.epoch());
}

auto format_version_to = [](auto l_out, const auto& version)
{
bool first = true;
for (const auto& part : version)
{
if (first)
{
first = false;
}
else
{
l_out = fmt::format_to(l_out, ".");
}
for (const auto& atom : part)
{
l_out = fmt::format_to(l_out, "{}", atom);
}
}
return l_out;
};
out = format_version_to(out, v.version());
if (!v.local().empty())
{
out = fmt::format_to(out, "+");
out = format_version_to(out, v.local());
}
return out;
}
auto parse(format_parse_context& ctx) -> decltype(ctx.begin());

auto format(const ::mamba::specs::Version v, format_context& ctx) -> decltype(ctx.out());
};

#endif
50 changes: 50 additions & 0 deletions libmamba/include/mamba/specs/version_spec.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@
#include <string_view>
#include <variant>

#include <fmt/format.h>

#include "mamba/specs/version.hpp"
#include "mamba/util/flat_bool_expr_tree.hpp"

namespace mamba::specs
{
/**
* A stateful unary boolean function on the Version space.
*/
class VersionPredicate
{
public:
Expand All @@ -35,8 +40,13 @@ namespace mamba::specs
/** Construct an free interval. */
VersionPredicate() = default;

/**
* True if the predicate contains the given version.
*/
[[nodiscard]] auto contains(const Version& point) const -> bool;

[[nodiscard]] auto str() const -> std::string;

private:

struct free_interval
Expand Down Expand Up @@ -90,11 +100,23 @@ namespace mamba::specs
friend auto operator==(not_starts_with, not_starts_with) -> bool;
friend auto operator==(compatible_with, compatible_with) -> bool;
friend auto operator==(const VersionPredicate& lhs, const VersionPredicate& rhs) -> bool;
friend class ::fmt::formatter<VersionPredicate>;
};

auto operator==(const VersionPredicate& lhs, const VersionPredicate& rhs) -> bool;
auto operator!=(const VersionPredicate& lhs, const VersionPredicate& rhs) -> bool;

/**
* Represent a set of versions.
*
* Internally, a VersionSpec is a binary expression tree of union (or) or intersections (and)
* of the sets represented by VersionPredicate.
*
* The VersionSpec can itself be considered a complex predicate on the space of Version.
*
* Due to the complex nature of the expression system (comutativity, associativity, etc.), there
* is no easy way to say if two VersionSpecs are equal.
*/
class VersionSpec
{
public:
Expand Down Expand Up @@ -123,12 +145,24 @@ namespace mamba::specs
VersionSpec() = default;
explicit VersionSpec(tree_type&& tree) noexcept;

/**
* A string representation of the version spec.
*
* May not always be the same as the parsed string (due to reconstruction) but reparsing
* this string will give the same version spec.
*/
[[nodiscard]] auto str() const -> std::string;

/**
* True if the set described by the VersionSpec contains the given version.
*/
[[nodiscard]] auto contains(const Version& point) const -> bool;

private:

tree_type m_tree;

friend class ::fmt::formatter<VersionSpec>;
};

namespace version_spec_literals
Expand All @@ -137,4 +171,20 @@ namespace mamba::specs
}
}

template <>
struct fmt::formatter<mamba::specs::VersionPredicate>
{
auto parse(format_parse_context& ctx) -> decltype(ctx.begin());

auto format(const ::mamba::specs::VersionPredicate& pred, format_context& ctx)
-> decltype(ctx.out());
};

template <>
struct fmt::formatter<mamba::specs::VersionSpec>
{
auto parse(format_parse_context& ctx) -> decltype(ctx.begin());

auto format(const ::mamba::specs::VersionSpec& spec, format_context& ctx) -> decltype(ctx.out());
};
#endif
26 changes: 26 additions & 0 deletions libmamba/include/mamba/util/flat_binary_tree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ namespace mamba::util
[[nodiscard]] auto right(idx_type idx) const -> idx_type;
[[nodiscard]] auto root() const -> idx_type;

template <typename Visitor>
void dfs_raw(Visitor&& visitor, idx_type start) const;

private:

node_list m_nodes;
Expand Down Expand Up @@ -245,5 +248,28 @@ namespace mamba::util
{
return add_branch_impl(std::move(branch), left_child, right_child);
}

template <typename B, typename L>
template <typename Visitor>
void flat_binary_tree<B, L>::dfs_raw(Visitor&& visitor, idx_type start_idx) const
{
if (is_leaf(start_idx))
{
visitor.on_leaf(*this, start_idx);
}
else
{
const auto left_idx = left(start_idx);
const auto right_idx = right(start_idx);

visitor.on_branch_left_before(*this, start_idx, left_idx);
dfs_raw(visitor, left_idx);

visitor.on_branch_infix(*this, start_idx, left_idx, right_idx);

dfs_raw(visitor, right_idx);
visitor.on_branch_right_after(*this, start_idx, right_idx);
}
}
}
#endif
98 changes: 96 additions & 2 deletions libmamba/include/mamba/util/flat_bool_expr_tree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@

#include <cassert>
#include <functional>
#include <iterator>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>
Expand Down Expand Up @@ -150,6 +148,14 @@ namespace mamba::util
using tree_type = flat_binary_tree<operator_type, variable_type>;
using size_type = typename tree_type::size_type;

struct LeftParenthesis
{
};

struct RightParenthesis
{
};

flat_bool_expr_tree() = default;
flat_bool_expr_tree(const flat_bool_expr_tree&) = default;
flat_bool_expr_tree(flat_bool_expr_tree&&) = default;
Expand All @@ -169,6 +175,9 @@ namespace mamba::util
[[nodiscard]] auto evaluate(UnaryFunc&& var_evaluator = {}, bool empty_val = true) const
-> bool;

template <typename UnaryFunc>
void infix_for_each(UnaryFunc&& func) const;

private:

using idx_type = typename tree_type::idx_type;
Expand All @@ -179,6 +188,42 @@ namespace mamba::util
tree_type m_tree = {};
};

template <typename V>
constexpr auto operator==(
typename flat_bool_expr_tree<V>::LeftParenthesis,
typename flat_bool_expr_tree<V>::LeftParenthesis
) -> bool
{
return true;
}

template <typename V>
constexpr auto operator!=(
typename flat_bool_expr_tree<V>::LeftParenthesis,
typename flat_bool_expr_tree<V>::LeftParenthesis
) -> bool
{
return false;
}

template <typename V>
constexpr auto operator==(
typename flat_bool_expr_tree<V>::RightParenthesis,
typename flat_bool_expr_tree<V>::RightParenthesis
) -> bool
{
return true;
}

template <typename V>
constexpr auto operator!=(
typename flat_bool_expr_tree<V>::RightParenthesis,
typename flat_bool_expr_tree<V>::RightParenthesis
) -> bool
{
return false;
}

/*************************************
* Implementation of PostfixParser *
*************************************/
Expand Down Expand Up @@ -551,5 +596,54 @@ namespace mamba::util
|| evaluate_impl(var_eval, m_tree.right(idx));
}
}

template <typename V>
template <typename UnaryFunc>
void flat_bool_expr_tree<V>::infix_for_each(UnaryFunc&& func) const
{
struct TreeVisitor
{
using idx_type = typename tree_type::idx_type;

void on_leaf(const tree_type& tree, idx_type idx)
{
m_func(tree.leaf(idx));
}

void on_branch_left_before(const tree_type& tree, idx_type, idx_type left_idx)
{
if (!tree.is_leaf(left_idx))
{
m_func(LeftParenthesis{});
}
}

void
on_branch_infix(const tree_type& tree, idx_type branch_idx, idx_type left_idx, idx_type right_idx)
{
if (!tree.is_leaf(left_idx))
{
m_func(RightParenthesis{});
}
m_func(tree.branch(branch_idx));
if (!tree.is_leaf(right_idx))
{
m_func(LeftParenthesis{});
}
}

void on_branch_right_after(const tree_type& tree, idx_type, idx_type right_idx)
{
if (!tree.is_leaf(right_idx))
{
m_func(RightParenthesis{});
}
}

UnaryFunc m_func;
} tree_visitor{ std::forward<UnaryFunc>(func) };

m_tree.dfs_raw(tree_visitor, m_tree.root());
}
}
#endif
Loading
Loading