Skip to content

Commit

Permalink
feat(uri): add setter for path (#887)
Browse files Browse the repository at this point in the history
Part of #811

---------

Signed-off-by: Tony Gorez <[email protected]>
  • Loading branch information
tony-go authored Aug 8, 2024
1 parent 8fd0d98 commit b0dec14
Show file tree
Hide file tree
Showing 6 changed files with 332 additions and 19 deletions.
38 changes: 38 additions & 0 deletions src/uri/include/sourcemeta/jsontoolkit/uri.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,17 @@ class SOURCEMETA_JSONTOOLKIT_URI_EXPORT URI {
/// ```
auto is_fragment_only() const -> bool;

/// Check if the URI is relative. For example:
///
/// ```cpp
/// #include <sourcemeta/jsontoolkit/uri.h>
/// #include <cassert>
///
/// sourcemeta::jsontoolkit::URI uri{"./foo"};
/// assert(uri.is_relative());
/// ```
auto is_relative() const -> bool;

/// Get the scheme part of the URI, if any. For example:
///
/// ```cpp
Expand Down Expand Up @@ -153,6 +164,33 @@ class SOURCEMETA_JSONTOOLKIT_URI_EXPORT URI {
/// ```
[[nodiscard]] auto path() const -> std::optional<std::string>;

/// Set the path part of the URI. For example:
///
/// ```cpp
/// #include <sourcemeta/jsontoolkit/uri.h>
/// #include <cassert>
///
/// sourcemeta::jsontoolkit::URI uri{"https://www.sourcemeta.com"};
/// const std::string path{"/foo/bar"};
/// uri.path(path);
/// assert(uri.path().has_value());
/// assert(uri.path().value() == "/foo/bar");
/// ```
auto path(const std::string &path) -> URI &;

/// Set the path part of the URI with move semantics. For example:
///
/// ```cpp
/// #include <sourcemeta/jsontoolkit/uri.h>
/// #include <cassert>
///
/// sourcemeta::jsontoolkit::URI uri{"https://www.sourcemeta.com"};
/// std::string path{"/foo/bar"};
/// uri.path(std::move(path));
/// assert(uri.path().has_value());
/// assert(uri.path().value() == "/foo/bar");
auto path(std::string &&path) -> URI &;

/// Get the fragment part of the URI, if any. For example:
///
/// ```cpp
Expand Down
47 changes: 38 additions & 9 deletions src/uri/uri.cc
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ static auto uri_parse(const std::string &data, UriUriA *uri) -> void {
uri_normalize(uri);
}

static auto
canonicalize_path(const std::string &path) -> std::optional<std::string> {
static auto canonicalize_path(const std::string &path, const bool is_relative)
-> std::optional<std::string> {
std::vector<std::string> segments;
std::string segment;

Expand All @@ -93,7 +93,7 @@ canonicalize_path(const std::string &path) -> std::optional<std::string> {

// Reconstruct the canonical path
std::string canonical_path;
std::string separator = "";
std::string separator = is_relative ? "/" : "";
for (const auto &seg : segments) {
canonical_path += separator + seg;
separator = "/";
Expand Down Expand Up @@ -200,6 +200,10 @@ auto URI::parse() -> void {
this->parsed = true;
}

auto URI::is_relative() const -> bool {
return !this->scheme().has_value() || this->data.starts_with(".");
}

auto URI::is_absolute() const noexcept -> bool {
// An absolute URI always contains a scheme component,
return this->internal->uri.scheme.first != nullptr;
Expand Down Expand Up @@ -240,13 +244,37 @@ auto URI::path() const -> std::optional<std::string> {
return "/" + this->path_.value();
}

size_t path_pos = this->data.find(this->path_.value());
if (path_pos != std::string::npos && path_pos > 0 &&
this->data[path_pos - 1] == '/') {
return "/" + this->path_.value();
return path_;
}

auto URI::path(const std::string &path) -> URI & {
if (path.empty()) {
this->path_ = std::nullopt;
return *this;
}

return path_;
const auto is_relative_path = path.starts_with(".");
if (is_relative_path) {
throw URIError{"You cannot set a relative path"};
}

this->path_ = URI{path}.path_;
return *this;
}

auto URI::path(std::string &&path) -> URI & {
if (path.empty()) {
this->path_ = std::nullopt;
return *this;
}

const auto is_relative_path = path.starts_with(".");
if (is_relative_path) {
throw URIError{"You cannot set a relative path"};
}

this->path_ = URI{std::move(path)}.path_;
return *this;
}

auto URI::fragment() const -> std::optional<std::string_view> {
Expand Down Expand Up @@ -343,7 +371,8 @@ auto URI::canonicalize() -> URI & {
// Clean Path form ".." and "."
const auto result_path{this->path()};
if (result_path.has_value()) {
const auto canonical_path{canonicalize_path(result_path.value())};
const auto canonical_path{
canonicalize_path(result_path.value(), this->is_relative())};
if (canonical_path.has_value()) {
this->path_ = canonical_path.value();
}
Expand Down
1 change: 1 addition & 0 deletions test/uri/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ add_executable(sourcemeta_jsontoolkit_uri_unit
uri_scheme_test.cc
uri_query_test.cc
uri_is_absolute_test.cc
uri_is_relative_test.cc
uri_is_urn_test.cc
uri_is_tag_test.cc
uri_recompose_test.cc
Expand Down
6 changes: 6 additions & 0 deletions test/uri/uri_is_absolute_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@ TEST(URI_is_absolute, urn) {
const sourcemeta::jsontoolkit::URI uri{"urn:example:schema"};
EXPECT_TRUE(uri.is_absolute());
}

TEST(URI_is_absolute, slash) {
// The is a relative URI, even if the path is absolute.
const sourcemeta::jsontoolkit::URI uri{"/foo"};
EXPECT_FALSE(uri.is_absolute());
}
23 changes: 23 additions & 0 deletions test/uri/uri_is_relative_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include <gtest/gtest.h>
#include <sourcemeta/jsontoolkit/uri.h>

TEST(URI_is_relative, dot_slash) {
const sourcemeta::jsontoolkit::URI uri{"./foo"};
EXPECT_TRUE(uri.is_relative());
}

TEST(URI_is_relative, dot_dot_slash) {
const sourcemeta::jsontoolkit::URI uri{"../foo"};
EXPECT_TRUE(uri.is_relative());
}

TEST(URI_is_relative, without_dot_and_slash) {
const sourcemeta::jsontoolkit::URI uri{"foo"};
EXPECT_TRUE(uri.is_relative());
}

TEST(URI_is_relative, unique_slash) {
// The is a relative URI, even if the path is absolute.
const sourcemeta::jsontoolkit::URI uri{"/foo"};
EXPECT_TRUE(uri.is_relative());
}
Loading

0 comments on commit b0dec14

Please sign in to comment.