• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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() const24 std::vector<absl::string_view> RelativePath::Segments() const {
25   return absl::StrSplit(this->path_, '/', absl::SkipWhitespace());
26 }
27 
IsDirectory() const28 bool RelativePath::IsDirectory() const {
29   return absl::EndsWith(this->path_, "/");
30 }
31 
Relative(const RelativePath & dest) const32 std::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