sapling/eden/fs/inodes/GlobNode.h
Wez Furlong 2e43c3b76d move GlobNode -> inodes dir
Summary:
This makes it easier to add some test coverage.

There's no real functional change in this diff; the only code change is to
throw a system_error instead of a thrift eden error wrapper class from the core
globbing code.  There's a little bit of code to restore this exception type in
the callers in EdenServiceHandler; this is covered by existing integration
tests, but I've also expanded that coverage to cover both variants of the glob
thrift calls.

Reviewed By: strager

Differential Revision: D8776767

fbshipit-source-id: 3ea4ea642ae5108aa4b0153541bd3604f010b54c
2018-07-13 11:22:19 -07:00

132 lines
5.1 KiB
C++

/*
* Copyright (c) 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#pragma once
#include <folly/futures/Future.h>
#include "eden/fs/inodes/InodePtrFwd.h"
#include "eden/fs/model/Hash.h"
#include "eden/fs/model/Tree.h"
#include "eden/fs/model/git/GlobMatcher.h"
#include "eden/fs/store/ObjectStore.h"
#include "eden/fs/utils/PathFuncs.h"
namespace facebook {
namespace eden {
/** Represents the compiled state of a tree-walking glob operation.
* We split the glob into path components and build a tree of name
* matching operations.
* For non-recursive globs this allows an efficient walk and compare
* as we work through the tree. Path components that have no glob
* special characters can be looked up directly from the directory
* contents as a hash lookup, rather than by repeatedly matching the
* pattern against each entry.
*/
class GlobNode {
public:
// Single parameter constructor is intended to create the root of a set of
// globs that will be parsed into the overall glob tree.
explicit GlobNode(bool includeDotfiles) : includeDotfiles_(includeDotfiles) {}
using PrefetchList = std::shared_ptr<folly::Synchronized<std::vector<Hash>>>;
GlobNode(folly::StringPiece pattern, bool includeDotfiles, bool hasSpecials);
// Compile and add a new glob pattern to the tree.
// Compilation splits the pattern into nodes, with one node for each
// directory separator separated path component.
void parse(folly::StringPiece pattern);
// This is a recursive function to evaluate the compiled glob against
// the provided input path and inode.
// It returns the set of matching file names.
// Note: the caller is responsible for ensuring that this
// GlobNode exists until the returned Future is resolved.
// If prefetchFiles is true, each matching file will have its content
// prefetched via the ObjectStore layer. This will not change the
// materialization or overlay state for children that already have
// inodes assigned.
folly::Future<std::vector<RelativePath>> evaluate(
const ObjectStore* store,
RelativePathPiece rootPath,
TreeInodePtr root,
PrefetchList fileBlobsToPrefetch);
// This is the Tree version of the method above
folly::Future<std::vector<RelativePath>> evaluate(
const ObjectStore* store,
RelativePathPiece rootPath,
const std::shared_ptr<const Tree>& tree,
PrefetchList fileBlobsToPrefetch);
private:
// Returns the next glob node token.
// This is the text from the start of pattern up to the first
// slash, or the end of the string is there was no slash.
// pattern is advanced to the start of the next token.
// hasSpecials is set to true if the returned token contains
// any special glob characters, false otherwise.
static folly::StringPiece tokenize(
folly::StringPiece& pattern,
bool* hasSpecials);
// Look up the child corresponding to a token.
// Returns nullptr if it does not exist.
// This is a simple brute force walk of the vector; the cardinality
// of the glob nodes are typically very low so this is fine.
GlobNode* lookupToken(
std::vector<std::unique_ptr<GlobNode>>* container,
folly::StringPiece token);
// Evaluates any recursive glob entries associated with this node.
// This is a recursive function which evaluates the current GlobNode against
// the recursive set of children.
// By contrast, evaluate() walks down through the GlobNodes AND the
// inode children.
// The difference is because a pattern like "**/foo" must be recursively
// matched against all the children of the inode.
template <typename ROOT>
folly::Future<std::vector<RelativePath>> evaluateRecursiveComponentImpl(
const ObjectStore* store,
RelativePathPiece rootPath,
ROOT&& root,
PrefetchList fileBlobsToPrefetch);
template <typename ROOT>
folly::Future<std::vector<RelativePath>> evaluateImpl(
const ObjectStore* store,
RelativePathPiece rootPath,
ROOT&& root,
PrefetchList fileBlobsToPrefetch);
// The pattern fragment for this node
std::string pattern_;
// The compiled pattern
GlobMatcher matcher_;
// List of non-** child rules
std::vector<std::unique_ptr<GlobNode>> children_;
// List of ** child rules
std::vector<std::unique_ptr<GlobNode>> recursiveChildren_;
// For a child GlobNode that is added to this GlobNode (presumably via
// parse()), the GlobMatcher pattern associated with the child node should use
// this value for its includeDotfiles parameter.
bool includeDotfiles_;
// If true, generate results for matches. Only applies
// to non-recursive glob patterns.
bool isLeaf_{false};
// If false we can try a name lookup of pattern rather
// than walking the children and applying the matcher
bool hasSpecials_{false};
// true when both of the following hold:
// - this node is "**" or "*"
// - it was created with includeDotfiles=true.
bool alwaysMatch_{false};
};
} // namespace eden
} // namespace facebook