@ 261106.2
current
#ifndef SRC_GLOB_GLOB_WALKER_H_
#define SRC_GLOB_GLOB_WALKER_H_

#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#include <cstdint>
#include <limits>
#include <memory>
#include <string>
#include <string_view>
#include <vector>

#include "glob/glob_program.h"
#include "uv.h"

namespace node {
class Environment;
}  // namespace node

namespace node::glob {

class ExcludeFilter {
 public:
  virtual ~ExcludeFilter() = default;
  // A path relative to the walk's cwd.
  virtual bool ExcludesPath(std::string_view path) = 0;
  // A directory entry: its name, the absolute path of the directory holding
  // it, and its uv_dirent_type_t.
  virtual bool ExcludesEntry(std::string_view name,
                             std::string_view parent_path,
                             int type) = 0;
  // Set once a call has thrown. The walk stops and the exception is left
  // for the caller to propagate.
  virtual bool failed() const = 0;
};

struct WalkOptions {
  // options.cwd, or ".": the string paths are resolved against.
  std::string cwd = ".";
  bool follow_symlinks = false;
  bool with_file_types = false;
  // options.maxDepth: how many directory levels below cwd a result may
  // sit at (cwd itself is 0), or kNoDepthLimit.
  static constexpr size_t kNoDepthLimit = std::numeric_limits<size_t>::max();
  size_t max_depth = kNoDepthLimit;

  ExcludeFilter* exclude_filter = nullptr;
};

struct WalkEntry {
  std::string path;
  int type = UV_DIRENT_UNKNOWN;
};

// One call a walk off the main thread needs made to the exclude callback,
// which only that thread can do. A directory's questions are recorded in
// the order the callback would see them, together with what decides
// whether each is asked at all, so the main thread can put exactly the
// calls the walk on that thread would have made.
struct Question {
  static constexpr size_t kNoEntry = SIZE_MAX;
  // ExcludesEntry(first, parent, type) when set, else ExcludesPath(first)
  bool entry;
  std::string first;
  std::string parent;
  int type;
  // The listing entry the question belongs to; kNoEntry for none
  size_t entry_id;
  // For an entry question: whether "not excluded" makes the walk descend
  // into the entry, which raises the entry's path question. For a path
  // question: whether the walk descends regardless of the entry's answers.
  bool descends;
};

struct Answer {
  bool entry;
  bool excluded;
};

class WalkerImpl;

class Walk {
 public:
  Walk(Environment* env,
       const WalkOptions& options,
       const std::vector<CompiledPatternPtr>& includes,
       const std::vector<CompiledPatternPtr>& excludes);
  ~Walk();

  // Advances the traversal until at least `max_results` new results are
  // available or the walk is over
  bool RunSlice(size_t max_results,
                bool on_main_thread,
                std::vector<WalkEntry>* out);

  // A slice that stopped because a directory needs the exclude callback:
  // the questions for the main thread, then the answers to resume with.
  bool HasQuestions() const;
  const std::vector<Question>& questions() const;
  void SetAnswers(std::vector<Answer> answers);

  // Paths the permission model denied off the main thread since the last
  // call, for publishing from that thread
  void TakeDenied(std::vector<std::string>* out);

  // Stops background scanning and joins the walk's threads. Idempotent;
  // safe while a slice is in flight (it will wind down promptly). Called
  // when the walk finishes or is cancelled so threads never wait for
  // garbage collection.
  void Stop();

 private:
  std::unique_ptr<WalkerImpl> impl_;
};

// Runs a walk to completion.
void GlobSync(Environment* env,
              const WalkOptions& options,
              const std::vector<CompiledPatternPtr>& includes,
              const std::vector<CompiledPatternPtr>& excludes,
              std::vector<WalkEntry>* out);

}  // namespace node::glob

#endif  // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#endif  // SRC_GLOB_GLOB_WALKER_H_