Skip to content

Commit

Permalink
Add CondaURL::path_without_token
Browse files Browse the repository at this point in the history
  • Loading branch information
AntoinePrv committed Oct 6, 2023
1 parent ec0a0f9 commit a5b03e4
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 11 deletions.
62 changes: 60 additions & 2 deletions libmamba/include/mamba/specs/conda_url.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,50 @@ namespace mamba::specs
using Base::authority;
using Base::path;
using Base::pretty_path;
using Base::set_path;
using Base::clear_path;
using Base::append_path;
using Base::query;
using Base::set_query;
using Base::clear_query;
using Base::fragment;
using Base::set_fragment;
using Base::clear_fragment;

/**
* Set the path from a not encoded value.
*
* All '/' are not encoded but interpreted as separators.
* On windows with a file scheme, the colon after the drive letter is not encoded.
* A leading '/' is added if abscent.
* If the path contains only a token, a trailing '/' is added afterwards.
*/
void set_path(std::string_view path, Encode::yes_type = Encode::yes);

/** Set the path from an already encoded value.
*
* A leading '/' is added if abscent.
* If the path contains only a token, a trailing '/' is added afterwards.
*/
void set_path(std::string path, Encode::no_type);

/**
* Append a not encoded sub path to the current path.
*
* Contrary to `std::filesystem::path::append`, this always append and never replace
* the current path, even if @p subpath starts with a '/'.
* All '/' are not encoded but interpreted as separators.
* If the final path contains only a token, a trailing '/' is added afterwards.
*/
void append_path(std::string_view path, Encode::yes_type = Encode::yes);

/**
* Append a already encoded sub path to the current path.
*
* Contrary to `std::filesystem::path::append`, this always append and never replace
* the current path, even if @p subpath starts with a '/'.
* If the final path contains only a token, a trailing '/' is added afterwards.
*/
void append_path(std::string_view path, Encode::no_type);

/** Return the Conda token, as delimited with "/t/", or empty if there isn't any. */
[[nodiscard]] auto token() const -> std::string_view;

Expand All @@ -79,6 +113,29 @@ namespace mamba::specs
/** Clear the token and return ``true`` if it exists, otherwise return ``false``. */
auto clear_token() -> bool;

/** Return the encoded part of the path without any Conda token, always start with '/'. */
[[nodiscard]] auto path_without_token(Decode::no_type) const -> std::string_view;

/** Return the decoded part of the path without any Conda token, always start with '/'. */
[[nodiscard]] auto path_without_token(Decode::yes_type = Decode::yes) const -> std::string;

/**
* Set the path from an already encoded value, without changing the Conda token.
*
* A leading '/' is added if abscent.
*/
void set_path_without_token(std::string_view path, Encode::no_type);

/**
* Set the path from an not yet encoded value, without changing the Conda token.
*
* A leading '/' is added if abscent.
*/
void set_path_without_token(std::string_view path, Encode::yes_type = Encode::yes);

/** Clear the path without changing the Conda token and return ``true`` if it exists. */
auto clear_path_without_token() -> bool;

/** Return the platform if part of the URL path. */
[[nodiscard]] auto platform() const -> std::optional<Platform>;

Expand Down Expand Up @@ -162,6 +219,7 @@ namespace mamba::specs
private:

void set_platform_no_check_input(std::string_view platform);
void ensure_path_without_token_leading_slash();

friend auto operator==(const CondaURL&, const CondaURL&) -> bool;
};
Expand Down
90 changes: 82 additions & 8 deletions libmamba/src/specs/conda_url.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,18 @@ namespace mamba::specs
}
}

void CondaURL::ensure_path_without_token_leading_slash()
{
if (path_without_token().empty())
{
set_path_without_token("/", Encode::no);
}
}

CondaURL::CondaURL(URL&& url)
: Base(std::move(url))
{
ensure_path_without_token_leading_slash();
}

CondaURL::CondaURL(const util::URL& url)
Expand All @@ -82,6 +91,30 @@ namespace mamba::specs
return CondaURL(URL::parse(url));
}

void CondaURL::set_path(std::string_view path, Encode::yes_type)
{
Base::set_path(path, Encode::yes);
ensure_path_without_token_leading_slash();
}

void CondaURL::set_path(std::string path, Encode::no_type)
{
Base::set_path(path, Encode::no);
ensure_path_without_token_leading_slash();
}

void CondaURL::append_path(std::string_view path, Encode::yes_type)
{
Base::append_path(path, Encode::yes);
ensure_path_without_token_leading_slash();
}

