mirror of https://github.com/mamba-org/mamba.git
Use expected for specs parsing (#3201)
* Expected in Version::parse * Use boolean check in expr trees * Expected in VersionSpec::parse * Add assert caster safeguard * Expected in BuildNumberSpec::parse * Minor improvement in GlobSpec * Expected in UnresolvedChannel::parse * Expected in URL::parse
This commit is contained in:
parent
86b2b8c121
commit
88748be956
|
@ -299,6 +299,7 @@ set(
|
|||
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/build_number_spec.hpp
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/channel.hpp
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/conda_url.hpp
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/error.hpp
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/glob_spec.hpp
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/match_spec.hpp
|
||||
${LIBMAMBA_INCLUDE_DIR}/mamba/specs/package_info.hpp
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "mamba/specs/error.hpp"
|
||||
|
||||
namespace mamba::specs
|
||||
{
|
||||
/**
|
||||
|
@ -97,7 +99,7 @@ namespace mamba::specs
|
|||
static constexpr std::string_view less_str = "<";
|
||||
static constexpr std::string_view less_equal_str = "<=";
|
||||
|
||||
[[nodiscard]] static auto parse(std::string_view str) -> BuildNumberSpec;
|
||||
[[nodiscard]] static auto parse(std::string_view str) -> expected_parse_t<BuildNumberSpec>;
|
||||
|
||||
/** Construct BuildNumberSpec that match all versions. */
|
||||
BuildNumberSpec();
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) 2024, QuantStack and Mamba Contributors
|
||||
//
|
||||
// Distributed under the terms of the BSD 3-Clause License.
|
||||
//
|
||||
// The full license is in the file LICENSE, distributed with this software.
|
||||
|
||||
#ifndef MAMBA_SPECS_ERROR_HPP
|
||||
#define MAMBA_SPECS_ERROR_HPP
|
||||
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include <tl/expected.hpp>
|
||||
|
||||
namespace mamba::specs
|
||||
{
|
||||
|
||||
struct ParseError final : std::invalid_argument
|
||||
{
|
||||
using std::invalid_argument::invalid_argument;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using expected_parse_t = tl::expected<T, ParseError>;
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] auto make_unexpected_parse(T&& err) -> tl::unexpected<ParseError>;
|
||||
|
||||
/********************
|
||||
* Implementation *
|
||||
********************/
|
||||
|
||||
template <typename T>
|
||||
auto make_unexpected_parse(T&& err) -> tl::unexpected<ParseError>
|
||||
{
|
||||
return tl::make_unexpected(ParseError(std::forward<T>(err)));
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -24,6 +24,7 @@ namespace mamba::specs
|
|||
public:
|
||||
|
||||
inline static constexpr std::string_view free_pattern = "*";
|
||||
inline static constexpr char glob_pattern = '*';
|
||||
|
||||
GlobSpec() = default;
|
||||
explicit GlobSpec(std::string pattern);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <fmt/core.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "mamba/specs/error.hpp"
|
||||
#include "mamba/util/flat_set.hpp"
|
||||
|
||||
namespace mamba::specs
|
||||
|
@ -88,7 +89,7 @@ namespace mamba::specs
|
|||
|
||||
using dynamic_platform_set = util::flat_set<std::string>;
|
||||
|
||||
[[nodiscard]] static auto parse(std::string_view str) -> UnresolvedChannel;
|
||||
[[nodiscard]] static auto parse(std::string_view str) -> expected_parse_t<UnresolvedChannel>;
|
||||
|
||||
UnresolvedChannel() = default;
|
||||
UnresolvedChannel(std::string location, dynamic_platform_set filters, Type type);
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "mamba/specs/error.hpp"
|
||||
|
||||
namespace mamba::specs
|
||||
{
|
||||
|
||||
|
@ -101,7 +103,7 @@ namespace mamba::specs
|
|||
static constexpr char part_delim_alt = '-';
|
||||
static constexpr char part_delim_special = '_';
|
||||
|
||||
static auto parse(std::string_view str) -> Version;
|
||||
static auto parse(std::string_view str) -> expected_parse_t<Version>;
|
||||
|
||||
/** Construct version ``0.0``. */
|
||||
Version() noexcept = default;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "mamba/specs/error.hpp"
|
||||
#include "mamba/specs/version.hpp"
|
||||
#include "mamba/util/flat_bool_expr_tree.hpp"
|
||||
|
||||
|
@ -149,7 +150,7 @@ namespace mamba::specs
|
|||
static constexpr std::string_view glob_suffix_str = ".*";
|
||||
static constexpr char glob_suffix_token = '*';
|
||||
|
||||
[[nodiscard]] static auto parse(std::string_view str) -> VersionSpec;
|
||||
[[nodiscard]] static auto parse(std::string_view str) -> expected_parse_t<VersionSpec>;
|
||||
|
||||
/** Construct VersionSpec that match all versions. */
|
||||
VersionSpec() = default;
|
||||
|
|
|
@ -9,17 +9,12 @@
|
|||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include "mamba/util/flat_binary_tree.hpp"
|
||||
#include "mamba/util/functional.hpp"
|
||||
#include "mamba/util/type_traits.hpp"
|
||||
|
||||
namespace mamba::util
|
||||
{
|
||||
|
@ -40,11 +35,11 @@ namespace mamba::util
|
|||
using variable_type = Variable;
|
||||
using tree_type = flat_binary_tree<operator_type, variable_type>;
|
||||
|
||||
void push_variable(const variable_type& var);
|
||||
void push_variable(variable_type&& var);
|
||||
void push_operator(const operator_type& op);
|
||||
void push_operator(operator_type&& op);
|
||||
void finalize();
|
||||
[[nodiscard]] auto push_variable(const variable_type& var) -> bool;
|
||||
[[nodiscard]] auto push_variable(variable_type&& var) -> bool;
|
||||
[[nodiscard]] auto push_operator(const operator_type& op) -> bool;
|
||||
[[nodiscard]] auto push_operator(operator_type&& op) -> bool;
|
||||
[[nodiscard]] auto finalize() -> bool;
|
||||
|
||||
[[nodiscard]] auto tree() const& -> const tree_type&;
|
||||
[[nodiscard]] auto tree() && -> tree_type&&;
|
||||
|
@ -63,9 +58,9 @@ namespace mamba::util
|
|||
auto orphans_pop() -> idx_type;
|
||||
|
||||
template <typename V>
|
||||
void push_variable_impl(V&& var);
|
||||
[[nodiscard]] auto push_variable_impl(V&& var) -> bool;
|
||||
template <typename O>
|
||||
void push_operator_impl(O&& op);
|
||||
[[nodiscard]] auto push_operator_impl(O&& op) -> bool;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -89,13 +84,13 @@ namespace mamba::util
|
|||
InfixParser(const operator_precedence_type& cmp);
|
||||
InfixParser(operator_precedence_type&& cmp = {});
|
||||
|
||||
void push_variable(const variable_type& var);
|
||||
void push_variable(variable_type&& var);
|
||||
void push_operator(const operator_type& op);
|
||||
void push_operator(operator_type&& op);
|
||||
void push_left_parenthesis();
|
||||
void push_right_parenthesis();
|
||||
void finalize();
|
||||
[[nodiscard]] auto push_variable(const variable_type& var) -> bool;
|
||||
[[nodiscard]] auto push_variable(variable_type&& var) -> bool;
|
||||
[[nodiscard]] auto push_operator(const operator_type& op) -> bool;
|
||||
[[nodiscard]] auto push_operator(operator_type&& op) -> bool;
|
||||
[[nodiscard]] auto push_left_parenthesis() -> bool;
|
||||
[[nodiscard]] auto push_right_parenthesis() -> bool;
|
||||
[[nodiscard]] auto finalize() -> bool;
|
||||
|
||||
[[nodiscard]] auto tree() const& -> const tree_type&;
|
||||
[[nodiscard]] auto tree() && -> tree_type&&;
|
||||
|
@ -120,15 +115,15 @@ namespace mamba::util
|
|||
template <typename T>
|
||||
void stack_push(T&& elem);
|
||||
auto stack_pop() -> operator_or_parenthesis_type;
|
||||
auto stack_empty() const -> bool;
|
||||
[[nodiscard]] auto stack_empty() const -> bool;
|
||||
auto stack_top() const -> const operator_or_parenthesis_type&;
|
||||
auto stack_top_is_parenthesis() const -> bool;
|
||||
[[nodiscard]] auto stack_top_is_parenthesis() const -> bool;
|
||||
auto stack_top_is_op_with_greater_precedence_than(const operator_type&) const -> bool;
|
||||
|
||||
template <typename V>
|
||||
void push_variable_impl(V&& var);
|
||||
[[nodiscard]] auto push_variable_impl(V&& var) -> bool;
|
||||
template <typename O>
|
||||
void push_operator_impl(O&& op);
|
||||
[[nodiscard]] auto push_operator_impl(O&& op) -> bool;
|
||||
};
|
||||
|
||||
enum struct BoolOperator
|
||||
|
@ -245,56 +240,58 @@ namespace mamba::util
|
|||
|
||||
template <typename V, typename O>
|
||||
template <typename Var>
|
||||
void PostfixParser<V, O>::push_variable_impl(Var&& var)
|
||||
auto PostfixParser<V, O>::push_variable_impl(Var&& var) -> bool
|
||||
{
|
||||
orphans_push(m_tree.add_leaf(std::forward<Var>(var)));
|
||||
return true; // Always valid
|
||||
}
|
||||
|
||||
template <typename V, typename O>
|
||||
void PostfixParser<V, O>::push_variable(const variable_type& var)
|
||||
auto PostfixParser<V, O>::push_variable(const variable_type& var) -> bool
|
||||
{
|
||||
return push_variable_impl(var);
|
||||
}
|
||||
|
||||
template <typename V, typename O>
|
||||
void PostfixParser<V, O>::push_variable(variable_type&& var)
|
||||
auto PostfixParser<V, O>::push_variable(variable_type&& var) -> bool
|
||||
{
|
||||
return push_variable_impl(std::move(var));
|
||||
}
|
||||
|
||||
template <typename V, typename O>
|
||||
template <typename Op>
|
||||
void PostfixParser<V, O>::push_operator_impl(Op&& op)
|
||||
auto PostfixParser<V, O>::push_operator_impl(Op&& op) -> bool
|
||||
{
|
||||
if (m_orphans.size() < 2)
|
||||
{
|
||||
throw std::invalid_argument("Invalid expression");
|
||||
return false;
|
||||
}
|
||||
const auto right = orphans_pop();
|
||||
const auto left = orphans_pop();
|
||||
orphans_push(m_tree.add_branch(std::forward<Op>(op), left, right));
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename V, typename O>
|
||||
void PostfixParser<V, O>::push_operator(const operator_type& op)
|
||||
auto PostfixParser<V, O>::push_operator(const operator_type& op) -> bool
|
||||
{
|
||||
return push_operator_impl(op);
|
||||
}
|
||||
|
||||
template <typename V, typename O>
|
||||
void PostfixParser<V, O>::push_operator(operator_type&& op)
|
||||
auto PostfixParser<V, O>::push_operator(operator_type&& op) -> bool
|
||||
{
|
||||
return push_operator_impl(std::move(op));
|
||||
}
|
||||
|
||||
template <typename V, typename O>
|
||||
void PostfixParser<V, O>::finalize()
|
||||
auto PostfixParser<V, O>::finalize() -> bool
|
||||
{
|
||||
if (((m_orphans.size() == 1) && !m_tree.empty()) || (m_orphans.empty() && m_tree.empty()))
|
||||
{
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
throw std::invalid_argument("Incomplete expression");
|
||||
return false; // Incomplete expression
|
||||
}
|
||||
|
||||
template <typename V, typename O>
|
||||
|
@ -377,138 +374,127 @@ namespace mamba::util
|
|||
|
||||
template <typename V, typename O, typename C>
|
||||
template <typename Var>
|
||||
void InfixParser<V, O, C>::push_variable_impl(Var&& var)
|
||||
auto InfixParser<V, O, C>::push_variable_impl(Var&& var) -> bool
|
||||
{
|
||||
// Input check
|
||||
if (m_expects_op)
|
||||
{
|
||||
std::string msg = {};
|
||||
if constexpr (fmt::is_formattable<Var>::value)
|
||||
{
|
||||
msg = fmt::format("Unexpected variable: {}", var);
|
||||
}
|
||||
else if constexpr (is_ostreamable_v<Var>)
|
||||
{
|
||||
msg = fmt::format("Unexpected variable: {}", fmt::streamed(var));
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = "Unexpected variable";
|
||||
}
|
||||
throw std::invalid_argument(std::move(msg));
|
||||
return false; // Unexpected variable
|
||||
}
|
||||
m_expects_op = true;
|
||||
// Parsing
|
||||
m_postfix_parser.push_variable(std::forward<Var>(var));
|
||||
return m_postfix_parser.push_variable(std::forward<Var>(var));
|
||||
}
|
||||
|
||||
template <typename V, typename O, typename C>
|
||||
void InfixParser<V, O, C>::push_variable(const variable_type& var)
|
||||
auto InfixParser<V, O, C>::push_variable(const variable_type& var) -> bool
|
||||
{
|
||||
return push_variable_impl(var);
|
||||
}
|
||||
|
||||
template <typename V, typename O, typename C>
|
||||
void InfixParser<V, O, C>::push_variable(variable_type&& var)
|
||||
auto InfixParser<V, O, C>::push_variable(variable_type&& var) -> bool
|
||||
{
|
||||
return push_variable_impl(std::move(var));
|
||||
}
|
||||
|
||||
template <typename V, typename O, typename C>
|
||||
template <typename Op>
|
||||
void InfixParser<V, O, C>::push_operator_impl(Op&& op)
|
||||
auto InfixParser<V, O, C>::push_operator_impl(Op&& op) -> bool
|
||||
{
|
||||
// Input check
|
||||
if (!m_expects_op)
|
||||
{
|
||||
std::string msg = {};
|
||||
if constexpr (fmt::is_formattable<Op>::value)
|
||||
{
|
||||
msg = fmt::format("Unexpected operator: {}", op);
|
||||
}
|
||||
else if constexpr (is_ostreamable_v<Op>)
|
||||
{
|
||||
msg = fmt::format("Unexpected operator: {}", fmt::streamed(op));
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = "Unexpected operator";
|
||||
}
|
||||
throw std::invalid_argument(std::move(msg));
|
||||
return false;
|
||||
}
|
||||
m_expects_op = false;
|
||||
// Parsing
|
||||
while (stack_top_is_op_with_greater_precedence_than(op))
|
||||
{
|
||||
m_postfix_parser.push_operator(std::get<operator_type>(stack_pop()));
|
||||
bool pushed = m_postfix_parser.push_operator(std::get<operator_type>(stack_pop()));
|
||||
if (!pushed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
stack_push(std::forward<Op>(op));
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename V, typename O, typename C>
|
||||
void InfixParser<V, O, C>::push_operator(const operator_type& op)
|
||||
auto InfixParser<V, O, C>::push_operator(const operator_type& op) -> bool
|
||||
{
|
||||
return push_operator_impl(op);
|
||||
}
|
||||
|
||||
template <typename V, typename O, typename C>
|
||||
void InfixParser<V, O, C>::push_operator(operator_type&& op)
|
||||
auto InfixParser<V, O, C>::push_operator(operator_type&& op) -> bool
|
||||
{
|
||||
return push_operator_impl(std::move(op));
|
||||
}
|
||||
|
||||
template <typename V, typename O, typename C>
|
||||
void InfixParser<V, O, C>::push_left_parenthesis()
|
||||
auto InfixParser<V, O, C>::push_left_parenthesis() -> bool
|
||||
{
|
||||
// Input check
|
||||
if (m_expects_op)
|
||||
{
|
||||
throw std::invalid_argument("Unexpected left parenthesis");
|
||||
return false; // Unexpected left parenthesis
|
||||
}
|
||||
++m_parenthesis_level;
|
||||
// Parsing
|
||||
stack_push(LeftParenthesis{});
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename V, typename O, typename C>
|
||||
void InfixParser<V, O, C>::push_right_parenthesis()
|
||||
auto InfixParser<V, O, C>::push_right_parenthesis() -> bool
|
||||
{
|
||||
// Input check
|
||||
if (!m_expects_op || (m_parenthesis_level == 0))
|
||||
{
|
||||
throw std::invalid_argument("Unexpected right parenthesis");
|
||||
return false; // Unexpected right parenthesis
|
||||
}
|
||||
--m_parenthesis_level;
|
||||
// Parsing
|
||||
while (!stack_top_is_parenthesis())
|
||||
{
|
||||
assert(!stack_empty());
|
||||
m_postfix_parser.push_operator(std::get<operator_type>(stack_pop()));
|
||||
bool pushed = m_postfix_parser.push_operator(std::get<operator_type>(stack_pop()));
|
||||
if (!pushed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
assert(stack_top_is_parenthesis());
|
||||
stack_pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename V, typename O, typename C>
|
||||
void InfixParser<V, O, C>::finalize()
|
||||
auto InfixParser<V, O, C>::finalize() -> bool
|
||||
{
|
||||
// Empty expression case
|
||||
if (m_postfix_parser.tree().empty() && stack_empty())
|
||||
{
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
// Input check
|
||||
if (!m_expects_op || (m_parenthesis_level != 0))
|
||||
{
|
||||
throw std::invalid_argument("Invalid expression");
|
||||
return false; // Invalid expression
|
||||
}
|
||||
// Parsing
|
||||
while (!stack_empty())
|
||||
{
|
||||
assert(!stack_top_is_parenthesis());
|
||||
m_postfix_parser.push_operator(std::get<operator_type>(stack_pop()));
|
||||
bool pushed = m_postfix_parser.push_operator(std::get<operator_type>(stack_pop()));
|
||||
if (!pushed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
m_postfix_parser.finalize();
|
||||
return m_postfix_parser.finalize();
|
||||
}
|
||||
|
||||
template <typename V, typename O, typename C>
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <tl/expected.hpp>
|
||||
|
||||
namespace mamba::util
|
||||
{
|
||||
namespace detail
|
||||
|
@ -68,6 +70,11 @@ namespace mamba::util
|
|||
using Encode = detail::Encode;
|
||||
using Decode = detail::Decode;
|
||||
|
||||
struct ParseError
|
||||
{
|
||||
std::string what;
|
||||
};
|
||||
|
||||
inline static constexpr std::string_view https = "https";
|
||||
inline static constexpr std::string_view localhost = "localhost";
|
||||
|
||||
|
@ -84,7 +91,7 @@ namespace mamba::util
|
|||
* @see Encode
|
||||
* @see mamba::util::url_encode
|
||||
*/
|
||||
[[nodiscard]] static auto parse(std::string_view url) -> URL;
|
||||
[[nodiscard]] static auto parse(std::string_view url) -> tl::expected<URL, ParseError>;
|
||||
|
||||
/** Create a local URL. */
|
||||
URL() = default;
|
||||
|
|
|
@ -36,7 +36,9 @@ namespace mamba
|
|||
template <typename Param>
|
||||
auto make_unique_chan(std::string_view loc, const Param& params) -> specs::Channel
|
||||
{
|
||||
auto uc = specs::UnresolvedChannel::parse(loc);
|
||||
auto uc = specs::UnresolvedChannel::parse(loc)
|
||||
.or_else([](specs::ParseError&& error) { throw std::move(error); })
|
||||
.value();
|
||||
auto channels = specs::Channel::resolve(std::move(uc), params);
|
||||
assert(channels.size() == 1);
|
||||
return std::move(channels.front());
|
||||
|
@ -178,7 +180,9 @@ namespace mamba
|
|||
out.reserve(ctx.repodata_has_zst.size());
|
||||
for (const auto& loc : ctx.repodata_has_zst)
|
||||
{
|
||||
auto uc = specs::UnresolvedChannel::parse(loc);
|
||||
auto uc = specs::UnresolvedChannel::parse(loc)
|
||||
.or_else([](specs::ParseError&& error) { throw std::move(error); })
|
||||
.value();
|
||||
auto channels = specs::Channel::resolve(std::move(uc), params);
|
||||
for (auto& chan : channels)
|
||||
{
|
||||
|
@ -239,7 +243,12 @@ namespace mamba
|
|||
|
||||
auto [it, inserted] = m_channel_cache.emplace(
|
||||
name,
|
||||
Channel::resolve(specs::UnresolvedChannel::parse(name), params())
|
||||
Channel::resolve(
|
||||
specs::UnresolvedChannel::parse(name)
|
||||
.or_else([](specs::ParseError&& error) { throw std::move(error); })
|
||||
.value(),
|
||||
params()
|
||||
)
|
||||
);
|
||||
assert(inserted);
|
||||
return it->second;
|
||||
|
|
|
@ -66,7 +66,8 @@ namespace mamba
|
|||
{
|
||||
return std::tuple<decltype(pkg.name) const&, specs::Version>(
|
||||
pkg.name,
|
||||
specs::Version::parse(pkg.version)
|
||||
// Failed parsing last
|
||||
specs::Version::parse(pkg.version).value_or(specs::Version())
|
||||
);
|
||||
};
|
||||
return attrs(*lhs) < attrs(*rhs);
|
||||
|
|
|
@ -67,7 +67,10 @@ namespace mamba
|
|||
out.set_name(specs::MatchSpec::NameSpec(pkg.name));
|
||||
if (!pkg.version.empty())
|
||||
{
|
||||
out.set_version(specs::VersionSpec::parse(fmt::format("=={}", pkg.version)));
|
||||
out.set_version(specs::VersionSpec::parse(fmt::format("=={}", pkg.version))
|
||||
.or_else([](specs::ParseError&& error)
|
||||
{ throw std::move(error); })
|
||||
.value());
|
||||
}
|
||||
if (!pkg.build_string.empty())
|
||||
{
|
||||
|
|
|
@ -1568,7 +1568,7 @@ namespace mamba
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto url_parsed = util::URL::parse(url);
|
||||
const auto url_parsed = util::URL::parse(url).value();
|
||||
auto scheme = url_parsed.scheme();
|
||||
auto host = url_parsed.host();
|
||||
std::vector<std::string> options;
|
||||
|
|
|
@ -323,7 +323,7 @@ namespace mamba::download
|
|||
p_handle->add_header(user_agent);
|
||||
|
||||
// get url host
|
||||
const auto url_handler = util::URL::parse(p_request->url);
|
||||
const auto url_handler = util::URL::parse(p_request->url).value();
|
||||
auto host = url_handler.host();
|
||||
const auto port = url_handler.port();
|
||||
if (port.size())
|
||||
|
|
|
@ -1059,10 +1059,10 @@ namespace mamba::solver::libsolv
|
|||
{
|
||||
if (auto newer = find_new_python_in_solution(solution))
|
||||
{
|
||||
return !python_binary_compatible(
|
||||
specs::Version::parse(installed->version()),
|
||||
specs::Version::parse(newer->get().version)
|
||||
);
|
||||
auto installed_ver = specs::Version::parse(installed->version());
|
||||
auto newer_ver = specs::Version::parse(newer->get().version);
|
||||
return !installed_ver.has_value() || !newer_ver.has_value()
|
||||
|| !python_binary_compatible(installed_ver.value(), newer_ver.value());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -1182,8 +1182,31 @@ namespace mamba::solver::libsolv
|
|||
}
|
||||
|
||||
auto ms_modified = ms;
|
||||
ms_modified.set_channel(specs::UnresolvedChannel::parse(solvable->channel()));
|
||||
ms_modified.set_version(specs::VersionSpec::parse(solvable->version()));
|
||||
auto unresolved_chan = specs::UnresolvedChannel::parse(solvable->channel());
|
||||
if (unresolved_chan.has_value())
|
||||
{
|
||||
ms_modified.set_channel(std::move(unresolved_chan).value());
|
||||
}
|
||||
else
|
||||
{
|
||||
return make_unexpected(
|
||||
std::move(unresolved_chan).error().what(),
|
||||
mamba_error_code::invalid_spec
|
||||
);
|
||||
}
|
||||
auto version_spec = specs::VersionSpec::parse(solvable->version());
|
||||
if (version_spec.has_value())
|
||||
{
|
||||
ms_modified.set_version(std::move(version_spec).value());
|
||||
}
|
||||
else
|
||||
{
|
||||
return make_unexpected(
|
||||
std::move(version_spec).error().what(),
|
||||
mamba_error_code::invalid_spec
|
||||
);
|
||||
}
|
||||
|
||||
ms_modified.set_build_string(specs::GlobSpec(std::string(solvable->build_string())));
|
||||
|
||||
LOG_INFO << "Reinstall " << ms_modified.conda_build_form() << " from channel "
|
||||
|
|
|
@ -160,7 +160,7 @@ fmt::formatter<mamba::specs::BuildNumberPredicate>::format(
|
|||
|
||||
namespace mamba::specs
|
||||
{
|
||||
auto BuildNumberSpec::parse(std::string_view str) -> BuildNumberSpec
|
||||
auto BuildNumberSpec::parse(std::string_view str) -> expected_parse_t<BuildNumberSpec>
|
||||
{
|
||||
str = util::strip(str);
|
||||
if (std::find(all_free_strs.cbegin(), all_free_strs.cend(), str) != all_free_strs.cend())
|
||||
|
@ -179,7 +179,7 @@ namespace mamba::specs
|
|||
);
|
||||
if ((ec != std::errc()) || (ptr != (num_str.data() + num_str.size())))
|
||||
{
|
||||
throw std::invalid_argument(
|
||||
return make_unexpected_parse(
|
||||
fmt::format(R"(Expected number in build number spec "{}")", str)
|
||||
);
|
||||
}
|
||||
|
@ -213,7 +213,7 @@ namespace mamba::specs
|
|||
return BuildNumberSpec(BuildNumberPredicate::make_less_equal(build_number));
|
||||
}
|
||||
|
||||
throw std::invalid_argument(fmt::format(R"(Invalid build number operator in"{}")", str));
|
||||
return make_unexpected_parse(fmt::format(R"(Invalid build number operator in"{}")", str));
|
||||
}
|
||||
|
||||
BuildNumberSpec::BuildNumberSpec()
|
||||
|
@ -245,7 +245,9 @@ namespace mamba::specs
|
|||
{
|
||||
auto operator""_bs(const char* str, std::size_t len) -> BuildNumberSpec
|
||||
{
|
||||
return BuildNumberSpec::parse({ str, len });
|
||||
return BuildNumberSpec::parse({ str, len })
|
||||
.or_else([](ParseError&& err) { throw std::move(err); })
|
||||
.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "mamba/specs/archive.hpp"
|
||||
#include "mamba/specs/conda_url.hpp"
|
||||
#include "mamba/specs/error.hpp"
|
||||
#include "mamba/util/encoding.hpp"
|
||||
#include "mamba/util/string.hpp"
|
||||
|
||||
|
@ -120,7 +121,14 @@ namespace mamba::specs
|
|||
|
||||
auto CondaURL::parse(std::string_view url) -> CondaURL
|
||||
{
|
||||
return CondaURL(URL::parse(url));
|
||||
return CondaURL(
|
||||
URL::parse(url)
|
||||
.or_else(
|
||||
[&](const auto&)
|
||||
{ throw specs::ParseError(fmt::format(R"(Failed to parse URL "{}")", url)); }
|
||||
)
|
||||
.value()
|
||||
);
|
||||
}
|
||||
|
||||
void CondaURL::set_path(std::string_view path, Encode::yes_type)
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace mamba::specs
|
|||
auto GlobSpec::contains(std::string_view str) const -> bool
|
||||
{
|
||||
// is_free is not required but returns faster in the default case.
|
||||
return is_free() || util::glob_match(m_pattern, str);
|
||||
return is_free() || util::glob_match(m_pattern, str, glob_pattern);
|
||||
}
|
||||
|
||||
auto GlobSpec::is_free() const -> bool
|
||||
|
@ -35,7 +35,7 @@ namespace mamba::specs
|
|||
|
||||
auto GlobSpec::is_exact() const -> bool
|
||||
{
|
||||
return !util::contains(m_pattern, '*');
|
||||
return !util::contains(m_pattern, glob_pattern);
|
||||
}
|
||||
|
||||
auto GlobSpec::str() const -> const std::string&
|
||||
|
|
|
@ -29,7 +29,9 @@ namespace mamba::specs
|
|||
};
|
||||
|
||||
auto out = MatchSpec();
|
||||
out.m_channel = UnresolvedChannel::parse(spec);
|
||||
out.m_channel = UnresolvedChannel::parse(spec)
|
||||
.or_else([](specs::ParseError&& error) { throw std::move(error); })
|
||||
.value();
|
||||
auto [_, pkg] = util::rsplit_once(out.m_channel->location(), '/');
|
||||
out.m_filename = std::string(pkg);
|
||||
out.m_url = util::path_or_url_to_url(spec);
|
||||
|
@ -44,7 +46,9 @@ namespace mamba::specs
|
|||
|
||||
// Version
|
||||
std::tie(head, tail) = util::rsplit_once(head.value(), '-');
|
||||
out.m_version = VersionSpec::parse(tail);
|
||||
out.m_version = VersionSpec::parse(tail)
|
||||
.or_else([](ParseError&& error) { throw std::move(error); })
|
||||
.value();
|
||||
if (!head.has_value())
|
||||
{
|
||||
fail_parse();
|
||||
|
@ -65,7 +69,9 @@ namespace mamba::specs
|
|||
if (pos == s.npos || pos == 0)
|
||||
{
|
||||
return {
|
||||
VersionSpec::parse(s),
|
||||
VersionSpec::parse(s)
|
||||
.or_else([](ParseError&& error) { throw std::move(error); })
|
||||
.value(),
|
||||
MatchSpec::BuildStringSpec(),
|
||||
};
|
||||
}
|
||||
|
@ -77,7 +83,9 @@ namespace mamba::specs
|
|||
if (d == '=' || d == '!' || d == '|' || d == ',' || d == '<' || d == '>' || d == '~')
|
||||
{
|
||||
return {
|
||||
VersionSpec::parse(s),
|
||||
VersionSpec::parse(s)
|
||||
.or_else([](ParseError&& error) { throw std::move(error); })
|
||||
.value(),
|
||||
MatchSpec::BuildStringSpec(),
|
||||
};
|
||||
}
|
||||
|
@ -85,7 +93,9 @@ namespace mamba::specs
|
|||
// c is either ' ' or pm1 is none of the forbidden chars
|
||||
|
||||
return {
|
||||
VersionSpec::parse(s.substr(0, pos)),
|
||||
VersionSpec::parse(s.substr(0, pos))
|
||||
.or_else([](ParseError&& error) { throw std::move(error); })
|
||||
.value(),
|
||||
MatchSpec::BuildStringSpec(std::string(s.substr(pos + 1))),
|
||||
};
|
||||
}
|
||||
|
@ -170,7 +180,9 @@ namespace mamba::specs
|
|||
std::string channel_str;
|
||||
if (m5_len == 3)
|
||||
{
|
||||
out.m_channel = UnresolvedChannel::parse(m5[0]);
|
||||
out.m_channel = UnresolvedChannel::parse(m5[0])
|
||||
.or_else([](specs::ParseError&& error) { throw std::move(error); })
|
||||
.value();
|
||||
out.m_name_space = m5[1];
|
||||
spec_str = m5[2];
|
||||
}
|
||||
|
@ -245,7 +257,11 @@ namespace mamba::specs
|
|||
|
||||
if (const auto& val = at_or(extra, "build_number", ""); !val.empty())
|
||||
{
|
||||
out.set_build_number(BuildNumberSpec::parse(val));
|
||||
out.set_build_number(BuildNumberSpec::parse(val)
|
||||
.or_else([](ParseError&& error) { throw std::move(error); })
|
||||
.value()
|
||||
|
||||
);
|
||||
}
|
||||
if (const auto& val = at_or(extra, "build", ""); !val.empty())
|
||||
{
|
||||
|
@ -253,11 +269,16 @@ namespace mamba::specs
|
|||
}
|
||||
if (const auto& val = at_or(extra, "version", ""); !val.empty())
|
||||
{
|
||||
out.set_version(VersionSpec::parse(val));
|
||||
out.set_version(
|
||||
VersionSpec::parse(val).or_else([](ParseError&& error) { throw std::move(error); }
|
||||
).value()
|
||||
);
|
||||
}
|
||||
if (const auto& val = at_or(extra, "channel", ""); !val.empty())
|
||||
{
|
||||
out.set_channel(UnresolvedChannel::parse(val));
|
||||
out.set_channel(UnresolvedChannel::parse(val)
|
||||
.or_else([](ParseError&& error) { throw std::move(error); })
|
||||
.value());
|
||||
}
|
||||
if (const auto& val = at_or(extra, "subdir", ""); !val.empty())
|
||||
{
|
||||
|
|
|
@ -42,7 +42,9 @@ namespace mamba::specs
|
|||
using mamba::util::deserialize_maybe_missing;
|
||||
|
||||
p.name = j.at("name");
|
||||
p.version = Version::parse(j.at("version").template get<std::string_view>());
|
||||
p.version = Version::parse(j.at("version").template get<std::string_view>())
|
||||
.or_else([](ParseError&& error) { throw std::move(error); })
|
||||
.value();
|
||||
p.build_string = j.at("build");
|
||||
p.build_number = j.at("build_number");
|
||||
deserialize_maybe_missing(j, "subdir", p.subdir);
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace mamba::specs
|
|||
}
|
||||
|
||||
auto split_location_platform(std::string_view str)
|
||||
-> std::pair<std::string, dynamic_platform_set>
|
||||
-> expected_parse_t<std::pair<std::string, dynamic_platform_set>>
|
||||
{
|
||||
if (util::ends_with(str, ']'))
|
||||
{
|
||||
|
@ -76,10 +76,14 @@ namespace mamba::specs
|
|||
const auto start_pos = str.find_last_of('[');
|
||||
if ((start_pos != std::string_view::npos) && (start_pos != 0))
|
||||
{
|
||||
return {
|
||||
return { {
|
||||
std::string(util::rstrip(str.substr(0, start_pos))),
|
||||
parse_platform_list(str.substr(start_pos + 1, str.size() - start_pos - 2)),
|
||||
};
|
||||
} };
|
||||
}
|
||||
else
|
||||
{
|
||||
return make_unexpected_parse(R"(Unexpected closing backet "]")");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,16 +96,16 @@ namespace mamba::specs
|
|||
if (!plat.empty())
|
||||
{
|
||||
rest = util::rstrip(rest, '/');
|
||||
return {
|
||||
return { {
|
||||
std::move(rest),
|
||||
{ std::move(plat) },
|
||||
};
|
||||
} };
|
||||
}
|
||||
}
|
||||
|
||||
// For single archive channel specs, we don't need to compute platform filters
|
||||
// since they are not needed to compute URLs.
|
||||
return { std::string(util::rstrip(str)), {} };
|
||||
return { { std::string(util::rstrip(str)), {} } };
|
||||
}
|
||||
|
||||
auto parse_path(std::string_view str) -> std::string
|
||||
|
@ -122,15 +126,27 @@ namespace mamba::specs
|
|||
}
|
||||
}
|
||||
|
||||
auto UnresolvedChannel::parse(std::string_view str) -> UnresolvedChannel
|
||||
auto UnresolvedChannel::parse(std::string_view str) -> expected_parse_t<UnresolvedChannel>
|
||||
{
|
||||
str = util::strip(str);
|
||||
if (is_unknown_channel(str))
|
||||
{
|
||||
return { std::string(unknown_channel), {}, Type::Unknown };
|
||||
return { { std::string(unknown_channel), {}, Type::Unknown } };
|
||||
}
|
||||
|
||||
auto [location, filters] = split_location_platform(str);
|
||||
auto location = std::string();
|
||||
auto filters = dynamic_platform_set();
|
||||
auto split_outcome = split_location_platform(str);
|
||||
if (split_outcome)
|
||||
{
|
||||
std::tie(location, filters) = std::move(split_outcome).value();
|
||||
}
|
||||
else
|
||||
{
|
||||
return make_unexpected_parse(
|
||||
fmt::format(R"(Error parsing channel "{}": {})", str, split_outcome.error().what())
|
||||
);
|
||||
}
|
||||
|
||||
const std::string_view scheme = util::url_get_scheme(location);
|
||||
Type type = {};
|
||||
|
@ -152,7 +168,7 @@ namespace mamba::specs
|
|||
type = Type::Name;
|
||||
}
|
||||
|
||||
return { std::move(location), std::move(filters), type };
|
||||
return { { std::move(location), std::move(filters), type } };
|
||||
}
|
||||
|
||||
UnresolvedChannel::UnresolvedChannel(std::string location, dynamic_platform_set filters, Type type)
|
||||
|
|
|
@ -534,17 +534,18 @@ namespace mamba::specs
|
|||
}
|
||||
|
||||
template <typename Int>
|
||||
auto parse_leading_epoch(std::string_view str) -> std::pair<Int, std::string_view>
|
||||
auto parse_leading_epoch(std::string_view str)
|
||||
-> expected_parse_t<std::pair<Int, std::string_view>>
|
||||
{
|
||||
const auto delim_pos = str.find(Version::epoch_delim);
|
||||
// No epoch is specified
|
||||
if (delim_pos == std::string_view::npos) // TODO(C++20) [[likely]]
|
||||
{
|
||||
return { Int(0), str };
|
||||
return { { Int(0), str } };
|
||||
}
|
||||
if (delim_pos == 0)
|
||||
{
|
||||
throw std::invalid_argument(
|
||||
return make_unexpected_parse(
|
||||
fmt::format("Empty epoch delimited by '{}'.", Version::epoch_delim)
|
||||
);
|
||||
}
|
||||
|
@ -554,12 +555,12 @@ namespace mamba::specs
|
|||
// Epoch is not a number (or empty)
|
||||
if (!maybe_int.has_value())
|
||||
{
|
||||
throw std::invalid_argument(
|
||||
return make_unexpected_parse(
|
||||
fmt::format("Epoch should be a number, got '{}'.", epoch_str)
|
||||
);
|
||||
}
|
||||
// Found an epoch
|
||||
return { maybe_int.value(), str.substr(delim_pos + 1) };
|
||||
return { { maybe_int.value(), str.substr(delim_pos + 1) } };
|
||||
}
|
||||
|
||||
template <typename Int>
|
||||
|
@ -617,7 +618,7 @@ namespace mamba::specs
|
|||
return atoms;
|
||||
}
|
||||
|
||||
void check_common_version(std::string_view str)
|
||||
auto check_common_version(std::string_view str) -> expected_parse_t<void>
|
||||
{
|
||||
// `_` and `-` delimiter cannot be used together.
|
||||
// Special meaning for `_` at the end of the string.
|
||||
|
@ -625,7 +626,7 @@ namespace mamba::specs
|
|||
&& (str.find(Version::part_delim_special) < str.size() - 1)) // TODO(C++20)
|
||||
// [[unlikely]]
|
||||
{
|
||||
throw std::invalid_argument(fmt::format(
|
||||
return make_unexpected_parse(fmt::format(
|
||||
"Cannot use both '{}' and '{}' delimiters in {}'.",
|
||||
Version::part_delim_alt,
|
||||
Version::part_delim_special,
|
||||
|
@ -643,16 +644,20 @@ namespace mamba::specs
|
|||
};
|
||||
if (std::find_if_not(str.cbegin(), str.cend(), allowed_char) != str.cend())
|
||||
{
|
||||
throw std::invalid_argument(
|
||||
return make_unexpected_parse(
|
||||
fmt::format("Version contains invalid characters in {}.", str)
|
||||
);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
auto parse_common_version(std::string_view str) -> CommonVersion
|
||||
auto parse_common_version(std::string_view str) -> expected_parse_t<CommonVersion>
|
||||
{
|
||||
assert(!str.empty());
|
||||
check_common_version(str);
|
||||
if (auto outcome = check_common_version(str); !outcome)
|
||||
{
|
||||
return make_unexpected_parse(outcome.error());
|
||||
}
|
||||
|
||||
static constexpr auto delims_buf = std::array{
|
||||
Version::part_delim,
|
||||
|
@ -678,7 +683,7 @@ namespace mamba::specs
|
|||
if ((tail_delim_pos == 0) || (tail_delim_pos == tail.size() - 1)) // TODO(C++20)
|
||||
// [[unlikely]]
|
||||
{
|
||||
throw std::invalid_argument(fmt::format("Empty part in '{}'.", str));
|
||||
return make_unexpected_parse(fmt::format("Empty part in '{}'.", str));
|
||||
}
|
||||
parts.push_back(parse_part(tail.substr(0, tail_delim_pos)));
|
||||
if (tail_delim_pos == std::string_view::npos)
|
||||
|
@ -687,63 +692,86 @@ namespace mamba::specs
|
|||
}
|
||||
tail = tail.substr(tail_delim_pos + 1);
|
||||
}
|
||||
return parts;
|
||||
return { std::move(parts) };
|
||||
}
|
||||
|
||||
auto parse_trailing_local_version(std::string_view str)
|
||||
-> std::pair<std::string_view, CommonVersion>
|
||||
-> expected_parse_t<std::pair<std::string_view, CommonVersion>>
|
||||
{
|
||||
const auto delim_pos = str.rfind(Version::local_delim);
|
||||
// No local is specified
|
||||
if (delim_pos == std::string_view::npos) // TODO(C++20) [[likely]]
|
||||
{
|
||||
return { str, {} };
|
||||
return { { str, {} } };
|
||||
}
|
||||
// local specified but empty
|
||||
if (delim_pos + 1 == str.size())
|
||||
{
|
||||
throw std::invalid_argument(
|
||||
return make_unexpected_parse(
|
||||
fmt::format("Empty local version delimited by '{}'.", Version::local_delim)
|
||||
);
|
||||
}
|
||||
return { str.substr(0, delim_pos), parse_common_version(str.substr(delim_pos + 1)) };
|
||||
return parse_common_version(str.substr(delim_pos + 1))
|
||||
.transform(
|
||||
[&](CommonVersion&& version) {
|
||||
return std::pair{ str.substr(0, delim_pos), std::move(version) };
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
auto parse_version(std::string_view str) -> CommonVersion
|
||||
auto parse_version(std::string_view str) -> expected_parse_t<CommonVersion>
|
||||
{
|
||||
if (str.empty())
|
||||
{
|
||||
throw std::invalid_argument("Empty version.");
|
||||
return make_unexpected_parse("Empty version.");
|
||||
}
|
||||
return parse_common_version(str);
|
||||
}
|
||||
}
|
||||
|
||||
auto Version::parse(std::string_view str) -> Version
|
||||
auto Version::parse(std::string_view str) -> expected_parse_t<Version>
|
||||
{
|
||||
str = util::strip(str);
|
||||
try
|
||||
|
||||
auto make_unexpected = [&str](const ParseError& error)
|
||||
{
|
||||
auto [epoch, version_and_local_str] = parse_leading_epoch<std::size_t>(str);
|
||||
auto [version_str, local] = parse_trailing_local_version(version_and_local_str);
|
||||
auto version = parse_version(version_str);
|
||||
return {
|
||||
/* .epoch= */ epoch,
|
||||
/* .version= */ std::move(version),
|
||||
/* .local= */ std::move(local),
|
||||
};
|
||||
}
|
||||
catch (const std::invalid_argument& ia)
|
||||
return make_unexpected_parse(
|
||||
fmt::format(R"(Error parsing version "{}". {})", str, error.what())
|
||||
);
|
||||
};
|
||||
|
||||
auto epoch_rest = parse_leading_epoch<std::size_t>(str);
|
||||
if (!epoch_rest)
|
||||
{
|
||||
throw std::invalid_argument(fmt::format("Error parsing version '{}'. {}", str, ia.what()));
|
||||
return make_unexpected(epoch_rest.error());
|
||||
}
|
||||
|
||||
auto version_and_local = parse_trailing_local_version(epoch_rest->second);
|
||||
if (!version_and_local)
|
||||
{
|
||||
return make_unexpected(version_and_local.error());
|
||||
}
|
||||
|
||||
auto version = parse_version(version_and_local->first);
|
||||
if (!version)
|
||||
{
|
||||
return make_unexpected(version.error());
|
||||
}
|
||||
|
||||
return { {
|
||||
/* .epoch= */ epoch_rest->first,
|
||||
/* .version= */ std::move(version).value(),
|
||||
/* .local= */ std::move(version_and_local)->second,
|
||||
} };
|
||||
}
|
||||
|
||||
namespace version_literals
|
||||
{
|
||||
auto operator""_v(const char* str, std::size_t len) -> Version
|
||||
{
|
||||
return Version::parse(std::literals::string_view_literals::operator""sv(str, len));
|
||||
return Version::parse(std::literals::string_view_literals::operator""sv(str, len))
|
||||
.or_else([](ParseError&& error) { throw std::move(error); })
|
||||
.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
@ -320,7 +319,7 @@ namespace mamba::specs
|
|||
return std::find(range.cbegin(), range.cend(), val) != range.cend();
|
||||
}
|
||||
|
||||
auto parse_op_and_version(std::string_view str) -> VersionPredicate
|
||||
auto parse_op_and_version(std::string_view str) -> expected_parse_t<VersionPredicate>
|
||||
{
|
||||
str = util::strip(str);
|
||||
// WARNING order is important since some operator are prefix of others.
|
||||
|
@ -330,35 +329,40 @@ namespace mamba::specs
|
|||
}
|
||||
if (util::starts_with(str, VersionSpec::greater_equal_str))
|
||||
{
|
||||
return VersionPredicate::make_greater_equal(
|
||||
Version::parse(str.substr(VersionSpec::greater_equal_str.size()))
|
||||
);
|
||||
return Version::parse(str.substr(VersionSpec::greater_equal_str.size()))
|
||||
.transform([](specs::Version&& ver)
|
||||
{ return VersionPredicate::make_greater_equal(std::move(ver)); });
|
||||
}
|
||||
if (util::starts_with(str, VersionSpec::greater_str))
|
||||
{
|
||||
return VersionPredicate::make_greater(
|
||||
Version::parse(str.substr(VersionSpec::greater_str.size()))
|
||||
);
|
||||
return Version::parse(str.substr(VersionSpec::greater_str.size()))
|
||||
.transform([](specs::Version&& ver)
|
||||
{ return VersionPredicate::make_greater(std::move(ver)); });
|
||||
}
|
||||
if (util::starts_with(str, VersionSpec::less_equal_str))
|
||||
{
|
||||
return VersionPredicate::make_less_equal(
|
||||
Version::parse(str.substr(VersionSpec::less_equal_str.size()))
|
||||
);
|
||||
return Version::parse(str.substr(VersionSpec::less_equal_str.size()))
|
||||
.transform([](specs::Version&& ver)
|
||||
{ return VersionPredicate::make_less_equal(std::move(ver)); });
|
||||
}
|
||||
if (util::starts_with(str, VersionSpec::less_str))
|
||||
{
|
||||
return VersionPredicate::make_less(
|
||||
Version::parse(str.substr(VersionSpec::less_str.size()))
|
||||
);
|
||||
return Version::parse(str.substr(VersionSpec::less_str.size()))
|
||||
.transform([](specs::Version&& ver)
|
||||
{ return VersionPredicate::make_less(std::move(ver)); });
|
||||
}
|
||||
if (util::starts_with(str, VersionSpec::compatible_str))
|
||||
{
|
||||
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 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);
|
||||
return Version::parse(str.substr(VersionSpec::compatible_str.size()))
|
||||
.transform(
|
||||
[](specs::Version&& ver)
|
||||
{
|
||||
// in ``~=1.1`` level is assumed to be 1, in ``~=1.1.1`` level 2, etc.
|
||||
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);
|
||||
}
|
||||
);
|
||||
}
|
||||
const bool has_glob_suffix = util::ends_with(str, VersionSpec::glob_suffix_str);
|
||||
const std::size_t glob_len = has_glob_suffix * VersionSpec::glob_suffix_str.size();
|
||||
|
@ -368,13 +372,15 @@ namespace mamba::specs
|
|||
// Glob suffix changes meaning for ==1.3.*
|
||||
if (has_glob_suffix)
|
||||
{
|
||||
return VersionPredicate::make_starts_with(
|
||||
Version::parse(str.substr(start, str.size() - glob_len - start))
|
||||
);
|
||||
return Version::parse(str.substr(VersionSpec::less_equal_str.size()))
|
||||
.transform([](specs::Version&& ver)
|
||||
{ return VersionPredicate::make_starts_with(std::move(ver)); });
|
||||
}
|
||||
else
|
||||
{
|
||||
return VersionPredicate::make_equal_to(Version::parse(str.substr(start)));
|
||||
return Version::parse(str.substr(start))
|
||||
.transform([](specs::Version&& ver)
|
||||
{ return VersionPredicate::make_equal_to(std::move(ver)); });
|
||||
}
|
||||
}
|
||||
if (util::starts_with(str, VersionSpec::not_equal_str))
|
||||
|
@ -383,22 +389,25 @@ namespace mamba::specs
|
|||
// Glob suffix changes meaning for !=1.3.*
|
||||
if (has_glob_suffix)
|
||||
{
|
||||
return VersionPredicate::make_not_starts_with(
|
||||
Version::parse(str.substr(start, str.size() - glob_len - start))
|
||||
);
|
||||
return Version::parse(str.substr(start, str.size() - glob_len - start))
|
||||
.transform([](specs::Version&& ver)
|
||||
{ return VersionPredicate::make_not_starts_with(std::move(ver)); }
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
return VersionPredicate::make_not_equal_to(Version::parse(str.substr(start)));
|
||||
return Version::parse(str.substr(start))
|
||||
.transform([](specs::Version&& ver)
|
||||
{ return VersionPredicate::make_not_equal_to(std::move(ver)); });
|
||||
}
|
||||
}
|
||||
if (util::starts_with(str, VersionSpec::starts_with_str))
|
||||
{
|
||||
const std::size_t start = VersionSpec::starts_with_str.size();
|
||||
// Glob suffix does not change meaning for =1.3.*
|
||||
return VersionPredicate::make_starts_with(
|
||||
Version::parse(str.substr(start, str.size() - glob_len - start))
|
||||
);
|
||||
return Version::parse(str.substr(start, str.size() - glob_len - start))
|
||||
.transform([](specs::Version&& ver)
|
||||
{ return VersionPredicate::make_starts_with(std::move(ver)); });
|
||||
}
|
||||
if (util::is_digit(str.front())) // All versions must start with a digit
|
||||
{
|
||||
|
@ -408,19 +417,25 @@ namespace mamba::specs
|
|||
// either ".*" or "*"
|
||||
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)));
|
||||
return Version::parse(str.substr(0, len))
|
||||
.transform([](specs::Version&& ver)
|
||||
{ return VersionPredicate::make_starts_with(std::move(ver)); });
|
||||
}
|
||||
else
|
||||
{
|
||||
return VersionPredicate::make_equal_to(Version::parse(str));
|
||||
return Version::parse(str).transform(
|
||||
[](specs::Version&& ver)
|
||||
{ return VersionPredicate::make_equal_to(std::move(ver)); }
|
||||
);
|
||||
}
|
||||
}
|
||||
throw std::invalid_argument(fmt::format(R"(Found invalid version predicate in "{}")", str)
|
||||
return tl::make_unexpected(
|
||||
ParseError(fmt::format(R"(Found invalid version predicate in "{}")", str))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
auto VersionSpec::parse(std::string_view str) -> VersionSpec
|
||||
auto VersionSpec::parse(std::string_view str) -> expected_parse_t<VersionSpec>
|
||||
{
|
||||
static constexpr auto all_tokens = std::array{
|
||||
VersionSpec::and_token,
|
||||
|
@ -447,40 +462,95 @@ namespace mamba::specs
|
|||
{
|
||||
if (str.front() == VersionSpec::and_token)
|
||||
{
|
||||
parser.push_operator(util::BoolOperator::logical_and);
|
||||
const bool pushed = parser.push_operator(util::BoolOperator::logical_and);
|
||||
if (!pushed)
|
||||
{
|
||||
return tl::make_unexpected(ParseError(fmt::format(
|
||||
R"(Found unexpected token "{}" in version spec "{}")",
|
||||
VersionSpec::and_token,
|
||||
str
|
||||
)));
|
||||
}
|
||||
str = str.substr(1);
|
||||
}
|
||||
else if (str.front() == VersionSpec::or_token)
|
||||
{
|
||||
parser.push_operator(util::BoolOperator::logical_or);
|
||||
const bool pushed = parser.push_operator(util::BoolOperator::logical_or);
|
||||
if (!pushed)
|
||||
{
|
||||
return tl::make_unexpected(ParseError(fmt::format(
|
||||
R"(Found unexpected token "{}" in version spec "{}")",
|
||||
VersionSpec::or_token,
|
||||
str
|
||||
)));
|
||||
}
|
||||
str = str.substr(1);
|
||||
}
|
||||
else if (str.front() == VersionSpec::left_parenthesis_token)
|
||||
{
|
||||
parser.push_left_parenthesis();
|
||||
const bool pushed = parser.push_left_parenthesis();
|
||||
if (!pushed)
|
||||
{
|
||||
return tl::make_unexpected(ParseError(fmt::format(
|
||||
R"(Found unexpected token "{}" in version spec "{}")",
|
||||
VersionSpec::left_parenthesis_token,
|
||||
str
|
||||
)));
|
||||
}
|
||||
str = util::lstrip(str.substr(1));
|
||||
}
|
||||
else if (str.front() == VersionSpec::right_parenthesis_token)
|
||||
{
|
||||
parser.push_right_parenthesis();
|
||||
const bool pushed = parser.push_right_parenthesis();
|
||||
if (!pushed)
|
||||
{
|
||||
return tl::make_unexpected(ParseError(fmt::format(
|
||||
R"(Found unexpected token "{}" in version spec "{}")",
|
||||
VersionSpec::right_parenthesis_token,
|
||||
str
|
||||
)));
|
||||
}
|
||||
str = util::lstrip(str.substr(1));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto [op_ver, rest] = util::lstrip_if_parts(str, [&](auto c) { return !is_token(c); });
|
||||
parser.push_variable(parse_op_and_version(op_ver));
|
||||
auto pred = parse_op_and_version(op_ver);
|
||||
if (!pred.has_value())
|
||||
{
|
||||
return tl::make_unexpected(pred.error());
|
||||
}
|
||||
const bool pushed = parser.push_variable(std::move(pred).value());
|
||||
if (!pushed)
|
||||
{
|
||||
return tl::make_unexpected(ParseError(fmt::format(
|
||||
R"(Found unexpected version predicate "{}" in version spec "{}")",
|
||||
pred.value(),
|
||||
str
|
||||
)));
|
||||
}
|
||||
str = rest;
|
||||
}
|
||||
}
|
||||
parser.finalize();
|
||||
return VersionSpec{ std::move(parser).tree() };
|
||||
|
||||
const bool correct = parser.finalize();
|
||||
if (!correct)
|
||||
{
|
||||
return tl::make_unexpected(
|
||||
ParseError(fmt::format(R"(Version spec "{}" is an incorrect expression)", str))
|
||||
);
|
||||
}
|
||||
|
||||
return { VersionSpec{ std::move(parser).tree() } };
|
||||
}
|
||||
|
||||
namespace version_spec_literals
|
||||
{
|
||||
auto operator""_vs(const char* str, std::size_t len) -> VersionSpec
|
||||
{
|
||||
return VersionSpec::parse(std::literals::string_view_literals::operator""sv(str, len));
|
||||
return VersionSpec::parse(std::literals::string_view_literals::operator""sv(str, len))
|
||||
.or_else([](ParseError&& error) { throw std::move(error); })
|
||||
.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
// The full license is in the file LICENSE, distributed with this software.
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
@ -37,7 +38,7 @@ namespace mamba::util
|
|||
*
|
||||
* Never null, throw exception at construction if creating the handle fails.
|
||||
*/
|
||||
class CURLUrl
|
||||
class CurlUrl
|
||||
{
|
||||
public:
|
||||
|
||||
|
@ -46,21 +47,22 @@ namespace mamba::util
|
|||
using const_pointer = const value_type*;
|
||||
using flag_type = unsigned int;
|
||||
|
||||
CURLUrl();
|
||||
CURLUrl(const std::string& url, flag_type flags = 0);
|
||||
~CURLUrl();
|
||||
static auto parse(const std::string& url, flag_type flags = 0)
|
||||
-> tl::expected<CurlUrl, URL::ParseError>;
|
||||
|
||||
CURLUrl(const CURLUrl&) = delete;
|
||||
CURLUrl& operator=(const CURLUrl&) = delete;
|
||||
CURLUrl(CURLUrl&&) = delete;
|
||||
CURLUrl& operator=(CURLUrl&&) = delete;
|
||||
CurlUrl();
|
||||
|
||||
[[nodiscard]] auto get_part(CURLUPart part, flag_type flags = 0) const
|
||||
[[nodiscard]] auto get_part(::CURLUPart part, flag_type flags = 0) const
|
||||
-> std::optional<std::string>;
|
||||
|
||||
private:
|
||||
|
||||
pointer m_handle = nullptr;
|
||||
struct CurlDeleter
|
||||
{
|
||||
void operator()(pointer ptr);
|
||||
};
|
||||
|
||||
std::unique_ptr<value_type, CurlDeleter> m_handle = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -68,7 +70,7 @@ namespace mamba::util
|
|||
*
|
||||
* String can possibly be null, or zero-lenght, depending on the data returned by CURL.
|
||||
*/
|
||||
class CURLStr
|
||||
class CurlStr
|
||||
{
|
||||
using value_type = char;
|
||||
using pointer = value_type*;
|
||||
|
@ -78,13 +80,13 @@ namespace mamba::util
|
|||
|
||||
public:
|
||||
|
||||
explicit CURLStr() = default;
|
||||
~CURLStr();
|
||||
explicit CurlStr() = default;
|
||||
~CurlStr();
|
||||
|
||||
CURLStr(const CURLStr&) = delete;
|
||||
CURLStr& operator=(const CURLStr&) = delete;
|
||||
CURLStr(CURLStr&&) = delete;
|
||||
CURLStr& operator=(CURLStr&&) = delete;
|
||||
CurlStr(const CurlStr&) = delete;
|
||||
auto operator=(const CurlStr&) -> CurlStr& = delete;
|
||||
CurlStr(CurlStr&&) = delete;
|
||||
auto operator=(CurlStr&&) -> CurlStr& = delete;
|
||||
|
||||
[[nodiscard]] auto raw_input() -> input_pointer;
|
||||
|
||||
|
@ -97,36 +99,40 @@ namespace mamba::util
|
|||
size_type m_len = { -1 };
|
||||
};
|
||||
|
||||
CURLUrl::CURLUrl()
|
||||
auto CurlUrl::parse(const std::string& url, flag_type flags)
|
||||
-> tl::expected<CurlUrl, URL::ParseError>
|
||||
{
|
||||
m_handle = ::curl_url();
|
||||
if (m_handle == nullptr)
|
||||
{
|
||||
throw std::runtime_error("Could not create CURLU handle");
|
||||
}
|
||||
}
|
||||
|
||||
CURLUrl::CURLUrl(const std::string& url, flag_type flags)
|
||||
: CURLUrl()
|
||||
{
|
||||
const CURLUcode uc = ::curl_url_set(m_handle, CURLUPART_URL, url.c_str(), flags);
|
||||
auto out = CurlUrl();
|
||||
const CURLUcode uc = ::curl_url_set(out.m_handle.get(), CURLUPART_URL, url.c_str(), flags);
|
||||
if (uc != CURLUE_OK)
|
||||
{
|
||||
throw std::invalid_argument(
|
||||
fmt::format(R"(Failed to parse URL "{}": {})", url, ::curl_url_strerror(uc))
|
||||
);
|
||||
return tl::make_unexpected(URL::ParseError{
|
||||
fmt::format(R"(Failed to parse URL "{}": {})", url, ::curl_url_strerror(uc)) });
|
||||
}
|
||||
return { std::move(out) };
|
||||
}
|
||||
|
||||
CurlUrl::CurlUrl()
|
||||
{
|
||||
m_handle.reset(::curl_url());
|
||||
if (m_handle == nullptr)
|
||||
{
|
||||
throw std::runtime_error("Could not create CurlUrl handle");
|
||||
}
|
||||
}
|
||||
|
||||
CURLUrl::~CURLUrl()
|
||||
void CurlUrl::CurlDeleter::operator()(pointer ptr)
|
||||
{
|
||||
::curl_url_cleanup(m_handle);
|
||||
if (ptr)
|
||||
{
|
||||
::curl_url_cleanup(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
auto CURLUrl::get_part(CURLUPart part, flag_type flags) const -> std::optional<std::string>
|
||||
auto CurlUrl::get_part(CURLUPart part, flag_type flags) const -> std::optional<std::string>
|
||||
{
|
||||
CURLStr scheme{};
|
||||
const auto rc = ::curl_url_get(m_handle, part, scheme.raw_input(), flags);
|
||||
CurlStr scheme{};
|
||||
const auto rc = ::curl_url_get(m_handle.get(), part, scheme.raw_input(), flags);
|
||||
if (!rc)
|
||||
{
|
||||
if (auto str = scheme.str())
|
||||
|
@ -137,7 +143,7 @@ namespace mamba::util
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
CURLStr::~CURLStr()
|
||||
CurlStr::~CurlStr()
|
||||
{
|
||||
// Even when Curl returns a len along side the data, `curl_free` must be used without
|
||||
// len.
|
||||
|
@ -145,13 +151,13 @@ namespace mamba::util
|
|||
m_data = nullptr;
|
||||
}
|
||||
|
||||
auto CURLStr::raw_input() -> input_pointer
|
||||
auto CurlStr::raw_input() -> input_pointer
|
||||
{
|
||||
assert(m_data == nullptr); // Otherwise we leak Curl memory
|
||||
return &m_data;
|
||||
}
|
||||
|
||||
auto CURLStr::str() const -> std::optional<std::string_view>
|
||||
auto CurlStr::str() const -> std::optional<std::string_view>
|
||||
{
|
||||
if (m_data)
|
||||
{
|
||||
|
@ -172,28 +178,31 @@ namespace mamba::util
|
|||
* URLHandler implementation *
|
||||
*****************************/
|
||||
|
||||
auto URL::parse(std::string_view url) -> URL
|
||||
auto URL::parse(std::string_view url) -> tl::expected<URL, ParseError>
|
||||
{
|
||||
url = util::rstrip(url);
|
||||
auto out = URL();
|
||||
if (!url.empty())
|
||||
if (url.empty())
|
||||
{
|
||||
// CURL fails to parse the URL if no scheme is given, unless CURLU_DEFAULT_SCHEME is
|
||||
// given
|
||||
const CURLUrl handle = {
|
||||
file_uri_unc2_to_unc4(url),
|
||||
CURLU_NON_SUPPORT_SCHEME | CURLU_DEFAULT_SCHEME,
|
||||
};
|
||||
out.set_scheme(handle.get_part(CURLUPART_SCHEME).value_or(""));
|
||||
out.set_user(handle.get_part(CURLUPART_USER).value_or(""), Encode::no);
|
||||
out.set_password(handle.get_part(CURLUPART_PASSWORD).value_or(""), Encode::no);
|
||||
out.set_host(handle.get_part(CURLUPART_HOST).value_or(""), Encode::no);
|
||||
out.set_path(handle.get_part(CURLUPART_PATH).value_or("/"), Encode::no);
|
||||
out.set_port(handle.get_part(CURLUPART_PORT).value_or(""));
|
||||
out.set_query(handle.get_part(CURLUPART_QUERY).value_or(""));
|
||||
out.set_fragment(handle.get_part(CURLUPART_FRAGMENT).value_or(""));
|
||||
return tl::make_unexpected(ParseError{ "Empty URL" });
|
||||
}
|
||||
return out;
|
||||
return CurlUrl::parse(file_uri_unc2_to_unc4(url), CURLU_NON_SUPPORT_SCHEME | CURLU_DEFAULT_SCHEME)
|
||||
.transform(
|
||||
[&](CurlUrl&& handle) -> URL
|
||||
{
|
||||
auto out = URL();
|
||||
// CURL fails to parse the URL if no scheme is given, unless
|
||||
// CURLU_DEFAULT_SCHEME is given
|
||||
out.set_scheme(handle.get_part(CURLUPART_SCHEME).value_or(""));
|
||||
out.set_user(handle.get_part(CURLUPART_USER).value_or(""), Encode::no);
|
||||
out.set_password(handle.get_part(CURLUPART_PASSWORD).value_or(""), Encode::no);
|
||||
out.set_host(handle.get_part(CURLUPART_HOST).value_or(""), Encode::no);
|
||||
out.set_path(handle.get_part(CURLUPART_PATH).value_or("/"), Encode::no);
|
||||
out.set_port(handle.get_part(CURLUPART_PORT).value_or(""));
|
||||
out.set_query(handle.get_part(CURLUPART_QUERY).value_or(""));
|
||||
out.set_fragment(handle.get_part(CURLUPART_FRAGMENT).value_or(""));
|
||||
return out;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
auto URL::scheme_is_defaulted() const -> bool
|
||||
|
|
|
@ -360,7 +360,7 @@ namespace mamba::validation::v0_6
|
|||
auto tmp_dir = TemporaryDirectory();
|
||||
auto tmp_metadata_path = tmp_dir.path() / "pkg_mgr.json";
|
||||
|
||||
const auto url = mamba::util::URL::parse(base_url + "/pkg_mgr.json");
|
||||
const auto url = mamba::util::URL::parse(base_url + "/pkg_mgr.json").value();
|
||||
|
||||
if (download::check_resource_exists(url.pretty_str(), context))
|
||||
{
|
||||
|
|
|
@ -119,24 +119,22 @@ TEST_SUITE("specs::build_number_spec")
|
|||
for (const auto& spec : bad_specs)
|
||||
{
|
||||
CAPTURE(spec);
|
||||
// Silence [[nodiscard]] warning
|
||||
auto parse = [](auto s) { return BuildNumberSpec::parse(s); };
|
||||
CHECK_THROWS_AS(parse(spec), std::invalid_argument);
|
||||
CHECK_FALSE(BuildNumberSpec::parse(spec).has_value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("BuildNumberSepc::str")
|
||||
{
|
||||
CHECK_EQ(BuildNumberSpec::parse("=3").str(), "=3");
|
||||
CHECK_EQ(BuildNumberSpec::parse("<2").str(), "<2");
|
||||
CHECK_EQ(BuildNumberSpec::parse("*").str(), "=*");
|
||||
CHECK_EQ(BuildNumberSpec::parse("=3").value().str(), "=3");
|
||||
CHECK_EQ(BuildNumberSpec::parse("<2").value().str(), "<2");
|
||||
CHECK_EQ(BuildNumberSpec::parse("*").value().str(), "=*");
|
||||
}
|
||||
|
||||
TEST_CASE("BuildNumberSepc::is_explicitly_free")
|
||||
{
|
||||
CHECK(BuildNumberSpec::parse("*").is_explicitly_free());
|
||||
CHECK_FALSE(BuildNumberSpec::parse("=3").is_explicitly_free());
|
||||
CHECK_FALSE(BuildNumberSpec::parse("<2").is_explicitly_free());
|
||||
CHECK(BuildNumberSpec::parse("*").value().is_explicitly_free());
|
||||
CHECK_FALSE(BuildNumberSpec::parse("=3").value().is_explicitly_free());
|
||||
CHECK_FALSE(BuildNumberSpec::parse("<2").value().is_explicitly_free());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -234,7 +234,7 @@ TEST_SUITE("specs::channel")
|
|||
auto make_typical_params = []() -> ChannelResolveParams
|
||||
{
|
||||
auto make_channel = [](std::string_view loc, ChannelResolveParams const& params)
|
||||
{ return Channel::resolve(UnresolvedChannel::parse(loc), params).at(0); };
|
||||
{ return Channel::resolve(UnresolvedChannel::parse(loc).value(), params).at(0); };
|
||||
|
||||
auto params = ChannelResolveParams{};
|
||||
params.platforms = { "linux-64", "noarch" };
|
||||
|
@ -325,7 +325,8 @@ TEST_SUITE("specs::channel")
|
|||
};
|
||||
params.custom_channels.emplace(
|
||||
"mychan",
|
||||
Channel::resolve(UnresolvedChannel::parse("file:///home/conda-bld/"), params).at(0)
|
||||
Channel::resolve(UnresolvedChannel::parse("file:///home/conda-bld/").value(), params)
|
||||
.at(0)
|
||||
);
|
||||
CHECK_EQ(Channel::resolve(uc, params).at(0).display_name(), "mychan");
|
||||
}
|
||||
|
@ -711,7 +712,8 @@ TEST_SUITE("specs::channel")
|
|||
auto params = make_typical_params();
|
||||
params.custom_channels.emplace(
|
||||
"conda-forge",
|
||||
Channel::resolve(UnresolvedChannel::parse("ftp://mydomain.net/conda"), params).at(0)
|
||||
Channel::resolve(UnresolvedChannel::parse("ftp://mydomain.net/conda").value(), params)
|
||||
.at(0)
|
||||
);
|
||||
|
||||
auto channels = Channel::resolve(uc, params);
|
||||
|
@ -783,7 +785,7 @@ TEST_SUITE("specs::channel")
|
|||
params.custom_channels.emplace(
|
||||
"testchannel",
|
||||
Channel::resolve(
|
||||
UnresolvedChannel::parse("https://server.com/private/testchannel"),
|
||||
UnresolvedChannel::parse("https://server.com/private/testchannel").value(),
|
||||
params
|
||||
)
|
||||
.at(0)
|
||||
|
@ -812,7 +814,8 @@ TEST_SUITE("specs::channel")
|
|||
};
|
||||
params.custom_channels.emplace(
|
||||
"prefix",
|
||||
Channel::resolve(UnresolvedChannel::parse("https://server.com/prefix"), params).at(0)
|
||||
Channel::resolve(UnresolvedChannel::parse("https://server.com/prefix").value(), params)
|
||||
.at(0)
|
||||
);
|
||||
|
||||
auto channels = Channel::resolve(uc, params);
|
||||
|
|
|
@ -21,7 +21,7 @@ TEST_SUITE("specs::repo_data")
|
|||
{
|
||||
auto p = RepoDataPackage();
|
||||
p.name = "mamba";
|
||||
p.version = Version::parse("1.0.0");
|
||||
p.version = Version::parse("1.0.0").value();
|
||||
p.build_string = "bld";
|
||||
p.build_number = 3;
|
||||
p.subdir = "linux";
|
||||
|
|
|
@ -38,21 +38,30 @@ TEST_SUITE("specs::unresolved_channel")
|
|||
|
||||
TEST_CASE("Parsing")
|
||||
{
|
||||
SUBCASE("Invalid channels")
|
||||
SUBCASE("Unknown channels")
|
||||
{
|
||||
for (std::string_view str : { "", "<unknown>", ":///<unknown>", "none" })
|
||||
{
|
||||
CAPTURE(str);
|
||||
const auto uc = UnresolvedChannel::parse(str);
|
||||
const auto uc = UnresolvedChannel::parse(str).value();
|
||||
CHECK_EQ(uc.type(), Type::Unknown);
|
||||
CHECK_EQ(uc.location(), "<unknown>");
|
||||
CHECK_EQ(uc.platform_filters(), PlatformSet{});
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("Invalid channels")
|
||||
{
|
||||
for (std::string_view str : { "forgelinux-64]" })
|
||||
{
|
||||
CAPTURE(str);
|
||||
CHECK_FALSE(UnresolvedChannel::parse(str).has_value());
|
||||
}
|
||||
}
|
||||
|
||||
SUBCASE("https://repo.anaconda.com/conda-forge")
|
||||
{
|
||||
const auto uc = UnresolvedChannel::parse("https://repo.anaconda.com/conda-forge");
|
||||
const auto uc = UnresolvedChannel::parse("https://repo.anaconda.com/conda-forge").value();
|
||||
CHECK_EQ(uc.type(), Type::URL);
|
||||
CHECK_EQ(uc.location(), "https://repo.anaconda.com/conda-forge");
|
||||
CHECK_EQ(uc.platform_filters(), PlatformSet{});
|
||||
|
@ -60,7 +69,8 @@ TEST_SUITE("specs::unresolved_channel")
|
|||
|
||||
SUBCASE("https://repo.anaconda.com/conda-forge/osx-64")
|
||||
{
|
||||
const auto uc = UnresolvedChannel::parse("https://repo.anaconda.com/conda-forge/osx-64");
|
||||
const auto uc = UnresolvedChannel::parse("https://repo.anaconda.com/conda-forge/osx-64")
|
||||
.value();
|
||||
CHECK_EQ(uc.type(), Type::URL);
|
||||
CHECK_EQ(uc.location(), "https://repo.anaconda.com/conda-forge");
|
||||
CHECK_EQ(uc.platform_filters(), PlatformSet{ "osx-64" });
|
||||
|
@ -69,8 +79,9 @@ TEST_SUITE("specs::unresolved_channel")
|
|||
SUBCASE("https://repo.anaconda.com/conda-forge[win-64|noarch]")
|
||||
{
|
||||
const auto uc = UnresolvedChannel::parse(
|
||||
"https://repo.anaconda.com/conda-forge[win-64|noarch]"
|
||||
);
|
||||
"https://repo.anaconda.com/conda-forge[win-64|noarch]"
|
||||
)
|
||||
.value();
|
||||
CHECK_EQ(uc.type(), Type::URL);
|
||||
CHECK_EQ(uc.location(), "https://repo.anaconda.com/conda-forge");
|
||||
CHECK_EQ(uc.platform_filters(), PlatformSet{ "win-64", "noarch" });
|
||||
|
@ -79,8 +90,9 @@ TEST_SUITE("specs::unresolved_channel")
|
|||
SUBCASE("https://repo.anaconda.com/conda-forge/linux-64/pkg-0.0-bld.conda")
|
||||
{
|
||||
const auto uc = UnresolvedChannel::parse(
|
||||
"https://repo.anaconda.com/conda-forge/linux-64/pkg-0.0-bld.conda"
|
||||
);
|
||||
"https://repo.anaconda.com/conda-forge/linux-64/pkg-0.0-bld.conda"
|
||||
)
|
||||
.value();
|
||||
CHECK_EQ(uc.type(), Type::PackageURL);
|
||||
CHECK_EQ(uc.location(), "https://repo.anaconda.com/conda-forge/linux-64/pkg-0.0-bld.conda");
|
||||
CHECK_EQ(uc.platform_filters(), PlatformSet{});
|
||||
|
@ -88,7 +100,7 @@ TEST_SUITE("specs::unresolved_channel")
|
|||
|
||||
SUBCASE("file:///Users/name/conda")
|
||||
{
|
||||
const auto uc = UnresolvedChannel::parse("file:///Users/name/conda");
|
||||
const auto uc = UnresolvedChannel::parse("file:///Users/name/conda").value();
|
||||
CHECK_EQ(uc.type(), Type::Path);
|
||||
CHECK_EQ(uc.location(), "file:///Users/name/conda");
|
||||
CHECK_EQ(uc.platform_filters(), PlatformSet{});
|
||||
|
@ -96,7 +108,7 @@ TEST_SUITE("specs::unresolved_channel")
|
|||
|
||||
SUBCASE("file:///Users/name/conda[linux-64]")
|
||||
{
|
||||
const auto uc = UnresolvedChannel::parse("file:///Users/name/conda[linux-64]");
|
||||
const auto uc = UnresolvedChannel::parse("file:///Users/name/conda[linux-64]").value();
|
||||
CHECK_EQ(uc.type(), Type::Path);
|
||||
CHECK_EQ(uc.location(), "file:///Users/name/conda");
|
||||
CHECK_EQ(uc.platform_filters(), PlatformSet{ "linux-64" });
|
||||
|
@ -106,7 +118,7 @@ TEST_SUITE("specs::unresolved_channel")
|
|||
{
|
||||
if (util::on_win)
|
||||
{
|
||||
const auto uc = UnresolvedChannel::parse("file://C:/Users/name/conda");
|
||||
const auto uc = UnresolvedChannel::parse("file://C:/Users/name/conda").value();
|
||||
CHECK_EQ(uc.type(), Type::Path);
|
||||
CHECK_EQ(uc.location(), "file://C:/Users/name/conda");
|
||||
CHECK_EQ(uc.platform_filters(), PlatformSet{});
|
||||
|
@ -115,7 +127,7 @@ TEST_SUITE("specs::unresolved_channel")
|
|||
|
||||
SUBCASE("/Users/name/conda")
|
||||
{
|
||||
const auto uc = UnresolvedChannel::parse("/Users/name/conda");
|
||||
const auto uc = UnresolvedChannel::parse("/Users/name/conda").value();
|
||||
CHECK_EQ(uc.type(), Type::Path);
|
||||
CHECK_EQ(uc.location(), "/Users/name/conda");
|
||||
CHECK_EQ(uc.platform_filters(), PlatformSet{});
|
||||
|
@ -123,7 +135,7 @@ TEST_SUITE("specs::unresolved_channel")
|
|||
|
||||
SUBCASE("./folder/../folder/.")
|
||||
{
|
||||
const auto uc = UnresolvedChannel::parse("./folder/../folder/.");
|
||||
const auto uc = UnresolvedChannel::parse("./folder/../folder/.").value();
|
||||
CHECK_EQ(uc.type(), Type::Path);
|
||||
CHECK_EQ(uc.location(), "folder");
|
||||
CHECK_EQ(uc.platform_filters(), PlatformSet{});
|
||||
|
@ -131,7 +143,7 @@ TEST_SUITE("specs::unresolved_channel")
|
|||
|
||||
SUBCASE("~/folder/")
|
||||
{
|
||||
const auto uc = UnresolvedChannel::parse("~/folder/");
|
||||
const auto uc = UnresolvedChannel::parse("~/folder/").value();
|
||||
CHECK_EQ(uc.type(), Type::Path);
|
||||
CHECK_EQ(uc.location(), "~/folder");
|
||||
CHECK_EQ(uc.platform_filters(), PlatformSet{});
|
||||
|
@ -139,7 +151,7 @@ TEST_SUITE("specs::unresolved_channel")
|
|||
|
||||
SUBCASE("/tmp/pkg-0.0-bld.tar.bz2")
|
||||
{
|
||||
const auto uc = UnresolvedChannel::parse("/tmp/pkg-0.0-bld.tar.bz2");
|
||||
const auto uc = UnresolvedChannel::parse("/tmp/pkg-0.0-bld.tar.bz2").value();
|
||||
CHECK_EQ(uc.type(), Type::PackagePath);
|
||||
CHECK_EQ(uc.location(), "/tmp/pkg-0.0-bld.tar.bz2");
|
||||
CHECK_EQ(uc.platform_filters(), PlatformSet{});
|
||||
|
@ -147,7 +159,7 @@ TEST_SUITE("specs::unresolved_channel")
|
|||
|
||||
SUBCASE("C:/tmp//pkg-0.0-bld.tar.bz2")
|
||||
{
|
||||
const auto uc = UnresolvedChannel::parse("C:/tmp//pkg-0.0-bld.tar.bz2");
|
||||
const auto uc = UnresolvedChannel::parse("C:/tmp//pkg-0.0-bld.tar.bz2").value();
|
||||
CHECK_EQ(uc.type(), Type::PackagePath);
|
||||
CHECK_EQ(uc.location(), "C:/tmp/pkg-0.0-bld.tar.bz2");
|
||||
CHECK_EQ(uc.platform_filters(), PlatformSet{});
|
||||
|
@ -157,7 +169,7 @@ TEST_SUITE("specs::unresolved_channel")
|
|||
{
|
||||
if (util::on_win)
|
||||
{
|
||||
const auto uc = UnresolvedChannel::parse(R"(C:\tmp\pkg-0.0-bld.tar.bz2)");
|
||||
const auto uc = UnresolvedChannel::parse(R"(C:\tmp\pkg-0.0-bld.tar.bz2)").value();
|
||||
CHECK_EQ(uc.type(), Type::PackagePath);
|
||||
CHECK_EQ(uc.location(), "C:/tmp/pkg-0.0-bld.tar.bz2");
|
||||
CHECK_EQ(uc.platform_filters(), PlatformSet{});
|
||||
|
@ -166,7 +178,7 @@ TEST_SUITE("specs::unresolved_channel")
|
|||
|
||||
SUBCASE("conda-forge")
|
||||
{
|
||||
const auto uc = UnresolvedChannel::parse("conda-forge");
|
||||
const auto uc = UnresolvedChannel::parse("conda-forge").value();
|
||||
CHECK_EQ(uc.type(), Type::Name);
|
||||
CHECK_EQ(uc.location(), "conda-forge");
|
||||
CHECK_EQ(uc.platform_filters(), PlatformSet{});
|
||||
|
@ -174,7 +186,7 @@ TEST_SUITE("specs::unresolved_channel")
|
|||
|
||||
SUBCASE("repo.anaconda.com")
|
||||
{
|
||||
const auto uc = UnresolvedChannel::parse("repo.anaconda.com");
|
||||
const auto uc = UnresolvedChannel::parse("repo.anaconda.com").value();
|
||||
// Unintuitive but correct type, this is not a URL. Better explicit than clever.
|
||||
CHECK_EQ(uc.type(), Type::Name);
|
||||
CHECK_EQ(uc.location(), "repo.anaconda.com");
|
||||
|
@ -183,7 +195,7 @@ TEST_SUITE("specs::unresolved_channel")
|
|||
|
||||
SUBCASE("conda-forge/linux-64")
|
||||
{
|
||||
const auto uc = UnresolvedChannel::parse("conda-forge/linux-64");
|
||||
const auto uc = UnresolvedChannel::parse("conda-forge/linux-64").value();
|
||||
CHECK_EQ(uc.type(), Type::Name);
|
||||
CHECK_EQ(uc.location(), "conda-forge");
|
||||
CHECK_EQ(uc.platform_filters(), PlatformSet{ "linux-64" });
|
||||
|
@ -191,7 +203,7 @@ TEST_SUITE("specs::unresolved_channel")
|
|||
|
||||
SUBCASE("conda-forge[linux-avx512]")
|
||||
{
|
||||
const auto uc = UnresolvedChannel::parse("conda-forge[linux-avx512]");
|
||||
const auto uc = UnresolvedChannel::parse("conda-forge[linux-avx512]").value();
|
||||
CHECK_EQ(uc.type(), Type::Name);
|
||||
CHECK_EQ(uc.location(), "conda-forge");
|
||||
CHECK_EQ(uc.platform_filters(), PlatformSet{ "linux-avx512" });
|
||||
|
@ -199,7 +211,7 @@ TEST_SUITE("specs::unresolved_channel")
|
|||
|
||||
SUBCASE("conda-forge[]")
|
||||
{
|
||||
const auto uc = UnresolvedChannel::parse("conda-forge[linux-64]");
|
||||
const auto uc = UnresolvedChannel::parse("conda-forge[linux-64]").value();
|
||||
CHECK_EQ(uc.type(), Type::Name);
|
||||
CHECK_EQ(uc.location(), "conda-forge");
|
||||
CHECK_EQ(uc.platform_filters(), PlatformSet{ "linux-64" });
|
||||
|
@ -207,7 +219,7 @@ TEST_SUITE("specs::unresolved_channel")
|
|||
|
||||
SUBCASE("conda-forge/linux-64/label/foo_dev")
|
||||
{
|
||||
const auto uc = UnresolvedChannel::parse("conda-forge/linux-64/label/foo_dev");
|
||||
const auto uc = UnresolvedChannel::parse("conda-forge/linux-64/label/foo_dev").value();
|
||||
CHECK_EQ(uc.type(), Type::Name);
|
||||
CHECK_EQ(uc.location(), "conda-forge/label/foo_dev");
|
||||
CHECK_EQ(uc.platform_filters(), PlatformSet{ "linux-64" });
|
||||
|
@ -215,7 +227,7 @@ TEST_SUITE("specs::unresolved_channel")
|
|||
|
||||
SUBCASE("conda-forge/label/foo_dev[linux-64]")
|
||||
{
|
||||
const auto uc = UnresolvedChannel::parse("conda-forge/label/foo_dev[linux-64]");
|
||||
const auto uc = UnresolvedChannel::parse("conda-forge/label/foo_dev[linux-64]").value();
|
||||
CHECK_EQ(uc.type(), Type::Name);
|
||||
CHECK_EQ(uc.location(), "conda-forge/label/foo_dev");
|
||||
CHECK_EQ(uc.platform_filters(), PlatformSet{ "linux-64" });
|
||||
|
|
|
@ -418,51 +418,51 @@ TEST_SUITE("specs::version")
|
|||
));
|
||||
|
||||
// Default constructed
|
||||
CHECK_EQ(Version::parse("0.0"), Version());
|
||||
CHECK_EQ(Version::parse("0.0").value(), Version());
|
||||
|
||||
// Lowercase and strip
|
||||
CHECK_EQ(Version::parse("0.4.1.rc"), Version::parse(" 0.4.1.RC "));
|
||||
CHECK_EQ(Version::parse(" 0.4.1.RC "), Version::parse("0.4.1.rc"));
|
||||
CHECK_EQ(Version::parse("0.4.1.rc").value(), Version::parse(" 0.4.1.RC "));
|
||||
CHECK_EQ(Version::parse(" 0.4.1.RC ").value(), Version::parse("0.4.1.rc"));
|
||||
|
||||
// Functional assertions
|
||||
CHECK_EQ(Version::parse(" 0.4.rc "), Version::parse("0.4.RC"));
|
||||
CHECK_EQ(Version::parse("0.4"), Version::parse("0.4.0"));
|
||||
CHECK_NE(Version::parse("0.4"), Version::parse("0.4.1"));
|
||||
CHECK_EQ(Version::parse("0.4.a1"), Version::parse("0.4.0a1"));
|
||||
CHECK_NE(Version::parse("0.4.a1"), Version::parse("0.4.1a1"));
|
||||
CHECK_EQ(Version::parse(" 0.4.rc ").value(), Version::parse("0.4.RC"));
|
||||
CHECK_EQ(Version::parse("0.4").value(), Version::parse("0.4.0"));
|
||||
CHECK_NE(Version::parse("0.4").value(), Version::parse("0.4.1"));
|
||||
CHECK_EQ(Version::parse("0.4.a1").value(), Version::parse("0.4.0a1"));
|
||||
CHECK_NE(Version::parse("0.4.a1").value(), Version::parse("0.4.1a1"));
|
||||
}
|
||||
|
||||
TEST_CASE("parse_invalid")
|
||||
{
|
||||
// Wrong epoch
|
||||
CHECK_THROWS_AS(Version::parse("!1.1"), std::invalid_argument);
|
||||
CHECK_THROWS_AS(Version::parse("-1!1.1"), std::invalid_argument);
|
||||
CHECK_THROWS_AS(Version::parse("foo!1.1"), std::invalid_argument);
|
||||
CHECK_THROWS_AS(Version::parse("0post1!1.1"), std::invalid_argument);
|
||||
CHECK_FALSE(Version::parse("!1.1").has_value());
|
||||
CHECK_FALSE(Version::parse("-1!1.1").has_value());
|
||||
CHECK_FALSE(Version::parse("foo!1.1").has_value());
|
||||
CHECK_FALSE(Version::parse("0post1!1.1").has_value());
|
||||
|
||||
// Empty parts
|
||||
CHECK_THROWS_AS(Version::parse(""), std::invalid_argument);
|
||||
CHECK_THROWS_AS(Version::parse(" "), std::invalid_argument);
|
||||
CHECK_THROWS_AS(Version::parse("!2.2"), std::invalid_argument);
|
||||
CHECK_THROWS_AS(Version::parse("0!"), std::invalid_argument);
|
||||
CHECK_THROWS_AS(Version::parse("!"), std::invalid_argument);
|
||||
CHECK_THROWS_AS(Version::parse("1."), std::invalid_argument);
|
||||
CHECK_THROWS_AS(Version::parse("1..1"), std::invalid_argument);
|
||||
CHECK_THROWS_AS(Version::parse("5.5..mw"), std::invalid_argument);
|
||||
CHECK_THROWS_AS(Version::parse("1.2post+"), std::invalid_argument);
|
||||
CHECK_THROWS_AS(Version::parse("1!+1.1"), std::invalid_argument);
|
||||
CHECK_FALSE(Version::parse("").has_value());
|
||||
CHECK_FALSE(Version::parse(" ").has_value());
|
||||
CHECK_FALSE(Version::parse("!2.2").has_value());
|
||||
CHECK_FALSE(Version::parse("0!").has_value());
|
||||
CHECK_FALSE(Version::parse("!").has_value());
|
||||
CHECK_FALSE(Version::parse("1.").has_value());
|
||||
CHECK_FALSE(Version::parse("1..1").has_value());
|
||||
CHECK_FALSE(Version::parse("5.5..mw").has_value());
|
||||
CHECK_FALSE(Version::parse("1.2post+").has_value());
|
||||
CHECK_FALSE(Version::parse("1!+1.1").has_value());
|
||||
|
||||
// Repeated delimiters
|
||||
CHECK_THROWS_AS(Version::parse("5.5++"), std::invalid_argument);
|
||||
CHECK_THROWS_AS(Version::parse("5.5+1+0.0"), std::invalid_argument);
|
||||
CHECK_THROWS_AS(Version::parse("1!2!3.0"), std::invalid_argument);
|
||||
CHECK_FALSE(Version::parse("5.5++").has_value());
|
||||
CHECK_FALSE(Version::parse("5.5+1+0.0").has_value());
|
||||
CHECK_FALSE(Version::parse("1!2!3.0").has_value());
|
||||
|
||||
// '-' and '_' delimiters not allowed together.
|
||||
CHECK_THROWS_AS(Version::parse("1-1_1"), std::invalid_argument);
|
||||
CHECK_FALSE(Version::parse("1-1_1").has_value());
|
||||
|
||||
// Forbidden characters
|
||||
CHECK_THROWS_AS(Version::parse("3.5&1"), std::invalid_argument);
|
||||
CHECK_THROWS_AS(Version::parse("3.5|1"), std::invalid_argument);
|
||||
CHECK_FALSE(Version::parse("3.5&1").has_value());
|
||||
CHECK_FALSE(Version::parse("3.5|1").has_value());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -485,23 +485,23 @@ TEST_SUITE("specs::version")
|
|||
{
|
||||
// clang-format off
|
||||
auto versions = std::vector{
|
||||
Version::parse("1.0.1dev"),
|
||||
Version::parse("1.0.1_"), // <- this
|
||||
Version::parse("1.0.1a"),
|
||||
Version::parse("1.0.1b"),
|
||||
Version::parse("1.0.1c"),
|
||||
Version::parse("1.0.1d"),
|
||||
Version::parse("1.0.1r"),
|
||||
Version::parse("1.0.1rc"),
|
||||
Version::parse("1.0.1rc1"),
|
||||
Version::parse("1.0.1rc2"),
|
||||
Version::parse("1.0.1s"),
|
||||
Version::parse("1.0.1"), // <- compared to this
|
||||
Version::parse("1.0.1post.a"),
|
||||
Version::parse("1.0.1post.b"),
|
||||
Version::parse("1.0.1post.z"),
|
||||
Version::parse("1.0.1post.za"),
|
||||
Version::parse("1.0.2"),
|
||||
Version::parse("1.0.1dev").value(),
|
||||
Version::parse("1.0.1_").value(), // <- this
|
||||
Version::parse("1.0.1a").value(),
|
||||
Version::parse("1.0.1b").value(),
|
||||
Version::parse("1.0.1c").value(),
|
||||
Version::parse("1.0.1d").value(),
|
||||
Version::parse("1.0.1r").value(),
|
||||
Version::parse("1.0.1rc").value(),
|
||||
Version::parse("1.0.1rc1").value(),
|
||||
Version::parse("1.0.1rc2").value(),
|
||||
Version::parse("1.0.1s").value(),
|
||||
Version::parse("1.0.1").value(), // <- compared to this
|
||||
Version::parse("1.0.1post.a").value(),
|
||||
Version::parse("1.0.1post.b").value(),
|
||||
Version::parse("1.0.1post.z").value(),
|
||||
Version::parse("1.0.1post.za").value(),
|
||||
Version::parse("1.0.2").value(),
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
@ -521,59 +521,59 @@ TEST_SUITE("specs::version")
|
|||
{
|
||||
auto versions = std::vector{
|
||||
// Implicit epoch of 0
|
||||
Version::parse("1.0a1"),
|
||||
Version::parse("1.0a2.dev456"),
|
||||
Version::parse("1.0a12.dev456"),
|
||||
Version::parse("1.0a12"),
|
||||
Version::parse("1.0b1.dev456"),
|
||||
Version::parse("1.0b2"),
|
||||
Version::parse("1.0b2.post345.dev456"),
|
||||
Version::parse("1.0b2.post345"),
|
||||
Version::parse("1.0c1.dev456"),
|
||||
Version::parse("1.0c1"),
|
||||
Version::parse("1.0c3"),
|
||||
Version::parse("1.0rc2"),
|
||||
Version::parse("1.0.dev456"),
|
||||
Version::parse("1.0"),
|
||||
Version::parse("1.0.post456.dev34"),
|
||||
Version::parse("1.0.post456"),
|
||||
Version::parse("1.1.dev1"),
|
||||
Version::parse("1.2.r32+123456"),
|
||||
Version::parse("1.2.rev33+123456"),
|
||||
Version::parse("1.2+abc"),
|
||||
Version::parse("1.2+abc123def"),
|
||||
Version::parse("1.2+abc123"),
|
||||
Version::parse("1.2+123abc"),
|
||||
Version::parse("1.2+123abc456"),
|
||||
Version::parse("1.2+1234.abc"),
|
||||
Version::parse("1.2+123456"),
|
||||
Version::parse("1.0a1").value(),
|
||||
Version::parse("1.0a2.dev456").value(),
|
||||
Version::parse("1.0a12.dev456").value(),
|
||||
Version::parse("1.0a12").value(),
|
||||
Version::parse("1.0b1.dev456").value(),
|
||||
Version::parse("1.0b2").value(),
|
||||
Version::parse("1.0b2.post345.dev456").value(),
|
||||
Version::parse("1.0b2.post345").value(),
|
||||
Version::parse("1.0c1.dev456").value(),
|
||||
Version::parse("1.0c1").value(),
|
||||
Version::parse("1.0c3").value(),
|
||||
Version::parse("1.0rc2").value(),
|
||||
Version::parse("1.0.dev456").value(),
|
||||
Version::parse("1.0").value(),
|
||||
Version::parse("1.0.post456.dev34").value(),
|
||||
Version::parse("1.0.post456").value(),
|
||||
Version::parse("1.1.dev1").value(),
|
||||
Version::parse("1.2.r32+123456").value(),
|
||||
Version::parse("1.2.rev33+123456").value(),
|
||||
Version::parse("1.2+abc").value(),
|
||||
Version::parse("1.2+abc123def").value(),
|
||||
Version::parse("1.2+abc123").value(),
|
||||
Version::parse("1.2+123abc").value(),
|
||||
Version::parse("1.2+123abc456").value(),
|
||||
Version::parse("1.2+1234.abc").value(),
|
||||
Version::parse("1.2+123456").value(),
|
||||
// Explicit epoch of 1
|
||||
Version::parse("1!1.0a1"),
|
||||
Version::parse("1!1.0a2.dev456"),
|
||||
Version::parse("1!1.0a12.dev456"),
|
||||
Version::parse("1!1.0a12"),
|
||||
Version::parse("1!1.0b1.dev456"),
|
||||
Version::parse("1!1.0b2"),
|
||||
Version::parse("1!1.0b2.post345.dev456"),
|
||||
Version::parse("1!1.0b2.post345"),
|
||||
Version::parse("1!1.0c1.dev456"),
|
||||
Version::parse("1!1.0c1"),
|
||||
Version::parse("1!1.0c3"),
|
||||
Version::parse("1!1.0rc2"),
|
||||
Version::parse("1!1.0.dev456"),
|
||||
Version::parse("1!1.0"),
|
||||
Version::parse("1!1.0.post456.dev34"),
|
||||
Version::parse("1!1.0.post456"),
|
||||
Version::parse("1!1.1.dev1"),
|
||||
Version::parse("1!1.2.r32+123456"),
|
||||
Version::parse("1!1.2.rev33+123456"),
|
||||
Version::parse("1!1.2+abc"),
|
||||
Version::parse("1!1.2+abc123def"),
|
||||
Version::parse("1!1.2+abc123"),
|
||||
Version::parse("1!1.2+123abc"),
|
||||
Version::parse("1!1.2+123abc456"),
|
||||
Version::parse("1!1.2+1234.abc"),
|
||||
Version::parse("1!1.2+123456"),
|
||||
Version::parse("1!1.0a1").value(),
|
||||
Version::parse("1!1.0a2.dev456").value(),
|
||||
Version::parse("1!1.0a12.dev456").value(),
|
||||
Version::parse("1!1.0a12").value(),
|
||||
Version::parse("1!1.0b1.dev456").value(),
|
||||
Version::parse("1!1.0b2").value(),
|
||||
Version::parse("1!1.0b2.post345.dev456").value(),
|
||||
Version::parse("1!1.0b2.post345").value(),
|
||||
Version::parse("1!1.0c1.dev456").value(),
|
||||
Version::parse("1!1.0c1").value(),
|
||||
Version::parse("1!1.0c3").value(),
|
||||
Version::parse("1!1.0rc2").value(),
|
||||
Version::parse("1!1.0.dev456").value(),
|
||||
Version::parse("1!1.0").value(),
|
||||
Version::parse("1!1.0.post456.dev34").value(),
|
||||
Version::parse("1!1.0.post456").value(),
|
||||
Version::parse("1!1.1.dev1").value(),
|
||||
Version::parse("1!1.2.r32+123456").value(),
|
||||
Version::parse("1!1.2.rev33+123456").value(),
|
||||
Version::parse("1!1.2+abc").value(),
|
||||
Version::parse("1!1.2+abc123def").value(),
|
||||
Version::parse("1!1.2+abc123").value(),
|
||||
Version::parse("1!1.2+123abc").value(),
|
||||
Version::parse("1!1.2+123abc456").value(),
|
||||
Version::parse("1!1.2+1234.abc").value(),
|
||||
Version::parse("1!1.2+123456").value(),
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
// The full license is in the file LICENSE, distributed with this software.
|
||||
|
||||
#include <array>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
|
||||
#include <doctest/doctest.h>
|
||||
|
@ -16,13 +15,16 @@ using namespace mamba::specs;
|
|||
|
||||
TEST_SUITE("specs::version_spec")
|
||||
{
|
||||
using namespace mamba::specs::version_literals;
|
||||
using namespace mamba::specs::version_spec_literals;
|
||||
|
||||
TEST_CASE("VersionPredicate")
|
||||
{
|
||||
const auto v1 = Version::parse("1.0");
|
||||
const auto v2 = Version::parse("2.0");
|
||||
const auto v201 = Version::parse("2.0.1");
|
||||
const auto v3 = Version::parse("3.0");
|
||||
const auto v4 = Version::parse("4.0");
|
||||
const auto v1 = "1.0"_v;
|
||||
const auto v2 = "2.0"_v;
|
||||
const auto v201 = "2.0.1"_v;
|
||||
const auto v3 = "3.0"_v;
|
||||
const auto v4 = "4.0"_v;
|
||||
|
||||
const auto free = VersionPredicate::make_free();
|
||||
CHECK(free.contains(v1));
|
||||
|
@ -135,14 +137,14 @@ TEST_SUITE("specs::version_spec")
|
|||
const auto v28 = Version(0, { { { 2 } }, { { 8 } }, { { 0 } } });
|
||||
|
||||
auto parser = InfixParser<VersionPredicate, BoolOperator>{};
|
||||
parser.push_variable(VersionPredicate::make_less(v20));
|
||||
parser.push_operator(BoolOperator::logical_or);
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_variable(VersionPredicate::make_greater(v23));
|
||||
parser.push_operator(BoolOperator::logical_and);
|
||||
parser.push_variable(VersionPredicate::make_less_equal(v28));
|
||||
parser.push_right_parenthesis();
|
||||
parser.finalize();
|
||||
REQUIRE(parser.push_variable(VersionPredicate::make_less(v20)));
|
||||
REQUIRE(parser.push_operator(BoolOperator::logical_or));
|
||||
REQUIRE(parser.push_left_parenthesis());
|
||||
REQUIRE(parser.push_variable(VersionPredicate::make_greater(v23)));
|
||||
REQUIRE(parser.push_operator(BoolOperator::logical_and));
|
||||
REQUIRE(parser.push_variable(VersionPredicate::make_less_equal(v28)));
|
||||
REQUIRE(parser.push_right_parenthesis());
|
||||
REQUIRE(parser.finalize());
|
||||
|
||||
auto spec = VersionSpec(std::move(parser).tree());
|
||||
|
||||
|
@ -162,9 +164,6 @@ TEST_SUITE("specs::version_spec")
|
|||
|
||||
TEST_CASE("VersionSpec::parse")
|
||||
{
|
||||
using namespace mamba::specs::version_literals;
|
||||
using namespace mamba::specs::version_spec_literals;
|
||||
|
||||
SUBCASE("Successful")
|
||||
{
|
||||
CHECK(""_vs.contains("1.6"_v));
|
||||
|
@ -392,9 +391,7 @@ TEST_SUITE("specs::version_spec")
|
|||
for (const auto& spec : bad_specs)
|
||||
{
|
||||
CAPTURE(spec);
|
||||
// Silence [[nodiscard]] warning
|
||||
auto parse = [](auto s) { return VersionSpec::parse(s); };
|
||||
CHECK_THROWS_AS(parse(spec), std::invalid_argument);
|
||||
CHECK_FALSE(VersionSpec::parse(spec).has_value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -403,14 +400,14 @@ TEST_SUITE("specs::version_spec")
|
|||
{
|
||||
SUBCASE("2.3")
|
||||
{
|
||||
auto vs = VersionSpec::parse("2.3");
|
||||
auto vs = VersionSpec::parse("2.3").value();
|
||||
CHECK_EQ(vs.str(), "==2.3");
|
||||
CHECK_EQ(vs.str_conda_build(), "==2.3");
|
||||
}
|
||||
|
||||
SUBCASE("=2.3,<3.0")
|
||||
{
|
||||
auto vs = VersionSpec::parse("=2.3,<3.0");
|
||||
auto vs = VersionSpec::parse("=2.3,<3.0").value();
|
||||
CHECK_EQ(vs.str(), "=2.3,<3.0");
|
||||
CHECK_EQ(vs.str_conda_build(), "2.3.*,<3.0");
|
||||
}
|
||||
|
@ -422,18 +419,18 @@ TEST_SUITE("specs::version_spec")
|
|||
using namespace mamba::util;
|
||||
|
||||
auto parser = InfixParser<VersionPredicate, BoolOperator>{};
|
||||
parser.push_variable(VersionPredicate::make_free());
|
||||
parser.finalize();
|
||||
REQUIRE(parser.push_variable(VersionPredicate::make_free()));
|
||||
REQUIRE(parser.finalize());
|
||||
auto spec = VersionSpec(std::move(parser).tree());
|
||||
|
||||
CHECK(spec.is_explicitly_free());
|
||||
}
|
||||
|
||||
CHECK(VersionSpec().is_explicitly_free());
|
||||
CHECK(VersionSpec::parse("*").is_explicitly_free());
|
||||
CHECK(VersionSpec::parse("").is_explicitly_free());
|
||||
CHECK(VersionSpec::parse("*").value().is_explicitly_free());
|
||||
CHECK(VersionSpec::parse("").value().is_explicitly_free());
|
||||
|
||||
CHECK_FALSE(VersionSpec::parse("==2.3|!=2.3").is_explicitly_free());
|
||||
CHECK_FALSE(VersionSpec::parse("=2.3,<3.0").is_explicitly_free());
|
||||
CHECK_FALSE(VersionSpec::parse("==2.3|!=2.3").value().is_explicitly_free());
|
||||
CHECK_FALSE(VersionSpec::parse("=2.3,<3.0").value().is_explicitly_free());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,15 +109,15 @@ TEST_SUITE("util::flat_bool_expr_tree")
|
|||
|
||||
SUBCASE("empty")
|
||||
{
|
||||
parser.finalize();
|
||||
CHECK(parser.finalize());
|
||||
const auto& tree = parser.tree();
|
||||
CHECK(tree.empty());
|
||||
}
|
||||
|
||||
SUBCASE("a")
|
||||
{
|
||||
parser.push_variable('a');
|
||||
parser.finalize();
|
||||
CHECK(parser.push_variable('a'));
|
||||
CHECK(parser.finalize());
|
||||
|
||||
const auto& tree = parser.tree();
|
||||
CHECK_EQ(tree.size(), 1);
|
||||
|
@ -129,16 +129,16 @@ TEST_SUITE("util::flat_bool_expr_tree")
|
|||
SUBCASE("a b + c d e + * *")
|
||||
{
|
||||
// Infix: (a + b) * (c * (d + e))
|
||||
parser.push_variable('a');
|
||||
parser.push_variable('b');
|
||||
parser.push_operator("+");
|
||||
parser.push_variable('c');
|
||||
parser.push_variable('d');
|
||||
parser.push_variable('e');
|
||||
parser.push_operator("+");
|
||||
parser.push_operator("*");
|
||||
parser.push_operator("*");
|
||||
parser.finalize();
|
||||
CHECK(parser.push_variable('a'));
|
||||
CHECK(parser.push_variable('b'));
|
||||
CHECK(parser.push_operator("+"));
|
||||
CHECK(parser.push_variable('c'));
|
||||
CHECK(parser.push_variable('d'));
|
||||
CHECK(parser.push_variable('e'));
|
||||
CHECK(parser.push_operator("+"));
|
||||
CHECK(parser.push_operator("*"));
|
||||
CHECK(parser.push_operator("*"));
|
||||
CHECK(parser.finalize());
|
||||
|
||||
const auto& tree = parser.tree();
|
||||
CHECK_EQ(tree.size(), 9);
|
||||
|
@ -149,49 +149,31 @@ TEST_SUITE("util::flat_bool_expr_tree")
|
|||
|
||||
SUBCASE("a b")
|
||||
{
|
||||
auto bad_parse = [&]()
|
||||
{
|
||||
parser.push_variable('a');
|
||||
parser.push_variable('b');
|
||||
parser.finalize();
|
||||
};
|
||||
CHECK_THROWS_AS(bad_parse(), std::invalid_argument);
|
||||
CHECK(parser.push_variable('a'));
|
||||
CHECK(parser.push_variable('b'));
|
||||
CHECK_FALSE(parser.finalize());
|
||||
}
|
||||
|
||||
SUBCASE("+")
|
||||
{
|
||||
auto bad_parse = [&]()
|
||||
{
|
||||
parser.push_operator("+");
|
||||
parser.finalize();
|
||||
};
|
||||
CHECK_THROWS_AS(bad_parse(), std::invalid_argument);
|
||||
CHECK_FALSE(parser.push_operator("+"));
|
||||
}
|
||||
|
||||
SUBCASE("a b + *")
|
||||
{
|
||||
auto bad_parse = [&]()
|
||||
{
|
||||
parser.push_variable('a');
|
||||
parser.push_variable('b');
|
||||
parser.push_operator("+");
|
||||
parser.push_operator("*");
|
||||
parser.finalize();
|
||||
};
|
||||
CHECK_THROWS_AS(bad_parse(), std::invalid_argument);
|
||||
CHECK(parser.push_variable('a'));
|
||||
CHECK(parser.push_variable('b'));
|
||||
CHECK(parser.push_operator("+"));
|
||||
CHECK_FALSE(parser.push_operator("*"));
|
||||
}
|
||||
|
||||
SUBCASE("a b + c")
|
||||
{
|
||||
auto bad_parse = [&]()
|
||||
{
|
||||
parser.push_variable('a');
|
||||
parser.push_variable('b');
|
||||
parser.push_operator("+");
|
||||
parser.push_variable('c');
|
||||
parser.finalize();
|
||||
};
|
||||
CHECK_THROWS_AS(bad_parse(), std::invalid_argument);
|
||||
CHECK(parser.push_variable('a'));
|
||||
CHECK(parser.push_variable('b'));
|
||||
CHECK(parser.push_operator("+"));
|
||||
CHECK(parser.push_variable('c'));
|
||||
CHECK_FALSE(parser.finalize());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,21 +183,21 @@ TEST_SUITE("util::flat_bool_expr_tree")
|
|||
|
||||
SUBCASE("empty")
|
||||
{
|
||||
parser.finalize();
|
||||
CHECK(parser.finalize());
|
||||
const auto& tree = parser.tree();
|
||||
CHECK(tree.empty());
|
||||
}
|
||||
|
||||
SUBCASE("(((a)))")
|
||||
{
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_variable('a');
|
||||
parser.push_right_parenthesis();
|
||||
parser.push_right_parenthesis();
|
||||
parser.push_right_parenthesis();
|
||||
parser.finalize();
|
||||
CHECK(parser.push_left_parenthesis());
|
||||
CHECK(parser.push_left_parenthesis());
|
||||
CHECK(parser.push_left_parenthesis());
|
||||
CHECK(parser.push_variable('a'));
|
||||
CHECK(parser.push_right_parenthesis());
|
||||
CHECK(parser.push_right_parenthesis());
|
||||
CHECK(parser.push_right_parenthesis());
|
||||
CHECK(parser.finalize());
|
||||
|
||||
const auto& tree = parser.tree();
|
||||
REQUIRE_EQ(tree.size(), 1);
|
||||
|
@ -226,16 +208,16 @@ TEST_SUITE("util::flat_bool_expr_tree")
|
|||
|
||||
SUBCASE("(((a)) + b)")
|
||||
{
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_variable('a');
|
||||
parser.push_right_parenthesis();
|
||||
parser.push_right_parenthesis();
|
||||
parser.push_operator("+");
|
||||
parser.push_variable('b');
|
||||
parser.push_right_parenthesis();
|
||||
parser.finalize();
|
||||
CHECK(parser.push_left_parenthesis());
|
||||
CHECK(parser.push_left_parenthesis());
|
||||
CHECK(parser.push_left_parenthesis());
|
||||
CHECK(parser.push_variable('a'));
|
||||
CHECK(parser.push_right_parenthesis());
|
||||
CHECK(parser.push_right_parenthesis());
|
||||
CHECK(parser.push_operator("+"));
|
||||
CHECK(parser.push_variable('b'));
|
||||
CHECK(parser.push_right_parenthesis());
|
||||
CHECK(parser.finalize());
|
||||
|
||||
const auto& tree = parser.tree();
|
||||
REQUIRE_EQ(tree.size(), 3);
|
||||
|
@ -250,22 +232,22 @@ TEST_SUITE("util::flat_bool_expr_tree")
|
|||
|
||||
SUBCASE("(a + b) * (c * (d + e))")
|
||||
{
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_variable('a');
|
||||
parser.push_operator("+");
|
||||
parser.push_variable('b');
|
||||
parser.push_right_parenthesis();
|
||||
parser.push_operator("*");
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_variable('c');
|
||||
parser.push_operator("*");
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_variable('d');
|
||||
parser.push_operator("+");
|
||||
parser.push_variable('e');
|
||||
parser.push_right_parenthesis();
|
||||
parser.push_right_parenthesis();
|
||||
parser.finalize();
|
||||
CHECK(parser.push_left_parenthesis());
|
||||
CHECK(parser.push_variable('a'));
|
||||
CHECK(parser.push_operator("+"));
|
||||
CHECK(parser.push_variable('b'));
|
||||
CHECK(parser.push_right_parenthesis());
|
||||
CHECK(parser.push_operator("*"));
|
||||
CHECK(parser.push_left_parenthesis());
|
||||
CHECK(parser.push_variable('c'));
|
||||
CHECK(parser.push_operator("*"));
|
||||
CHECK(parser.push_left_parenthesis());
|
||||
CHECK(parser.push_variable('d'));
|
||||
CHECK(parser.push_operator("+"));
|
||||
CHECK(parser.push_variable('e'));
|
||||
CHECK(parser.push_right_parenthesis());
|
||||
CHECK(parser.push_right_parenthesis());
|
||||
CHECK(parser.finalize());
|
||||
|
||||
const auto& tree = parser.tree();
|
||||
CHECK_EQ(tree.size(), 9);
|
||||
|
@ -276,126 +258,72 @@ TEST_SUITE("util::flat_bool_expr_tree")
|
|||
|
||||
SUBCASE("(")
|
||||
{
|
||||
auto bad_parse = [&]()
|
||||
{
|
||||
parser.push_left_parenthesis();
|
||||
parser.finalize();
|
||||
};
|
||||
CHECK_THROWS_AS(bad_parse(), std::invalid_argument);
|
||||
CHECK(parser.push_left_parenthesis());
|
||||
CHECK_FALSE(parser.finalize());
|
||||
}
|
||||
|
||||
SUBCASE(")")
|
||||
{
|
||||
auto bad_parse = [&]()
|
||||
{
|
||||
parser.push_right_parenthesis();
|
||||
// parser.finalize();
|
||||
};
|
||||
CHECK_THROWS_AS(bad_parse(), std::invalid_argument);
|
||||
CHECK_FALSE(parser.push_right_parenthesis());
|
||||
}
|
||||
|
||||
SUBCASE("(a+b")
|
||||
{
|
||||
auto bad_parse = [&]()
|
||||
{
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_variable('a');
|
||||
parser.push_operator("+");
|
||||
parser.push_variable('b');
|
||||
parser.finalize();
|
||||
};
|
||||
CHECK_THROWS_AS(bad_parse(), std::invalid_argument);
|
||||
CHECK(parser.push_left_parenthesis());
|
||||
CHECK(parser.push_variable('a'));
|
||||
CHECK(parser.push_operator("+"));
|
||||
CHECK(parser.push_variable('b'));
|
||||
CHECK_FALSE(parser.finalize());
|
||||
}
|
||||
|
||||
SUBCASE("a))")
|
||||
SUBCASE("a)")
|
||||
{
|
||||
auto bad_parse = [&]()
|
||||
{
|
||||
parser.push_variable('a');
|
||||
parser.push_right_parenthesis();
|
||||
parser.push_right_parenthesis();
|
||||
parser.finalize();
|
||||
};
|
||||
CHECK_THROWS_AS(bad_parse(), std::invalid_argument);
|
||||
CHECK(parser.push_variable('a'));
|
||||
CHECK_FALSE(parser.push_right_parenthesis());
|
||||
}
|
||||
|
||||
SUBCASE("+")
|
||||
{
|
||||
auto bad_parse = [&]()
|
||||
{
|
||||
parser.push_operator("+");
|
||||
parser.finalize();
|
||||
};
|
||||
CHECK_THROWS_AS(bad_parse(), std::invalid_argument);
|
||||
CHECK_FALSE(parser.push_operator("+"));
|
||||
}
|
||||
|
||||
SUBCASE("a b +")
|
||||
{
|
||||
auto bad_parse = [&]()
|
||||
{
|
||||
parser.push_variable('a');
|
||||
parser.push_variable('b');
|
||||
parser.push_operator("+");
|
||||
parser.finalize();
|
||||
};
|
||||
CHECK_THROWS_AS(bad_parse(), std::invalid_argument);
|
||||
CHECK(parser.push_variable('a'));
|
||||
CHECK_FALSE(parser.push_variable('b'));
|
||||
}
|
||||
|
||||
SUBCASE("a + + b")
|
||||
{
|
||||
auto bad_parse = [&]()
|
||||
{
|
||||
parser.push_variable('a');
|
||||
parser.push_operator("+");
|
||||
parser.push_operator("+");
|
||||
parser.push_variable('b');
|
||||
parser.finalize();
|
||||
};
|
||||
CHECK_THROWS_AS(bad_parse(), std::invalid_argument);
|
||||
CHECK(parser.push_variable('a'));
|
||||
CHECK(parser.push_operator("+"));
|
||||
CHECK_FALSE(parser.push_operator("+"));
|
||||
}
|
||||
|
||||
SUBCASE("a +")
|
||||
{
|
||||
auto bad_parse = [&]()
|
||||
{
|
||||
parser.push_variable('a');
|
||||
parser.push_operator("+");
|
||||
parser.finalize();
|
||||
};
|
||||
CHECK_THROWS_AS(bad_parse(), std::invalid_argument);
|
||||
CHECK(parser.push_variable('a'));
|
||||
CHECK(parser.push_operator("+"));
|
||||
CHECK_FALSE(parser.finalize());
|
||||
}
|
||||
|
||||
SUBCASE("a + )")
|
||||
{
|
||||
auto bad_parse = [&]()
|
||||
{
|
||||
parser.push_variable('a');
|
||||
parser.push_operator("+");
|
||||
parser.push_right_parenthesis();
|
||||
parser.finalize();
|
||||
};
|
||||
CHECK_THROWS_AS(bad_parse(), std::invalid_argument);
|
||||
CHECK(parser.push_variable('a'));
|
||||
CHECK(parser.push_operator("+"));
|
||||
CHECK_FALSE(parser.push_right_parenthesis());
|
||||
}
|
||||
SUBCASE("(((a)) + b (* c")
|
||||
{
|
||||
auto bad_parse = [&]()
|
||||
{
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_right_parenthesis();
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_variable('a');
|
||||
parser.push_right_parenthesis();
|
||||
parser.push_right_parenthesis();
|
||||
parser.push_operator("+");
|
||||
parser.push_variable('b');
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_operator("*");
|
||||
parser.push_variable('c');
|
||||
parser.finalize();
|
||||
};
|
||||
CHECK_THROWS_AS(bad_parse(), std::invalid_argument);
|
||||
CHECK(parser.push_left_parenthesis());
|
||||
CHECK(parser.push_left_parenthesis());
|
||||
CHECK(parser.push_left_parenthesis());
|
||||
CHECK(parser.push_variable('a'));
|
||||
CHECK(parser.push_right_parenthesis());
|
||||
CHECK(parser.push_right_parenthesis());
|
||||
CHECK(parser.push_operator("+"));
|
||||
CHECK(parser.push_variable('b'));
|
||||
CHECK_FALSE(parser.push_left_parenthesis());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -404,15 +332,15 @@ TEST_SUITE("util::flat_bool_expr_tree")
|
|||
// Infix: (false and false) or (false or (false or true))
|
||||
// Postfix: false true or false or false false and or
|
||||
auto parser = PostfixParser<bool, BoolOperator>{};
|
||||
parser.push_variable(false);
|
||||
parser.push_variable(true);
|
||||
parser.push_operator(BoolOperator::logical_or);
|
||||
parser.push_variable(false);
|
||||
parser.push_operator(BoolOperator::logical_or);
|
||||
parser.push_variable(false);
|
||||
parser.push_variable(false);
|
||||
parser.push_operator(BoolOperator::logical_and);
|
||||
parser.push_operator(BoolOperator::logical_or);
|
||||
CHECK(parser.push_variable(false));
|
||||
CHECK(parser.push_variable(true));
|
||||
CHECK(parser.push_operator(BoolOperator::logical_or));
|
||||
CHECK(parser.push_variable(false));
|
||||
CHECK(parser.push_operator(BoolOperator::logical_or));
|
||||
CHECK(parser.push_variable(false));
|
||||
CHECK(parser.push_variable(false));
|
||||
CHECK(parser.push_operator(BoolOperator::logical_and));
|
||||
CHECK(parser.push_operator(BoolOperator::logical_or));
|
||||
auto tree = flat_bool_expr_tree(std::move(parser).tree());
|
||||
|
||||
SUBCASE("Empty")
|
||||
|
@ -468,15 +396,15 @@ TEST_SUITE("util::flat_bool_expr_tree")
|
|||
// Infix: ((x3 or x4) and x2) and (x0 or x1)
|
||||
// Postfix: x0 x1 or x2 x3 x4 or and and
|
||||
auto parser = PostfixParser<std::size_t, BoolOperator>{};
|
||||
parser.push_variable(0);
|
||||
parser.push_variable(1);
|
||||
parser.push_operator(BoolOperator::logical_or);
|
||||
parser.push_variable(2);
|
||||
parser.push_variable(3);
|
||||
parser.push_variable(4);
|
||||
parser.push_operator(BoolOperator::logical_or);
|
||||
parser.push_operator(BoolOperator::logical_and);
|
||||
parser.push_operator(BoolOperator::logical_and);
|
||||
CHECK(parser.push_variable(0));
|
||||
CHECK(parser.push_variable(1));
|
||||
CHECK(parser.push_operator(BoolOperator::logical_or));
|
||||
CHECK(parser.push_variable(2));
|
||||
CHECK(parser.push_variable(3));
|
||||
CHECK(parser.push_variable(4));
|
||||
CHECK(parser.push_operator(BoolOperator::logical_or));
|
||||
CHECK(parser.push_operator(BoolOperator::logical_and));
|
||||
CHECK(parser.push_operator(BoolOperator::logical_and));
|
||||
auto tree = flat_bool_expr_tree(std::move(parser).tree());
|
||||
|
||||
static constexpr std::size_t n_vars = 5;
|
||||
|
@ -494,26 +422,26 @@ TEST_SUITE("util::flat_bool_expr_tree")
|
|||
{ return ((x[0] || x[1]) && (x[2] || x[3] || x[4]) && x[5]) || x[6]; };
|
||||
auto parser = InfixParser<std::size_t, BoolOperator>{};
|
||||
// Infix: ((x0 or x1) and (x2 or x3 or x4) and x5) or x6
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_variable(0);
|
||||
parser.push_operator(BoolOperator::logical_or);
|
||||
parser.push_variable(1);
|
||||
parser.push_right_parenthesis();
|
||||
parser.push_operator(BoolOperator::logical_and);
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_variable(2);
|
||||
parser.push_operator(BoolOperator::logical_or);
|
||||
parser.push_variable(3);
|
||||
parser.push_operator(BoolOperator::logical_or);
|
||||
parser.push_variable(4);
|
||||
parser.push_right_parenthesis();
|
||||
parser.push_operator(BoolOperator::logical_and);
|
||||
parser.push_variable(5);
|
||||
parser.push_right_parenthesis();
|
||||
parser.push_operator(BoolOperator::logical_or);
|
||||
parser.push_variable(6);
|
||||
parser.finalize();
|
||||
CHECK(parser.push_left_parenthesis());
|
||||
CHECK(parser.push_left_parenthesis());
|
||||
CHECK(parser.push_variable(0));
|
||||
CHECK(parser.push_operator(BoolOperator::logical_or));
|
||||
CHECK(parser.push_variable(1));
|
||||
CHECK(parser.push_right_parenthesis());
|
||||
CHECK(parser.push_operator(BoolOperator::logical_and));
|
||||
CHECK(parser.push_left_parenthesis());
|
||||
CHECK(parser.push_variable(2));
|
||||
CHECK(parser.push_operator(BoolOperator::logical_or));
|
||||
CHECK(parser.push_variable(3));
|
||||
CHECK(parser.push_operator(BoolOperator::logical_or));
|
||||
CHECK(parser.push_variable(4));
|
||||
CHECK(parser.push_right_parenthesis());
|
||||
CHECK(parser.push_operator(BoolOperator::logical_and));
|
||||
CHECK(parser.push_variable(5));
|
||||
CHECK(parser.push_right_parenthesis());
|
||||
CHECK(parser.push_operator(BoolOperator::logical_or));
|
||||
CHECK(parser.push_variable(6));
|
||||
CHECK(parser.finalize());
|
||||
auto tree = flat_bool_expr_tree(std::move(parser).tree());
|
||||
|
||||
static constexpr std::size_t n_vars = 7;
|
||||
|
@ -530,26 +458,26 @@ TEST_SUITE("util::flat_bool_expr_tree")
|
|||
{
|
||||
auto parser = InfixParser<std::size_t, BoolOperator>{};
|
||||
// Infix: ((x0 or x1) and (x2 or x3 or x4) and x5) or x6
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_variable(0);
|
||||
parser.push_operator(BoolOperator::logical_or);
|
||||
parser.push_variable(1);
|
||||
parser.push_right_parenthesis();
|
||||
parser.push_operator(BoolOperator::logical_and);
|
||||
parser.push_left_parenthesis();
|
||||
parser.push_variable(2);
|
||||
parser.push_operator(BoolOperator::logical_or);
|
||||
parser.push_variable(3);
|
||||
parser.push_operator(BoolOperator::logical_or);
|
||||
parser.push_variable(4);
|
||||
parser.push_right_parenthesis();
|
||||
parser.push_operator(BoolOperator::logical_and);
|
||||
parser.push_variable(5);
|
||||
parser.push_right_parenthesis();
|
||||
parser.push_operator(BoolOperator::logical_or);
|
||||
parser.push_variable(6);
|
||||
parser.finalize();
|
||||
CHECK(parser.push_left_parenthesis());
|
||||
CHECK(parser.push_left_parenthesis());
|
||||
CHECK(parser.push_variable(0));
|
||||
CHECK(parser.push_operator(BoolOperator::logical_or));
|
||||
CHECK(parser.push_variable(1));
|
||||
CHECK(parser.push_right_parenthesis());
|
||||
CHECK(parser.push_operator(BoolOperator::logical_and));
|
||||
CHECK(parser.push_left_parenthesis());
|
||||
CHECK(parser.push_variable(2));
|
||||
CHECK(parser.push_operator(BoolOperator::logical_or));
|
||||
CHECK(parser.push_variable(3));
|
||||
CHECK(parser.push_operator(BoolOperator::logical_or));
|
||||
CHECK(parser.push_variable(4));
|
||||
CHECK(parser.push_right_parenthesis());
|
||||
CHECK(parser.push_operator(BoolOperator::logical_and));
|
||||
CHECK(parser.push_variable(5));
|
||||
CHECK(parser.push_right_parenthesis());
|
||||
CHECK(parser.push_operator(BoolOperator::logical_or));
|
||||
CHECK(parser.push_variable(6));
|
||||
CHECK(parser.finalize());
|
||||
auto tree = flat_bool_expr_tree(std::move(parser).tree());
|
||||
|
||||
auto result = std::string();
|
||||
|
|
|
@ -204,21 +204,12 @@ TEST_SUITE("util::URL")
|
|||
{
|
||||
SUBCASE("Empty")
|
||||
{
|
||||
const URL url = URL::parse("");
|
||||
CHECK_EQ(url.scheme(), URL::https);
|
||||
CHECK_EQ(url.host(), URL::localhost);
|
||||
CHECK_EQ(url.path(), "/");
|
||||
CHECK_EQ(url.pretty_path(), "/");
|
||||
CHECK_EQ(url.user(), "");
|
||||
CHECK_EQ(url.password(), "");
|
||||
CHECK_EQ(url.port(), "");
|
||||
CHECK_EQ(url.query(), "");
|
||||
CHECK_EQ(url.fragment(), "");
|
||||
CHECK_FALSE(URL::parse("").has_value());
|
||||
}
|
||||
|
||||
SUBCASE("mamba.org")
|
||||
{
|
||||
const URL url = URL::parse("mamba.org");
|
||||
const URL url = URL::parse("mamba.org").value();
|
||||
CHECK_EQ(url.scheme(), URL::https);
|
||||
CHECK_EQ(url.host(), "mamba.org");
|
||||
CHECK_EQ(url.path(), "/");
|
||||
|
@ -232,7 +223,7 @@ TEST_SUITE("util::URL")
|
|||
|
||||
SUBCASE("http://mamba.org")
|
||||
{
|
||||
const URL url = URL::parse("http://mamba.org");
|
||||
const URL url = URL::parse("http://mamba.org").value();
|
||||
CHECK_EQ(url.scheme(), "http");
|
||||
CHECK_EQ(url.host(), "mamba.org");
|
||||
CHECK_EQ(url.path(), "/");
|
||||
|
@ -246,7 +237,7 @@ TEST_SUITE("util::URL")
|
|||
|
||||
SUBCASE("s3://userx123:üúßsajd@mamba.org")
|
||||
{
|
||||
const URL url = URL::parse("s3://userx123:üúßsajd@mamba.org");
|
||||
const URL url = URL::parse("s3://userx123:üúßsajd@mamba.org").value();
|
||||
CHECK_EQ(url.scheme(), "s3");
|
||||
CHECK_EQ(url.host(), "mamba.org");
|
||||
CHECK_EQ(url.path(), "/");
|
||||
|
@ -260,7 +251,7 @@ TEST_SUITE("util::URL")
|
|||
|
||||
SUBCASE("http://user%40email.com:test@localhost:8000")
|
||||
{
|
||||
const URL url = URL::parse("http://user%40email.com:test@localhost:8000");
|
||||
const URL url = URL::parse("http://user%40email.com:test@localhost:8000").value();
|
||||
CHECK_EQ(url.scheme(), "http");
|
||||
CHECK_EQ(url.host(), "localhost");
|
||||
CHECK_EQ(url.path(), "/");
|
||||
|
@ -276,15 +267,12 @@ TEST_SUITE("util::URL")
|
|||
{
|
||||
// Fails before "user@email.com" needs to be percent encoded, otherwise parsing is
|
||||
// ill defined.
|
||||
|
||||
// Silencing [[nodiscard]] warning
|
||||
auto failure = [](std::string_view str) { [[maybe_unused]] auto url = URL::parse(str); };
|
||||
CHECK_THROWS_AS(failure("http://user@40email.com:test@localhost"), std::invalid_argument);
|
||||
CHECK_FALSE(URL::parse("http://user@40email.com:test@localhost").has_value());
|
||||
}
|
||||
|
||||
SUBCASE("http://:pass@localhost:8000")
|
||||
{
|
||||
const URL url = URL::parse("http://:pass@localhost:8000");
|
||||
const URL url = URL::parse("http://:pass@localhost:8000").value();
|
||||
CHECK_EQ(url.scheme(), "http");
|
||||
CHECK_EQ(url.host(), "localhost");
|
||||
CHECK_EQ(url.path(), "/");
|
||||
|
@ -300,7 +288,8 @@ TEST_SUITE("util::URL")
|
|||
{
|
||||
// Not a valid IETF RFC 3986+ URL, but Curl parses it anyways.
|
||||
// Undefined behavior, no assumptions are made
|
||||
const URL url = URL::parse("https://mamba🆒🔬.org/this/is/a/path/?query=123&xyz=3333");
|
||||
const URL url = URL::parse("https://mamba🆒🔬.org/this/is/a/path/?query=123&xyz=3333")
|
||||
.value();
|
||||
CHECK_NE(url.host(URL::Decode::no), "mamba%f0%9f%86%92%f0%9f%94%ac.org");
|
||||
}
|
||||
|
||||
|
@ -308,7 +297,7 @@ TEST_SUITE("util::URL")
|
|||
{
|
||||
if (on_win)
|
||||
{
|
||||
const URL url = URL::parse("file://C:/Users/wolfv/test/document.json");
|
||||
const URL url = URL::parse("file://C:/Users/wolfv/test/document.json").value();
|
||||
CHECK_EQ(url.scheme(), "file");
|
||||
CHECK_EQ(url.host(), "");
|
||||
CHECK_EQ(url.path(), "/C:/Users/wolfv/test/document.json");
|
||||
|
@ -324,7 +313,7 @@ TEST_SUITE("util::URL")
|
|||
|
||||
SUBCASE("file:///home/wolfv/test/document.json")
|
||||
{
|
||||
const URL url = URL::parse("file:///home/wolfv/test/document.json");
|
||||
const URL url = URL::parse("file:///home/wolfv/test/document.json").value();
|
||||
CHECK_EQ(url.scheme(), "file");
|
||||
CHECK_EQ(url.host(), "");
|
||||
CHECK_EQ(url.path(), "/home/wolfv/test/document.json");
|
||||
|
@ -340,13 +329,13 @@ TEST_SUITE("util::URL")
|
|||
{
|
||||
// Not a valid IETF RFC 3986+ URL, but Curl parses it anyways.
|
||||
// Undefined behavior, no assumptions are made
|
||||
const URL url = URL::parse("file:///home/great:doc.json");
|
||||
const URL url = URL::parse("file:///home/great:doc.json").value();
|
||||
CHECK_NE(url.path(URL::Decode::no), "/home/great%3Adoc.json");
|
||||
}
|
||||
|
||||
SUBCASE("file:///home/great%3Adoc.json")
|
||||
{
|
||||
const URL url = URL::parse("file:///home/great%3Adoc.json");
|
||||
const URL url = URL::parse("file:///home/great%3Adoc.json").value();
|
||||
CHECK_EQ(url.scheme(), "file");
|
||||
CHECK_EQ(url.host(), "");
|
||||
CHECK_EQ(url.path(), "/home/great:doc.json");
|
||||
|
@ -361,7 +350,7 @@ TEST_SUITE("util::URL")
|
|||
|
||||
SUBCASE("https://169.254.0.0/page")
|
||||
{
|
||||
const URL url = URL::parse("https://169.254.0.0/page");
|
||||
const URL url = URL::parse("https://169.254.0.0/page").value();
|
||||
CHECK_EQ(url.scheme(), "https");
|
||||
CHECK_EQ(url.host(), "169.254.0.0");
|
||||
CHECK_EQ(url.path(), "/page");
|
||||
|
@ -375,8 +364,8 @@ TEST_SUITE("util::URL")
|
|||
|
||||
SUBCASE("ftp://user:pass@[2001:db8:85a3:8d3:1319:0:370:7348]:9999/page")
|
||||
{
|
||||
const URL url = URL::parse("ftp://user:pass@[2001:db8:85a3:8d3:1319:0:370:7348]:9999/page"
|
||||
);
|
||||
const URL url = URL::parse("ftp://user:pass@[2001:db8:85a3:8d3:1319:0:370:7348]:9999/page")
|
||||
.value();
|
||||
CHECK_EQ(url.scheme(), "ftp");
|
||||
CHECK_EQ(url.host(), "[2001:db8:85a3:8d3:1319:0:370:7348]");
|
||||
CHECK_EQ(url.path(), "/page");
|
||||
|
@ -390,7 +379,7 @@ TEST_SUITE("util::URL")
|
|||
|
||||
SUBCASE("https://mamba.org/page#the-fragment")
|
||||
{
|
||||
const URL url = URL::parse("https://mamba.org/page#the-fragment");
|
||||
const URL url = URL::parse("https://mamba.org/page#the-fragment").value();
|
||||
CHECK_EQ(url.scheme(), "https");
|
||||
CHECK_EQ(url.host(), "mamba.org");
|
||||
CHECK_EQ(url.path(), "/page");
|
||||
|
@ -672,16 +661,19 @@ TEST_SUITE("util::URL")
|
|||
TEST_CASE("Equality")
|
||||
{
|
||||
CHECK_EQ(URL(), URL());
|
||||
CHECK_EQ(URL::parse("https://169.254.0.0/page"), URL::parse("https://169.254.0.0/page"));
|
||||
CHECK_EQ(URL::parse("mamba.org"), URL::parse("mamba.org/"));
|
||||
CHECK_EQ(URL::parse("mAmba.oRg"), URL::parse("mamba.org/"));
|
||||
CHECK_EQ(URL::parse("localhost/page"), URL::parse("https://localhost/page"));
|
||||
CHECK_EQ(
|
||||
URL::parse("https://169.254.0.0/page").value(),
|
||||
URL::parse("https://169.254.0.0/page").value()
|
||||
);
|
||||
CHECK_EQ(URL::parse("mamba.org").value(), URL::parse("mamba.org/").value());
|
||||
CHECK_EQ(URL::parse("mAmba.oRg").value(), URL::parse("mamba.org/").value());
|
||||
CHECK_EQ(URL::parse("localhost/page").value(), URL::parse("https://localhost/page").value());
|
||||
|
||||
CHECK_NE(URL::parse("mamba.org/page"), URL::parse("mamba.org/"));
|
||||
CHECK_NE(URL::parse("mamba.org"), URL::parse("mamba.org:9999"));
|
||||
CHECK_NE(URL::parse("user@mamba.org"), URL::parse("mamba.org"));
|
||||
CHECK_NE(URL::parse("mamba.org/page"), URL::parse("mamba.org/page?q=v"));
|
||||
CHECK_NE(URL::parse("mamba.org/page"), URL::parse("mamba.org/page#there"));
|
||||
CHECK_NE(URL::parse("mamba.org/page").value(), URL::parse("mamba.org/").value());
|
||||
CHECK_NE(URL::parse("mamba.org").value(), URL::parse("mamba.org:9999").value());
|
||||
CHECK_NE(URL::parse("user@mamba.org").value(), URL::parse("mamba.org").value());
|
||||
CHECK_NE(URL::parse("mamba.org/page").value(), URL::parse("mamba.org/page?q=v").value());
|
||||
CHECK_NE(URL::parse("mamba.org/page").value(), URL::parse("mamba.org/page#there").value());
|
||||
}
|
||||
|
||||
TEST_CASE("Append path")
|
||||
|
|
|
@ -4,6 +4,11 @@
|
|||
//
|
||||
// The full license is in the file LICENSE, distributed with this software.
|
||||
|
||||
#ifndef MAMBA_PY_EXPECTED_CASTER
|
||||
#define MAMBA_PY_EXPECTED_CASTER
|
||||
|
||||
#include <exception>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <pybind11/cast.h>
|
||||
|
@ -11,9 +16,6 @@
|
|||
#include <pybind11/stl.h>
|
||||
#include <tl/expected.hpp>
|
||||
|
||||
#ifndef MAMBA_PY_EXPECTED_CASTER
|
||||
#define MAMBA_PY_EXPECTED_CASTER
|
||||
|
||||
namespace PYBIND11_NAMESPACE
|
||||
{
|
||||
namespace detail
|
||||
|
@ -47,6 +49,10 @@ namespace PYBIND11_NAMESPACE
|
|||
}
|
||||
else
|
||||
{
|
||||
// If we use ``expected`` without exception in our API, we need to convert them
|
||||
// to an exception before throwing it in PyBind11 code.
|
||||
// This could be done with partial specialization of this ``type_caster``.
|
||||
static_assert(std::is_base_of_v<std::exception, E>);
|
||||
throw std::forward<Expected>(src).error();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "mamba/specs/version_spec.hpp"
|
||||
|
||||
#include "bindings.hpp"
|
||||
#include "expected_caster.hpp"
|
||||
#include "flat_set_caster.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "weakening_map_bind.hpp"
|
||||
|
|
|
@ -36,7 +36,7 @@ read_stdin()
|
|||
std::string
|
||||
get_token_base(const std::string& host)
|
||||
{
|
||||
const auto url = mamba::util::URL::parse(host);
|
||||
const auto url = mamba::util::URL::parse(host).value();
|
||||
|
||||
std::string maybe_colon_and_port{};
|
||||
if (!url.port().empty())
|
||||
|
|
|
@ -52,7 +52,8 @@ namespace
|
|||
[&](auto pkg)
|
||||
{
|
||||
if (!out
|
||||
|| (specs::Version::parse(pkg.version) > specs::Version::parse(out->version)))
|
||||
|| (specs::Version::parse(pkg.version).value_or(specs::Version())
|
||||
> specs::Version::parse(out->version).value_or(specs::Version())))
|
||||
{
|
||||
out = std::move(pkg);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue