blob: 7c083f554bbae83c7d17dae268b5b9215955c223 [file] [log] [blame]
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "google/protobuf/compiler/rust/relative_path.h"
#include <iostream>
#include <string>
#include <vector>
#include "absl/algorithm/container.h"
#include "absl/strings/match.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace rust {
std::vector<absl::string_view> RelativePath::Segments() const {
return absl::StrSplit(this->path_, '/', absl::SkipWhitespace());
}
bool RelativePath::IsDirectory() const {
return absl::EndsWith(this->path_, "/");
}
std::string RelativePath::Relative(const RelativePath& dest) const {
ABSL_CHECK(!dest.IsDirectory())
<< "`dest` has to be a file path, but is a directory.";
std::vector<absl::string_view> current_segments = this->Segments();
if (!current_segments.empty() && !this->IsDirectory()) {
// `this` represents a file path, skip the last segment to get its
// directory.
current_segments.pop_back();
}
std::vector<absl::string_view> dest_segments = dest.Segments();
// Find the lowest common ancestor.
absl::c_reverse(current_segments);
absl::c_reverse(dest_segments);
while (true) {
if (current_segments.empty()) break;
if (dest_segments.empty()) break;
if (current_segments.back() != dest_segments.back()) break;
current_segments.pop_back();
dest_segments.pop_back();
}
// Construct the relative path in reverse order.
std::vector<absl::string_view> result;
result.reserve(current_segments.size() + dest_segments.size());
// Push the segments from the `dest` to the common ancestor.
for (const auto& segment : dest_segments) {
result.push_back(segment);
}
// Push `..` from the common ancestor to the current path.
for (int i = 0; i < current_segments.size(); ++i) {
result.push_back("..");
}
absl::c_reverse(result);
if (dest.IsDirectory()) {
// Convince the `StrJoin` below to add a trailing `/`.
result.push_back("");
}
return absl::StrJoin(result, "/");
}
} // namespace rust
} // namespace compiler
} // namespace protobuf
} // namespace google