void CondaURL::append_path(std::string_view path, Encode::no_type)
{
Base::append_path(path, Encode::no);
ensure_path_without_token_leading_slash();
}

auto CondaURL::token() const -> std::string_view
{
static constexpr auto npos = std::string_view::npos;
Expand Down Expand Up @@ -116,8 +149,6 @@ namespace mamba::specs

void CondaURL::set_token(std::string_view token)
{
static constexpr auto npos = std::string_view::npos;

if (!is_token(token))
{
throw std::invalid_argument(fmt::format(R"(Invalid CondaURL token "{}")", token));
Expand Down Expand Up @@ -147,10 +178,53 @@ namespace mamba::specs
assert(token_prefix.size() < len);
std::string l_path = clear_path(); // percent encoded
l_path.erase(0, len);
set_path(std::move(l_path), Encode::no);
Base::set_path(std::move(l_path), Encode::no);
return true;
}

auto CondaURL::path_without_token(Decode::no_type) const -> std::string_view
{
const auto& full_path = path(Decode::no);
if (const auto len = token_and_prefix_len(full_path); len > 0)
{
return std::string_view(full_path).substr(std::min(len, full_path.size()));
}
return full_path;
}

auto CondaURL::path_without_token(Decode::yes_type) const -> std::string
{
return util::url_decode(path_without_token(Decode::no));
}

void CondaURL::set_path_without_token(std::string_view new_path, Encode::no_type)
{
if (const auto len = token_and_prefix_len(path(Decode::no)); len > 0)
{
auto old_path = clear_path();
old_path.erase(std::min(len, old_path.size()));
Base::set_path(std::move(old_path), Encode::no);
Base::append_path(new_path.empty() ? "/" : new_path);
}
else
{
Base::set_path(std::string(new_path), Encode::no);
}
}

void CondaURL::set_path_without_token(std::string_view new_path, Encode::yes_type)
{
clear_path_without_token();
Base::append_path(new_path.empty() ? "/" : new_path, Encode::yes);
}

auto CondaURL::clear_path_without_token() -> bool
{
const std::size_t old_len = path(Decode::no).size();
set_path_without_token("", Encode::no);
return path(Decode::no).size() != old_len;
}

auto CondaURL::platform() const -> std::optional<Platform>
{
const auto& l_path = path(Decode::no);
Expand Down Expand Up @@ -191,7 +265,7 @@ namespace mamba::specs
std::string l_path = clear_path(); // percent encoded
const auto plat_len = (len != npos) ? len - 1 : npos;
l_path.replace(pos + 1, plat_len, platform);
set_path(std::move(l_path), Encode::no);
Base::set_path(std::move(l_path), Encode::no);
}

void CondaURL::set_platform(std::string_view platform)
Expand Down Expand Up @@ -219,7 +293,7 @@ namespace mamba::specs
assert(1 < count);
std::string l_path = clear_path(); // percent encoded
l_path.erase(pos, count);
set_path(std::move(l_path), Encode::no);
Base::set_path(std::move(l_path), Encode::no);
return true;
}

Expand Down Expand Up @@ -259,11 +333,11 @@ namespace mamba::specs
auto l_path = clear_path();
const auto pos = std::min(std::min(l_path.rfind('/'), l_path.size()) + 1ul, l_path.size());
l_path.replace(pos, std::string::npos, pkg);
set_path(std::move(l_path), Encode::no);
Base::set_path(std::move(l_path), Encode::no);
}
else
{
append_path(pkg, Encode::no);
Base::append_path(pkg, Encode::no);
}
}

Expand All @@ -274,7 +348,7 @@ namespace mamba::specs
{
auto l_path = clear_path();
l_path.erase(l_path.rfind('/'));
set_path(std::move(l_path), Encode::no);
Base::set_path(std::move(l_path), Encode::no);
return true;
}
return false;
Expand Down
48 changes: 47 additions & 1 deletion libmamba/tests/src/specs/test_conda_url.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,39 +23,46 @@ TEST_SUITE("specs::CondaURL")
{
url.set_path("/folder/file.txt");
CHECK_EQ(url.token(), "");
CHECK_EQ(url.path_without_token(), "/folder/file.txt");

url.set_token("mytoken");
CHECK_EQ(url.token(), "mytoken");
CHECK_EQ(url.path_without_token(), "/folder/file.txt");
CHECK_EQ(url.path(), "/t/mytoken/folder/file.txt");

CHECK(url.clear_token());
CHECK_EQ(url.path_without_token(), "/folder/file.txt");
CHECK_EQ(url.path(), "/folder/file.txt");
}

