aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/Uri/Uri.hpp61
-rw-r--r--src/Uri.cpp78
-rw-r--r--test/src/UriTests.cpp80
3 files changed, 217 insertions, 2 deletions
diff --git a/include/Uri/Uri.hpp b/include/Uri/Uri.hpp
index b5f85c2..6103bff 100644
--- a/include/Uri/Uri.hpp
+++ b/include/Uri/Uri.hpp
@@ -10,6 +10,8 @@
*/
#include <memory>
+#include <string>
+#include <vector>
namespace Uri {
@@ -33,6 +35,65 @@ namespace Uri {
*/
Uri();
+ /**
+ * This method sets the character or character sequence
+ * that should be interpreted as a path delimiter.
+ *
+ * @param[in] newPathDelimiter
+ * This is the character or character sequence
+ * that should be interpreted as a path delimiter.
+ */
+ void SetPathDelimiter(const std::string& newPathDelimiter);
+
+ /**
+ * This method builds the URI from the elements parsed
+ * from the given string rendering of a URI.
+ *
+ * @param[in] uriString
+ * This is the string rendering of the URI to parse.
+ *
+ * @return
+ * An indication of whether or not the URI was
+ * parsed successfully is returned.
+ */
+ bool ParseFromString(const std::string& uriString);
+
+ /**
+ * This method returns the "scheme" element of the URI.
+ *
+ * @return
+ * The "scheme" element of the URI is returned.
+ *
+ * @retval ""
+ * This is returned if there is no "scheme" element in the URI.
+ */
+ std::string GetScheme() const;
+
+ /**
+ * This method returns the "host" element of the URI.
+ *
+ * @return
+ * The "host" element of the URI is returned.
+ *
+ * @retval ""
+ * This is returned if there is no "host" element in the URI.
+ */
+ std::string GetHost() const;
+
+ /**
+ * This method returns the "path" element of the URI,
+ * as a sequence of steps.
+ *
+ * @note
+ * If the first step of the path is an empty string,
+ * then the URI has an absolute path.
+ *
+ * @return
+ * The "path" element of the URI is returned
+ * as a sequence of steps.
+ */
+ std::vector< std::string > GetPath() const;
+
// Private properties
private:
/**
diff --git a/src/Uri.cpp b/src/Uri.cpp
index d63a5a9..287d3ba 100644
--- a/src/Uri.cpp
+++ b/src/Uri.cpp
@@ -6,13 +6,36 @@
* © 2018 by Richard Walters
*/
+#include <string>
#include <Uri/Uri.hpp>
+#include <vector>
namespace Uri {
/**
* This contains the private properties of a Uri instance.
*/
struct Uri::Impl {
+ /**
+ * This is the character or character sequence
+ * that should be interpreted as a path delimiter.
+ */
+ std::string pathDelimiter = "/";
+
+ /**
+ * This is the "scheme" element of the URI.
+ */
+ std::string scheme;
+
+ /**
+ * This is the "host" element of the URI.
+ */
+ std::string host;
+
+ /**
+ * This is the "path" element of the URI,
+ * as a sequence of steps.
+ */
+ std::vector< std::string > path;
};
Uri::~Uri() = default;
@@ -22,4 +45,59 @@ namespace Uri {
{
}
+ void Uri::SetPathDelimiter(const std::string& newPathDelimiter) {
+ impl_->pathDelimiter = newPathDelimiter;
+ }
+
+ bool Uri::ParseFromString(const std::string& uriString) {
+ // First parse the scheme.
+ const auto schemeEnd = uriString.find(':');
+ impl_->scheme = uriString.substr(0, schemeEnd);
+ auto rest = uriString.substr(schemeEnd + 1);
+
+ // Next parse the host.
+ if (rest.substr(0, 2) == "//") {
+ const auto authorityEnd = rest.find(impl_->pathDelimiter, 2);
+ impl_->host = rest.substr(2, authorityEnd - 2);
+ rest = rest.substr(authorityEnd);
+ } else {
+ impl_->host.clear();
+ }
+
+ // Finally, parse the path.
+ impl_->path.clear();
+ if (rest == impl_->pathDelimiter) {
+ // Special case of a path that is empty but needs a single
+ // empty-string element to indicate that it is absolute.
+ impl_->path.push_back("");
+ } else if (!rest.empty()) {
+ for(;;) {
+ auto pathDelimiter = rest.find(impl_->pathDelimiter);
+ if (pathDelimiter == std::string::npos) {
+ impl_->path.push_back(rest);
+ break;
+ } else {
+ impl_->path.emplace_back(
+ rest.begin(),
+ rest.begin() + pathDelimiter
+ );
+ rest = rest.substr(pathDelimiter + impl_->pathDelimiter.length());
+ }
+ }
+ }
+ return true;
+ }
+
+ std::string Uri::GetScheme() const {
+ return impl_->scheme;
+ }
+
+ std::string Uri::GetHost() const {
+ return impl_->host;
+ }
+
+ std::vector< std::string > Uri::GetPath() const {
+ return impl_->path;
+ }
+
}
diff --git a/test/src/UriTests.cpp b/test/src/UriTests.cpp
index 3cb5336..73848a9 100644
--- a/test/src/UriTests.cpp
+++ b/test/src/UriTests.cpp
@@ -7,9 +7,85 @@
*/
#include <gtest/gtest.h>
+#include <stddef.h>
#include <Uri/Uri.hpp>
-TEST(UriTests, Placeholder) {
+TEST(UriTests, ParseFromStringUrl) {
Uri::Uri uri;
- ASSERT_TRUE(true);
+ ASSERT_TRUE(uri.ParseFromString("http://www.example.com/foo/bar"));
+ ASSERT_EQ("http", uri.GetScheme());
+ ASSERT_EQ("www.example.com", uri.GetHost());
+ ASSERT_EQ(
+ (std::vector< std::string >{
+ "",
+ "foo",
+ "bar",
+ }),
+ uri.GetPath()
+ );
+}
+
+TEST(UriTests, ParseFromStringUrnDefaultPathDelimiter) {
+ Uri::Uri uri;
+ ASSERT_TRUE(uri.ParseFromString("urn:book:fantasy:Hobbit"));
+ ASSERT_EQ("urn", uri.GetScheme());
+ ASSERT_EQ("", uri.GetHost());
+ ASSERT_EQ(
+ (std::vector< std::string >{
+ "book:fantasy:Hobbit",
+ }),
+ uri.GetPath()
+ );
+}
+
+TEST(UriTests, ParseFromStringUrnSingleCharacterPathDelimiter) {
+ Uri::Uri uri;
+ uri.SetPathDelimiter(":");
+ ASSERT_TRUE(uri.ParseFromString("urn:book:fantasy:Hobbit"));
+ ASSERT_EQ("urn", uri.GetScheme());
+ ASSERT_EQ("", uri.GetHost());
+ ASSERT_EQ(
+ (std::vector< std::string >{
+ "book",
+ "fantasy",
+ "Hobbit",
+ }),
+ uri.GetPath()
+ );
+}
+
+TEST(UriTests, ParseFromStringUrnMultiCharacterPathDelimiter) {
+ Uri::Uri uri;
+ uri.SetPathDelimiter("/-");
+ ASSERT_TRUE(uri.ParseFromString("urn:bo-/ok/-fant/asy/-Hob-bit"));
+ ASSERT_EQ("urn", uri.GetScheme());
+ ASSERT_EQ("", uri.GetHost());
+ ASSERT_EQ(
+ (std::vector< std::string >{
+ "bo-/ok",
+ "fant/asy",
+ "Hob-bit",
+ }),
+ uri.GetPath()
+ );
+}
+
+TEST(UriTests, ParseFromStringPathCornerCases) {
+ struct TestVector {
+ std::string pathIn;
+ std::vector< std::string > pathOut;
+ };
+ const std::vector< TestVector > testVectors{
+ {"", {}},
+ {"/", {""}},
+ {"/foo", {"", "foo"} },
+ {"foo/", {"foo", ""} },
+ };
+ size_t index = 0;
+ for (const auto& testVector : testVectors) {
+ Uri::Uri uri;
+ ASSERT_TRUE(uri.ParseFromString(testVector.pathIn)) << index;
+ ASSERT_EQ(testVector.pathOut, uri.GetPath()) << index;
+ ++index;
+ }
}