This is an automated email from the ASF dual-hosted git repository.

bennoe pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git

commit 12e4ee81599f1b696f501901d246002310dcb9c1
Author: Benjamin Bannier <[email protected]>
AuthorDate: Tue Jan 7 13:19:31 2020 +0100

    Added a stout function to compute relative paths.
    
    Review: https://reviews.apache.org/r/71882/
---
 3rdparty/stout/include/stout/path.hpp | 68 +++++++++++++++++++++++++++++++++++
 3rdparty/stout/tests/path_tests.cpp   | 24 +++++++++++++
 2 files changed, 92 insertions(+)

diff --git a/3rdparty/stout/include/stout/path.hpp 
b/3rdparty/stout/include/stout/path.hpp
index 06a8a55..14a0d4e 100644
--- a/3rdparty/stout/include/stout/path.hpp
+++ b/3rdparty/stout/include/stout/path.hpp
@@ -515,4 +515,72 @@ inline std::ostream& operator<<(
   return stream << path.string();
 }
 
+
+namespace path {
+
+// Compute path of `path` relative to `base`.
+inline Try<std::string> relative(
+    const std::string& path_,
+    const std::string& base_,
+    char path_separator = os::PATH_SEPARATOR)
+{
+  if (path::is_absolute(path_) != path::is_absolute(base_)) {
+    return Error(
+        "Relative paths can only be computed between paths which are either "
+        "both absolute or both relative");
+  }
+
+  // Normalize both `path` and `base`.
+  Try<std::string> normalized_path = path::normalize(path_);
+  Try<std::string> normalized_base = path::normalize(base_);
+
+  if (normalized_path.isError()) {
+    return normalized_path;
+  }
+
+  if (normalized_base.isError()) {
+    return normalized_base;
+  }
+
+  // If normalized `path` and `base` are identical return `.`.
+  if (*normalized_path == *normalized_base) {
+    return ".";
+  }
+
+  const Path path(*normalized_path);
+  const Path base(*normalized_base);
+
+  auto path_it = path.begin();
+  auto base_it = base.begin();
+
+  const auto path_end = path.end();
+  const auto base_end = base.end();
+
+  // Strip common path components.
+  for (; path_it != path_end && base_it != base_end && *path_it == *base_it;
+       ++path_it, ++base_it) {
+  }
+
+  std::vector<std::string> result;
+  result.reserve(
+      std::distance(base_it, base_end) + std::distance(path_it, path_end));
+
+  // If we have not fully consumed the range of `base` we need to go
+  // up from `path` to reach `base`. Insert ".." into the result.
+  if (base_it != base_end) {
+    for (; base_it != base_end; ++base_it) {
+      result.emplace_back("..");
+    }
+  }
+
+  // Add remaining path components to the result.
+  for (; path_it != path_end; ++path_it) {
+    result.emplace_back(*path_it);
+  }
+
+  return join(result, path_separator);
+}
+
+} // namespace path {
+
 #endif // __STOUT_PATH_HPP__
diff --git a/3rdparty/stout/tests/path_tests.cpp 
b/3rdparty/stout/tests/path_tests.cpp
index c5135a4..d418d1b 100644
--- a/3rdparty/stout/tests/path_tests.cpp
+++ b/3rdparty/stout/tests/path_tests.cpp
@@ -354,6 +354,30 @@ TEST(PathTest, IsAbsolute)
 }
 
 
+TEST(PathTest, Relative)
+{
+  // Check that relative paths can only be computed between paths
+  // which are either both absolute or both relative.
+  EXPECT_ERROR(path::relative("a", "/a"));
+  EXPECT_ERROR(path::relative("/a", "a"));
+
+  // Check that a path relative to itself is an empty path.
+  ASSERT_SOME_EQ(".", path::relative("/a/b/c", "/a/b/c"));
+  ASSERT_SOME_EQ(".", path::relative("a/b/c", "a/b/c"));
+
+  // Check for relative paths which do not require going up in the filesystem.
+  ASSERT_SOME_EQ("b", path::relative("/a/b", "/a"));
+  ASSERT_SOME_EQ("b", path::relative("a/b", "a"));
+
+  // Check for relative paths which do require going up in the filesystem.
+  ASSERT_SOME_EQ("../../d/e", path::relative("/a/d/e", "/a/b/c"));
+  ASSERT_SOME_EQ("../../d/e", path::relative("a/d/e", "a/b/c"));
+
+  // Check for behavior of not normalized paths.
+  ASSERT_SOME_EQ(".", path::relative("/a/./b", "/a/b"));
+}
+
+
 TEST(PathTest, Comparison)
 {
   EXPECT_TRUE(Path("a") == Path("a"));

Reply via email to