kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/cxx/common/path_utils.h (about)

     1  /*
     2   * Copyright 2018 The Kythe Authors. All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *   http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  #ifndef KYTHE_CXX_COMMON_PATH_UTILS_H_
    18  #define KYTHE_CXX_COMMON_PATH_UTILS_H_
    19  
    20  #include <memory>
    21  #include <optional>
    22  #include <string>
    23  #include <utility>
    24  #include <variant>
    25  #include <vector>
    26  
    27  #include "absl/container/flat_hash_map.h"
    28  #include "absl/status/statusor.h"
    29  #include "absl/strings/string_view.h"
    30  #include "absl/synchronization/mutex.h"
    31  #include "absl/types/span.h"
    32  #include "kythe/cxx/common/regex.h"
    33  
    34  namespace kythe {
    35  
    36  /// \brief PathCleaner relativizes paths against a root using CleanPath.
    37  class PathCleaner {
    38   public:
    39    /// \brief Creates a PathCleaner using the given root.
    40    /// \param root The root against which to relativize.
    41    ///             Will be cleaned and made an absolute path.
    42    static absl::StatusOr<PathCleaner> Create(absl::string_view root);
    43  
    44    /// \brief Transforms the cleaned, absolute version of `path` into a path
    45    ///        relative to the configured root.
    46    /// \return A path relative to root, if `path` is, else a cleaned `path` or an
    47    ///         error if the current directory cannot be determined.
    48    absl::StatusOr<std::string> Relativize(absl::string_view path) const;
    49  
    50   private:
    51    explicit PathCleaner(std::string root) : root_(std::move(root)) {}
    52  
    53    std::string root_;
    54  };
    55  
    56  /// \brief PathRealizer relativizes paths against a root using RealPath.
    57  class PathRealizer {
    58   public:
    59    /// \brief Creates a PathCleaner using the given root.
    60    /// \param root The root against which to relativize.
    61    ///             Will be resolved and made an absolute path.
    62    static absl::StatusOr<PathRealizer> Create(absl::string_view root);
    63  
    64    /// PathRealizer is copyable and movable.
    65    PathRealizer(const PathRealizer& other);
    66    PathRealizer& operator=(const PathRealizer& other);
    67    PathRealizer(PathRealizer&& other) = default;
    68    PathRealizer& operator=(PathRealizer&& other) = default;
    69  
    70    /// \brief Transforms the resolved, absolute version of `path` into a path
    71    ///        relative to the configured root.
    72    /// \return A path relative to root, if `path` is, else a resolved `path` or
    73    ///         an error if the path cannot be resolved.
    74    absl::StatusOr<std::string> Relativize(absl::string_view path) const;
    75  
    76   private:
    77    class PathCache {
    78     public:
    79      template <typename K, typename Fn>
    80      absl::StatusOr<std::string> FindOrInsert(K&& key, Fn&& make);
    81  
    82     private:
    83      absl::Mutex mu_;
    84      absl::flat_hash_map<std::string, absl::StatusOr<std::string>> cache_;
    85    };
    86  
    87    explicit PathRealizer(std::string root) : root_(std::move(root)) {}
    88  
    89    std::string root_;
    90    // A trivial lookup cache; if you're changing symlinks during the build you're
    91    // going to have a bad time.
    92    std::unique_ptr<PathCache> cache_ = std::make_unique<PathCache>();
    93  };
    94  
    95  /// \brief PathCanonicalizer relatives paths against a root.
    96  class PathCanonicalizer {
    97   public:
    98    enum class Policy {
    99      kCleanOnly = 0,       ///< Only clean paths when applying canonicalizer.
   100      kPreferRelative = 1,  ///< Use clean paths if real path is absolute.
   101      kPreferReal = 2,      ///< Use real path, except if errors.
   102    };
   103  
   104    /// \brief An entry to use when overriding the default policy.
   105    struct PathEntry {
   106      /// \brief The path pattern to override.
   107      std::variant<Regex, std::string> path;
   108  
   109      /// \brief The policy to apply to matching paths.
   110      Policy policy = Policy::kCleanOnly;
   111    };
   112  
   113    /// \brief Creates a new PathCanonicalizer with the given root and policy.
   114    /// \param root The root directory to use when relativizing paths.
   115    /// \param policy The default policy to apply.
   116    static absl::StatusOr<PathCanonicalizer> Create(
   117        absl::string_view root, Policy policy = Policy::kCleanOnly,
   118        absl::Span<const PathEntry> path_map = {});
   119  
   120    /// \brief Transforms the provided path into a relative path depending on
   121    ///        configured policy.
   122    absl::StatusOr<std::string> Relativize(absl::string_view path) const;
   123  
   124   private:
   125    explicit PathCanonicalizer(Policy policy, PathCleaner cleaner,
   126                               std::optional<PathRealizer> realizer,
   127                               RegexSet override_set,
   128                               std::vector<Policy> override_policy)
   129        : policy_(policy),
   130          cleaner_(std::move(cleaner)),
   131          realizer_(std::move(realizer)),
   132          override_set_(std::move(override_set)),
   133          override_policy_(std::move(override_policy)) {}
   134  
   135    absl::StatusOr<Policy> PolicyFor(absl::string_view path) const;
   136  
   137    Policy policy_;
   138    PathCleaner cleaner_;
   139    std::optional<PathRealizer> realizer_;
   140    RegexSet override_set_;
   141    std::vector<Policy> override_policy_;
   142  };
   143  
   144  /// \brief Parses a flag string as a PathCanonicalizer::Policy.
   145  ///
   146  /// This is an extension point for the Abseil Flags library to allow
   147  /// using PathCanonicalizer directly as a flag.
   148  bool AbslParseFlag(absl::string_view text, PathCanonicalizer::Policy* policy,
   149                     std::string* error);
   150  
   151  /// \brief Returns the flag string representation of PathCanonicalizer::Policy.
   152  ///
   153  /// This is an extension point for the Abseil Flags library to allow
   154  /// using PathCanonicalizer directly as a flag.
   155  std::string AbslUnparseFlag(PathCanonicalizer::Policy policy);
   156  
   157  /// \brief Parses a string into a PathCanonicalizer::Policy.
   158  ///
   159  /// Parses either integeral values of enumerators or lower-case,
   160  /// dash-separated names: "clean-only", "prefer-relative", "prefer-real".
   161  std::optional<PathCanonicalizer::Policy> ParseCanonicalizationPolicy(
   162      absl::string_view policy);
   163  
   164  /// \brief Parses a single PathEntry flag value as `<pattern>@<policy>`
   165  bool AbslParseFlag(absl::string_view text, PathCanonicalizer::PathEntry* entry,
   166                     std::string* error);
   167  
   168  /// \brief Returns the flag string representation of
   169  /// PathCanonicalizer::PathEntry.
   170  std::string AbslUnparseFlag(const PathCanonicalizer::PathEntry& entry);
   171  
   172  /// \brief Parses a space separated list of PathEntry flag values.
   173  bool AbslParseFlag(absl::string_view text,
   174                     std::vector<PathCanonicalizer::PathEntry>* entries,
   175                     std::string* error);
   176  
   177  /// \brief Returns the flag string representation of
   178  /// PathCanonicalizer::PathEntry.
   179  std::string AbslUnparseFlag(
   180      const std::vector<PathCanonicalizer::PathEntry>& entries);
   181  
   182  /// \brief Append path `b` to path `a`, cleaning and returning the result.
   183  std::string JoinPath(absl::string_view a, absl::string_view b);
   184  
   185  /// \brief Returns the part of the path before the final '/'.
   186  absl::string_view Dirname(absl::string_view path);
   187  
   188  /// \brief Returns the part of the path after the final '/'.
   189  absl::string_view Basename(absl::string_view path);
   190  
   191  /// \brief Collapse duplicate "/"s, resolve ".." and "." path elements, remove
   192  /// trailing "/".
   193  std::string CleanPath(absl::string_view input);
   194  
   195  // \brief Returns true if path is absolute.
   196  bool IsAbsolutePath(absl::string_view path);
   197  
   198  /// \brief Relativize `to_relativize` with respect to `relativize_against`.
   199  ///
   200  /// If `to_relativize` does not name a path that is a child of
   201  /// `relativize_against`, `RelativizePath` will return an absolute path
   202  /// (resolved against the current working directory).
   203  ///
   204  /// Note: arguments are assumed to be valid paths, but validity is not checked.
   205  ///
   206  /// \param to_relativize Relative or absolute path to a file.
   207  /// \param relativize_against Relative or absolute path to a directory.
   208  std::string RelativizePath(absl::string_view to_relativize,
   209                             absl::string_view relativize_against);
   210  
   211  /// \brief Convert `path` to an absolute path, eliminating `.` and `..`.
   212  /// \param path The path to convert.
   213  absl::StatusOr<std::string> MakeCleanAbsolutePath(absl::string_view path);
   214  
   215  /// \brief Returns the process's current working directory or an error.
   216  absl::StatusOr<std::string> GetCurrentDirectory();
   217  
   218  /// \brief Returns the result of resolving symbolic links.
   219  absl::StatusOr<std::string> RealPath(absl::string_view path);
   220  
   221  }  // namespace kythe
   222  
   223  #endif  // KYTHE_CXX_COMMON_PATH_UTILS_H_