1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2023 Google LLC. All rights reserved. 3 // 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file or at 6 // https://developers.google.com/open-source/licenses/bsd 7 8 #include "google/protobuf/compiler/rust/relative_path.h" 9 10 #include <string> 11 #include <vector> 12 13 #include "absl/algorithm/container.h" 14 #include "absl/strings/match.h" 15 #include "absl/strings/str_join.h" 16 #include "absl/strings/str_split.h" 17 #include "absl/strings/string_view.h" 18 19 namespace google { 20 namespace protobuf { 21 namespace compiler { 22 namespace rust { 23 Segments() const24std::vector<absl::string_view> RelativePath::Segments() const { 25 return absl::StrSplit(this->path_, '/', absl::SkipWhitespace()); 26 } 27 IsDirectory() const28bool RelativePath::IsDirectory() const { 29 return absl::EndsWith(this->path_, "/"); 30 } 31 Relative(const RelativePath & dest) const32std::string RelativePath::Relative(const RelativePath& dest) const { 33 ABSL_CHECK(!dest.IsDirectory()) 34 << "`dest` has to be a file path, but is a directory."; 35 std::vector<absl::string_view> current_segments = this->Segments(); 36 37 if (!current_segments.empty() && !this->IsDirectory()) { 38 // `this` represents a file path, skip the last segment to get its 39 // directory. 40 current_segments.pop_back(); 41 } 42 43 std::vector<absl::string_view> dest_segments = dest.Segments(); 44 45 // Find the lowest common ancestor. 46 absl::c_reverse(current_segments); 47 absl::c_reverse(dest_segments); 48 while (true) { 49 if (current_segments.empty()) break; 50 if (dest_segments.empty()) break; 51 if (current_segments.back() != dest_segments.back()) break; 52 53 current_segments.pop_back(); 54 dest_segments.pop_back(); 55 } 56 57 // Construct the relative path in reverse order. 58 std::vector<absl::string_view> result; 59 result.reserve(current_segments.size() + dest_segments.size()); 60 // Push the segments from the `dest` to the common ancestor. 61 for (const auto& segment : dest_segments) { 62 result.push_back(segment); 63 } 64 // Push `..` from the common ancestor to the current path. 65 for (int i = 0; i < current_segments.size(); ++i) { 66 result.push_back(".."); 67 } 68 absl::c_reverse(result); 69 if (dest.IsDirectory()) { 70 // Convince the `StrJoin` below to add a trailing `/`. 71 result.push_back(""); 72 } 73 return absl::StrJoin(result, "/"); 74 } 75 76 } // namespace rust 77 } // namespace compiler 78 } // namespace protobuf 79 } // namespace google 80