SUBCASE("https://repo.mamba.pm/t/xy-12345678-1234/conda-forge/linux-64")
{
url.set_path("/t/xy-12345678-1234/conda-forge/linux-64");
CHECK_EQ(url.token(), "xy-12345678-1234");
CHECK_EQ(url.path_without_token(), "/conda-forge/linux-64");

SUBCASE("Cannot set invalid token")
{
CHECK_THROWS_AS(url.set_token(""), std::invalid_argument);
CHECK_THROWS_AS(url.set_token("?fds:g"), std::invalid_argument);
CHECK_EQ(url.token(), "xy-12345678-1234");
CHECK_EQ(url.path_without_token(), "/conda-forge/linux-64");
CHECK_EQ(url.path(), "/t/xy-12345678-1234/conda-forge/linux-64");
}

SUBCASE("Clear token")
{
CHECK(url.clear_token());
CHECK_EQ(url.token(), "");
CHECK_EQ(url.path_without_token(), "/conda-forge/linux-64");
CHECK_EQ(url.path(), "/conda-forge/linux-64");
}

SUBCASE("Set token")
{
url.set_token("abcd");
CHECK_EQ(url.token(), "abcd");
CHECK_EQ(url.path_without_token(), "/conda-forge/linux-64");
CHECK_EQ(url.path(), "/t/abcd/conda-forge/linux-64");
}
}
Expand All @@ -67,9 +74,12 @@ TEST_SUITE("specs::CondaURL")

url.set_token("abcd");
CHECK_EQ(url.token(), "abcd");
CHECK_EQ(url.path(), "/t/abcd");
CHECK_EQ(url.path_without_token(), "/");
CHECK_EQ(url.path(), "/t/abcd/");

CHECK(url.clear_token());
CHECK_EQ(url.token(), "");
CHECK_EQ(url.path_without_token(), "/");
CHECK_EQ(url.path(), "/");
}

Expand All @@ -80,13 +90,48 @@ TEST_SUITE("specs::CondaURL")

url.set_token("abcd");
CHECK_EQ(url.token(), "abcd");
CHECK_EQ(url.path_without_token(), "/bar/t/xy-12345678-1234-1234-1234-123456789012/");
CHECK_EQ(url.path(), "/t/abcd/bar/t/xy-12345678-1234-1234-1234-123456789012/");

CHECK(url.clear_token());
CHECK_EQ(url.path_without_token(), "/bar/t/xy-12345678-1234-1234-1234-123456789012/");
CHECK_EQ(url.path(), "/bar/t/xy-12345678-1234-1234-1234-123456789012/");
}
}

TEST_CASE("Path without token")
{
CondaURL url{};
url.set_scheme("https");
url.set_host("repo.mamba.pm");

SUBCASE("Setters")
{
url.set_path_without_token("foo");
CHECK_EQ(url.path_without_token(), "/foo");
url.set_token("mytoken");
CHECK_EQ(url.path_without_token(), "/foo");
CHECK(url.clear_path_without_token());
CHECK_EQ(url.path_without_token(), "/");
}

SUBCASE("Parse")
{
url = CondaURL::parse("mamba.org/t/xy-12345678-1234-1234-1234-123456789012");
CHECK_EQ(url.token(), "xy-12345678-1234-1234-1234-123456789012");
CHECK_EQ(url.path_without_token(), "/");
CHECK_EQ(url.path(), "/t/xy-12345678-1234-1234-1234-123456789012/");
}

SUBCASE("Encoding")
{
url.set_token("mytoken");
url.set_path_without_token("some / weird/path %");
CHECK_EQ(url.path_without_token(), "/some / weird/path %");
CHECK_EQ(url.path_without_token(CondaURL::Decode::no), "/some%20/%20weird/path%20%25");
}
}

TEST_CASE("Platform")
{
CondaURL url{};
Expand All @@ -99,6 +144,7 @@ TEST_SUITE("specs::CondaURL")
CHECK_EQ(url.platform_name(), "");

CHECK_THROWS_AS(url.set_platform(Platform::linux_64), std::invalid_argument);
CHECK_EQ(url.path_without_token(), "/");
CHECK_EQ(url.path(), "/");

CHECK_FALSE(url.clear_platform());
Expand Down

0 comments on commit a5b03e4

Please sign in to comment.