mirror of
https://github.com/ModOrganizer2/modorganizer-uibase.git
synced 2026-07-27 14:03:49 -07:00
Move glob() and walk() generator versions to standalone functions. (#173)
This commit is contained in:
@@ -29,6 +29,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <version>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QFlags>
|
||||
@@ -112,14 +113,6 @@ struct QDLLEXPORT UnsupportedOperationException : public Exception
|
||||
using Exception::Exception;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Exception thrown when an invalid glob pattern is specified.
|
||||
*/
|
||||
struct QDLLEXPORT InvalidGlobPatternException : public Exception
|
||||
{
|
||||
using Exception::Exception;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represent an entry in a file tree, either a file or a directory. This class
|
||||
* inherited by IFileTree so that operations on entry are the same for a file or
|
||||
@@ -631,20 +624,6 @@ public: // Walk & Glob operations
|
||||
|
||||
};
|
||||
|
||||
enum class GlobPatternType
|
||||
{
|
||||
/**
|
||||
* @brief Glob mode, similar to python pathlib.Path.glob function
|
||||
*/
|
||||
GLOB,
|
||||
|
||||
/**
|
||||
* @brief Regex mode, each part of the pattern (between / or \) is considered a
|
||||
* regex, except for ** which is still considered as glob.
|
||||
*/
|
||||
REGEX
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Walk this tree, calling the given function for each entry in it.
|
||||
*
|
||||
@@ -662,27 +641,6 @@ public: // Walk & Glob operations
|
||||
callback,
|
||||
QString sep = "\\") const;
|
||||
|
||||
/**
|
||||
* @brief Walk this tree, returning entries.
|
||||
*
|
||||
* During the walk, parent tree are guaranteed to be visited before their childrens.
|
||||
* The current tree is not included in the return generator.
|
||||
*
|
||||
* @return a generator over the entries.
|
||||
*/
|
||||
std::generator<std::shared_ptr<const FileTreeEntry>> walk() const;
|
||||
|
||||
/**
|
||||
* @brief Glob entries matching the given pattern in this tree.
|
||||
*
|
||||
* @param pattern Glob pattern to match, using the same syntax as QRegularExpression.
|
||||
* @param patternType Type of the pattern.
|
||||
*
|
||||
* @return a generator over the entries matching the given pattern.
|
||||
*/
|
||||
std::generator<std::shared_ptr<const FileTreeEntry>>
|
||||
glob(QString pattern, GlobPatternType patternType = GlobPatternType::GLOB) const;
|
||||
|
||||
public: // Utility functions:
|
||||
/**
|
||||
* @brief Create a new orphan empty tree.
|
||||
@@ -1161,4 +1119,12 @@ private: // Private stuff, look away!
|
||||
|
||||
} // namespace MOBase
|
||||
|
||||
// __has_cpp_attribute(__cpp_lib_generator) does not seem to work, maybe some conflict
|
||||
// with Qt?
|
||||
#ifdef __cpp_lib_generator
|
||||
|
||||
#include "ifiletree_utils.h"
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
Mod Organizer shared UI functionality
|
||||
|
||||
Copyright (C) 2020 MO2 Team. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef UIBASE_IFILETREE_UTILS_H
|
||||
#define UIBASE_IFILETREE_UTILS_H
|
||||
|
||||
#include <generator>
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "dllimport.h"
|
||||
#include "exceptions.h"
|
||||
#include "ifiletree.h"
|
||||
|
||||
namespace MOBase
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Exception thrown when an invalid glob pattern is specified.
|
||||
*/
|
||||
struct QDLLEXPORT InvalidGlobPatternException : public Exception
|
||||
{
|
||||
using Exception::Exception;
|
||||
};
|
||||
|
||||
enum class GlobPatternType
|
||||
{
|
||||
/**
|
||||
* @brief Glob mode, similar to python pathlib.Path.glob function
|
||||
*/
|
||||
GLOB,
|
||||
|
||||
/**
|
||||
* @brief Regex mode, each part of the pattern (between / or \) is considered a
|
||||
* regex, except for ** which is still considered as glob.
|
||||
*/
|
||||
REGEX
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Walk this tree, returning entries.
|
||||
*
|
||||
* During the walk, parent tree are guaranteed to be visited before their childrens.
|
||||
* The current tree is not included in the return generator.
|
||||
*
|
||||
* @return a generator over the entries.
|
||||
*/
|
||||
QDLLEXPORT std::generator<std::shared_ptr<const FileTreeEntry>>
|
||||
walk(std::shared_ptr<const IFileTree> fileTree);
|
||||
|
||||
/**
|
||||
* @brief Glob entries matching the given pattern in this tree.
|
||||
*
|
||||
* @param pattern Glob pattern to match, using the same syntax as QRegularExpression.
|
||||
* @param patternType Type of the pattern.
|
||||
*
|
||||
* @return a generator over the entries matching the given pattern.
|
||||
*/
|
||||
QDLLEXPORT std::generator<std::shared_ptr<const FileTreeEntry>>
|
||||
glob(std::shared_ptr<const IFileTree> fileTree, QString pattern,
|
||||
GlobPatternType patternType = GlobPatternType::GLOB);
|
||||
|
||||
} // namespace MOBase
|
||||
|
||||
#endif
|
||||
@@ -35,6 +35,7 @@ set(root_headers
|
||||
)
|
||||
set(interface_headers
|
||||
../include/uibase/ifiletree.h
|
||||
../include/uibase/ifiletree_utils.h
|
||||
../include/uibase/iinstallationmanager.h
|
||||
../include/uibase/imodinterface.h
|
||||
../include/uibase/imodlist.h
|
||||
|
||||
+174
-178
@@ -180,184 +180,6 @@ void IFileTree::walk(
|
||||
}
|
||||
}
|
||||
|
||||
std::generator<std::shared_ptr<const FileTreeEntry>> IFileTree::walk() const
|
||||
{
|
||||
std::stack<std::shared_ptr<const FileTreeEntry>> stack;
|
||||
|
||||
// we start by pushing all the entries in this tree, this avoid having to do extra
|
||||
// check later to avoid leading separator
|
||||
for (const auto& entry : entries() | std::views::reverse) {
|
||||
stack.push(entry);
|
||||
}
|
||||
|
||||
while (!stack.empty()) {
|
||||
auto entry = stack.top();
|
||||
stack.pop();
|
||||
|
||||
co_yield entry;
|
||||
|
||||
if (entry->isDir()) {
|
||||
auto tree = entry->astree();
|
||||
for (const auto& child : tree->entries() | std::views::reverse) {
|
||||
stack.push(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
using glob_stack_t = std::stack<
|
||||
std::pair<std::shared_ptr<const FileTreeEntry>, std::span<QRegularExpression>>>;
|
||||
|
||||
// check the given entry against the list of patterns and add new (entry, patterns) to
|
||||
// the given stack
|
||||
//
|
||||
std::generator<std::shared_ptr<const FileTreeEntry>>
|
||||
ifiletree_glob_impl_step(glob_stack_t& glob_stack,
|
||||
std::shared_ptr<const FileTreeEntry> entry,
|
||||
std::span<QRegularExpression> const& patterns)
|
||||
{
|
||||
// no more patterns, nothing to do
|
||||
if (patterns.size() == 0) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
// special handling if the pattern is empty (correspond to a '**' glob)
|
||||
if (patterns[0].pattern() == "") {
|
||||
|
||||
// if there are more patterns after '**', we need to check the entry again, e.g.,
|
||||
// if the entry name is 'x' and the pattern is '**/x' to match it
|
||||
if (patterns.size() != 1) {
|
||||
glob_stack.emplace(entry, patterns.subspan(1));
|
||||
}
|
||||
|
||||
// if the entry is a file, there is nothing to do with '**'
|
||||
if (entry->isFile()) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
// if this is the end of the patterns list, we need to yield the current entry
|
||||
// since it is a directory
|
||||
if (patterns.size() == 1) {
|
||||
co_yield entry;
|
||||
}
|
||||
|
||||
// recurse over childs, but for directories, we need to keep the leading '**' in
|
||||
// the list of patterns since '**' can match multiple level of directories
|
||||
auto tree = entry->astree();
|
||||
for (auto rit = tree->rbegin(); rit != tree->rend(); ++rit) {
|
||||
glob_stack.emplace(*rit, (*rit)->isDir() ? patterns : patterns.subspan(1));
|
||||
}
|
||||
}
|
||||
// otherwise (if the first patterns is not '**'), we simply check if we have a match
|
||||
else if (patterns[0].match(entry->name()).hasMatch()) {
|
||||
// this was the last pattern and we have a match, so we yield the current entry,
|
||||
// not that this will yield intermediate matching directory, but this is expected
|
||||
if (patterns.size() == 1) {
|
||||
co_yield entry;
|
||||
}
|
||||
|
||||
// if the entry is not a directory, we have nothing more to do
|
||||
if (entry->isFile()) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
// if all that remain after this pattern is a '**', we need to yield the current
|
||||
// entry since '**' can also match an empty succession of directories, e.g. 'a/b'
|
||||
// is matched by 'a/b/**'
|
||||
if (patterns.size() == 2 && patterns[1].pattern() == "") {
|
||||
co_yield entry;
|
||||
}
|
||||
|
||||
// we then need to recurse over
|
||||
auto tree = entry->astree();
|
||||
for (auto rit = tree->rbegin(); rit != tree->rend(); ++rit) {
|
||||
glob_stack.emplace(*rit, patterns.subspan(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// main function for IFileTree::glob - this function simply manages the stack of of
|
||||
// entry / patterns to handle, and then falls back to ifiletree_glob_impl_step
|
||||
//
|
||||
std::generator<std::shared_ptr<const FileTreeEntry>>
|
||||
ifiletree_glob_impl(std::shared_ptr<const FileTreeEntry> entry,
|
||||
std::span<QRegularExpression> const& patterns)
|
||||
{
|
||||
glob_stack_t stack;
|
||||
stack.emplace(entry, patterns);
|
||||
|
||||
while (!stack.empty()) {
|
||||
const auto [entry, patterns] = stack.top();
|
||||
stack.pop();
|
||||
co_yield std::ranges::elements_of(
|
||||
ifiletree_glob_impl_step(stack, entry, patterns));
|
||||
}
|
||||
}
|
||||
|
||||
// actual implementation for IFileTree::glob
|
||||
//
|
||||
std::generator<std::shared_ptr<const FileTreeEntry>>
|
||||
ifiletree_glob_impl(std::shared_ptr<const IFileTree> tree, QString pattern,
|
||||
IFileTree::GlobPatternType patternType)
|
||||
{
|
||||
// replace \\ by / to simply handling
|
||||
pattern = pattern.trimmed().replace("\\", "/");
|
||||
|
||||
// reduce successions of **/** to **, this makes it easier to handle it in the
|
||||
// actual implementation
|
||||
pattern = pattern.replace(QRegularExpression("(\\*\\*/)*\\*\\*"), "**");
|
||||
|
||||
// we are going to match directly starting from the child of the tree, so we need
|
||||
// to handle the root here
|
||||
//
|
||||
// note: this is the only pattern that can match the tree itself
|
||||
if (pattern == "**") {
|
||||
co_yield tree;
|
||||
}
|
||||
|
||||
// split pattern into blocks, we keep
|
||||
const auto regexOptions = FileNameComparator::CaseSensitivity == Qt::CaseInsensitive
|
||||
? QRegularExpression::CaseInsensitiveOption
|
||||
: QRegularExpression::NoPatternOption;
|
||||
std::vector<QRegularExpression> patterns;
|
||||
for (const auto& part : pattern.split("/")) {
|
||||
if (part == "**") {
|
||||
// for '**' pattern, push an empty regex
|
||||
patterns.emplace_back();
|
||||
} else {
|
||||
const auto pattern =
|
||||
patternType == IFileTree::GlobPatternType::GLOB
|
||||
? QRegularExpression::wildcardToRegularExpression(
|
||||
part, QRegularExpression::NonPathWildcardConversion)
|
||||
: part;
|
||||
const QRegularExpression regex(pattern, regexOptions);
|
||||
|
||||
if (!regex.isValid()) {
|
||||
throw InvalidGlobPatternException(regex.errorString());
|
||||
}
|
||||
|
||||
patterns.push_back(regex);
|
||||
}
|
||||
}
|
||||
|
||||
// fall back to the main glob function for the child of this tree
|
||||
for (const auto& child : *tree) {
|
||||
co_yield std::ranges::elements_of(ifiletree_glob_impl(child, patterns));
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
std::generator<std::shared_ptr<const FileTreeEntry>>
|
||||
IFileTree::glob(QString pattern, GlobPatternType patternType) const
|
||||
{
|
||||
return ifiletree_glob_impl(astree(), pattern, patternType);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -1030,4 +852,178 @@ void IFileTree::populate() const
|
||||
}
|
||||
}
|
||||
|
||||
// walk and glob with generator
|
||||
|
||||
std::generator<std::shared_ptr<const FileTreeEntry>>
|
||||
walk(std::shared_ptr<const IFileTree> fileTree)
|
||||
{
|
||||
std::stack<std::shared_ptr<const FileTreeEntry>> stack;
|
||||
|
||||
// we start by pushing all the entries in this tree, this avoid having to do extra
|
||||
// check later to avoid leading separator
|
||||
for (auto rit = fileTree->rbegin(); rit != fileTree->rend(); ++rit) {
|
||||
stack.push(*rit);
|
||||
}
|
||||
|
||||
while (!stack.empty()) {
|
||||
auto entry = stack.top();
|
||||
stack.pop();
|
||||
|
||||
co_yield entry;
|
||||
|
||||
if (entry->isDir()) {
|
||||
auto tree = entry->astree();
|
||||
for (auto rit = tree->rbegin(); rit != tree->rend(); ++rit) {
|
||||
stack.push(*rit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
using glob_stack_t = std::stack<
|
||||
std::pair<std::shared_ptr<const FileTreeEntry>, std::span<QRegularExpression>>>;
|
||||
|
||||
// check the given entry against the list of patterns and add new (entry, patterns) to
|
||||
// the given stack
|
||||
//
|
||||
std::generator<std::shared_ptr<const FileTreeEntry>>
|
||||
ifiletree_glob_impl_step(glob_stack_t& glob_stack,
|
||||
std::shared_ptr<const FileTreeEntry> entry,
|
||||
std::span<QRegularExpression> const& patterns)
|
||||
{
|
||||
// no more patterns, nothing to do
|
||||
if (patterns.size() == 0) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
// special handling if the pattern is empty (correspond to a '**' glob)
|
||||
if (patterns[0].pattern() == "") {
|
||||
|
||||
// if there are more patterns after '**', we need to check the entry again, e.g.,
|
||||
// if the entry name is 'x' and the pattern is '**/x' to match it
|
||||
if (patterns.size() != 1) {
|
||||
glob_stack.emplace(entry, patterns.subspan(1));
|
||||
}
|
||||
|
||||
// if the entry is a file, there is nothing to do with '**'
|
||||
if (entry->isFile()) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
// if this is the end of the patterns list, we need to yield the current entry
|
||||
// since it is a directory
|
||||
if (patterns.size() == 1) {
|
||||
co_yield entry;
|
||||
}
|
||||
|
||||
// recurse over childs, but for directories, we need to keep the leading '**' in
|
||||
// the list of patterns since '**' can match multiple level of directories
|
||||
auto tree = entry->astree();
|
||||
for (auto rit = tree->rbegin(); rit != tree->rend(); ++rit) {
|
||||
glob_stack.emplace(*rit, (*rit)->isDir() ? patterns : patterns.subspan(1));
|
||||
}
|
||||
}
|
||||
// otherwise (if the first patterns is not '**'), we simply check if we have a match
|
||||
else if (patterns[0].match(entry->name()).hasMatch()) {
|
||||
// this was the last pattern and we have a match, so we yield the current entry,
|
||||
// not that this will yield intermediate matching directory, but this is expected
|
||||
if (patterns.size() == 1) {
|
||||
co_yield entry;
|
||||
}
|
||||
|
||||
// if the entry is not a directory, we have nothing more to do
|
||||
if (entry->isFile()) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
// if all that remain after this pattern is a '**', we need to yield the current
|
||||
// entry since '**' can also match an empty succession of directories, e.g. 'a/b'
|
||||
// is matched by 'a/b/**'
|
||||
if (patterns.size() == 2 && patterns[1].pattern() == "") {
|
||||
co_yield entry;
|
||||
}
|
||||
|
||||
// we then need to recurse over
|
||||
auto tree = entry->astree();
|
||||
for (auto rit = tree->rbegin(); rit != tree->rend(); ++rit) {
|
||||
glob_stack.emplace(*rit, patterns.subspan(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// main function for IFileTree::glob - this function simply manages the stack of of
|
||||
// entry / patterns to handle, and then falls back to ifiletree_glob_impl_step
|
||||
//
|
||||
std::generator<std::shared_ptr<const FileTreeEntry>>
|
||||
ifiletree_glob_impl(std::shared_ptr<const FileTreeEntry> entry,
|
||||
std::span<QRegularExpression> const& patterns)
|
||||
{
|
||||
glob_stack_t stack;
|
||||
stack.emplace(entry, patterns);
|
||||
|
||||
while (!stack.empty()) {
|
||||
const auto [childEntry, childPatterns] = stack.top();
|
||||
stack.pop();
|
||||
co_yield std::ranges::elements_of(
|
||||
ifiletree_glob_impl_step(stack, childEntry, childPatterns));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
std::generator<std::shared_ptr<const FileTreeEntry>>
|
||||
glob(std::shared_ptr<const IFileTree> fileTree, QString pattern,
|
||||
GlobPatternType patternType)
|
||||
{
|
||||
// replace \\ by / to simply handling
|
||||
pattern = pattern.trimmed().replace("\\", "/");
|
||||
|
||||
// reduce successions of **/** to **, this makes it easier to handle it in the
|
||||
// actual implementation
|
||||
pattern = pattern.replace(QRegularExpression("(\\*\\*/)*\\*\\*"), "**");
|
||||
|
||||
// we are going to match directly starting from the child of the tree, so we need
|
||||
// to handle the root here
|
||||
//
|
||||
// note: this is the only pattern that can match the tree itself
|
||||
if (pattern == "**") {
|
||||
co_yield fileTree;
|
||||
}
|
||||
|
||||
// split pattern into blocks, we keep
|
||||
const auto regexOptions = FileNameComparator::CaseSensitivity == Qt::CaseInsensitive
|
||||
? QRegularExpression::CaseInsensitiveOption
|
||||
: QRegularExpression::NoPatternOption;
|
||||
std::vector<QRegularExpression> patterns;
|
||||
for (const auto& part : pattern.split("/")) {
|
||||
if (part == "**") {
|
||||
// for '**' pattern, push an empty regex
|
||||
patterns.emplace_back();
|
||||
} else {
|
||||
const auto regexpPattern =
|
||||
patternType == GlobPatternType::GLOB
|
||||
? QRegularExpression::wildcardToRegularExpression(
|
||||
part, QRegularExpression::NonPathWildcardConversion)
|
||||
: part;
|
||||
const QRegularExpression regex(regexpPattern, regexOptions);
|
||||
|
||||
if (!regex.isValid()) {
|
||||
throw InvalidGlobPatternException(regex.errorString());
|
||||
}
|
||||
|
||||
patterns.push_back(regex);
|
||||
}
|
||||
}
|
||||
|
||||
// fall back to the main glob function for the child of this tree
|
||||
for (const auto& child : *fileTree) {
|
||||
co_yield std::ranges::elements_of(ifiletree_glob_impl(child, patterns));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MOBase
|
||||
|
||||
+32
-32
@@ -890,14 +890,14 @@ TEST(IFileTreeTest, TreeWalkOperations)
|
||||
// same as above but with generator version
|
||||
{
|
||||
// Populate the vector:
|
||||
auto entries = fileTree->walk() | std::ranges::to<std::vector>();
|
||||
auto entries = walk(fileTree) | std::ranges::to<std::vector>();
|
||||
decltype(entries) expected{map["a"], map["b"], map["b/u"], map["b/v"],
|
||||
map["e"], map["e/q"], map["e/q/p"], map["e/q/c.t"],
|
||||
map["c.x"], map["d.y"]};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries.clear();
|
||||
for (const auto entry : fileTree->walk()) {
|
||||
for (const auto entry : walk(fileTree)) {
|
||||
if (entry->name() == "e") {
|
||||
break; // Stop on e
|
||||
}
|
||||
@@ -921,7 +921,7 @@ TEST(IFileTreeTest, TreeGlobOperations)
|
||||
{
|
||||
using entrySet = std::unordered_set<std::shared_ptr<const FileTreeEntry>>;
|
||||
|
||||
const auto REGEX = IFileTree::GlobPatternType::REGEX;
|
||||
const auto REGEX = GlobPatternType::REGEX;
|
||||
|
||||
{
|
||||
auto fileTree = FileListTree::makeTree({{"a/", true},
|
||||
@@ -939,61 +939,61 @@ TEST(IFileTreeTest, TreeGlobOperations)
|
||||
|
||||
entrySet entries, expected;
|
||||
|
||||
entries = fileTree->glob("*") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "*") | std::ranges::to<std::unordered_set>();
|
||||
expected = {map["a"], map["b"], map["c.x"], map["d.y"], map["e"]};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob(".*", REGEX) | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, ".*", REGEX) | std::ranges::to<std::unordered_set>();
|
||||
expected = {map["a"], map["b"], map["c.x"], map["d.y"], map["e"]};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("**") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "**") | std::ranges::to<std::unordered_set>();
|
||||
expected = {fileTree, map["a"], map["b"], map["e"], map["e/q"], map["e/q/p"]};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("**", REGEX) | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "**", REGEX) | std::ranges::to<std::unordered_set>();
|
||||
expected = {fileTree, map["a"], map["b"], map["e"], map["e/q"], map["e/q/p"]};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("*.x") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "*.x") | std::ranges::to<std::unordered_set>();
|
||||
expected = {map["c.x"]};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob(".*[.]x", REGEX) | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, ".*[.]x", REGEX) | std::ranges::to<std::unordered_set>();
|
||||
expected = {map["c.x"]};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("**/*.x") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "**/*.x") | std::ranges::to<std::unordered_set>();
|
||||
expected = {map["c.x"], map["e/q/m.x"]};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries =
|
||||
fileTree->glob("**/.*[.]x", REGEX) | std::ranges::to<std::unordered_set>();
|
||||
glob(fileTree, "**/.*[.]x", REGEX) | std::ranges::to<std::unordered_set>();
|
||||
expected = {map["c.x"], map["e/q/m.x"]};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("*.t") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "*.t") | std::ranges::to<std::unordered_set>();
|
||||
expected = {};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("**/*.t") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "**/*.t") | std::ranges::to<std::unordered_set>();
|
||||
expected = {map["a/g.t"], map["e/q/c.t"]};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("a/*") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "a/*") | std::ranges::to<std::unordered_set>();
|
||||
expected = {map["a/g.t"]};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("a/.*", REGEX) | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "a/.*", REGEX) | std::ranges::to<std::unordered_set>();
|
||||
expected = {map["a/g.t"]};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("**/*.[xt]") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "**/*.[xt]") | std::ranges::to<std::unordered_set>();
|
||||
expected = {map["c.x"], map["e/q/m.x"], map["a/g.t"], map["e/q/c.t"]};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries =
|
||||
fileTree->glob("**/.*[.][xt]", REGEX) | std::ranges::to<std::unordered_set>();
|
||||
glob(fileTree, "**/.*[.][xt]", REGEX) | std::ranges::to<std::unordered_set>();
|
||||
expected = {map["c.x"], map["e/q/m.x"], map["a/g.t"], map["e/q/c.t"]};
|
||||
EXPECT_EQ(entries, expected);
|
||||
}
|
||||
@@ -1050,17 +1050,17 @@ TEST(IFileTreeTest, TreeGlobOperations)
|
||||
|
||||
entrySet entries, expected;
|
||||
|
||||
entries = fileTree->glob("*.h") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "*.h") | std::ranges::to<std::unordered_set>();
|
||||
expected = {};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("*") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "*") | std::ranges::to<std::unordered_set>();
|
||||
expected = {map.at("aq.js"), map.at("bb"), map.at("cm.tx"), map.at("dp.js"),
|
||||
map.at("ev"), map.at("go.ya"), map.at("gw.md"), map.at("hh"),
|
||||
map.at("hl"), map.at("in"), map.at("mz"), map.at("sc")};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("*/*") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "*/*") | std::ranges::to<std::unordered_set>();
|
||||
expected = {
|
||||
map.at("bb/ce.cp"), map.at("bb/cm.tx"), map.at("bb/gw"), map.at("bb/iw.cp"),
|
||||
map.at("bb/js"), map.at("bb/px.cp"), map.at("hl/ds.in"), map.at("in/nu"),
|
||||
@@ -1069,20 +1069,20 @@ TEST(IFileTreeTest, TreeGlobOperations)
|
||||
map.at("sc/nd.o"), map.at("sc/nv.o"), map.at("sc/rv.ui"), map.at("sc/tv.h")};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("*/*/*") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "*/*/*") | std::ranges::to<std::unordered_set>();
|
||||
expected = {map.at("bb/gw/cp.qm"), map.at("bb/gw/hq.qm"), map.at("bb/gw/pu.ts"),
|
||||
map.at("bb/gw/tu.ts"), map.at("bb/js/cm.tx"), map.at("bb/js/co.cp"),
|
||||
map.at("in/nu/el.h"), map.at("in/nu/fj.h"), map.at("in/nu/lw"),
|
||||
map.at("in/nu/xx")};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("**") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "**") | std::ranges::to<std::unordered_set>();
|
||||
expected = {fileTree, map.at("bb"), map.at("bb/gw"), map.at("bb/js"),
|
||||
map.at("hl"), map.at("in"), map.at("in/nu"), map.at("in/nu/lw"),
|
||||
map.at("in/nu/xx"), map.at("mz"), map.at("sc")};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("**/*") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "**/*") | std::ranges::to<std::unordered_set>();
|
||||
expected = {map.at("aq.js"),
|
||||
map.at("bb"),
|
||||
map.at("cm.tx"),
|
||||
@@ -1131,41 +1131,41 @@ TEST(IFileTreeTest, TreeGlobOperations)
|
||||
map.at("sc/tv.h")};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("**/cm.tx") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "**/cm.tx") | std::ranges::to<std::unordered_set>();
|
||||
expected = {map.at("cm.tx"), map.at("bb/cm.tx"), map.at("bb/js/cm.tx"),
|
||||
map.at("sc/cm.tx")};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("**/sc/**/cm.tx") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "**/sc/**/cm.tx") | std::ranges::to<std::unordered_set>();
|
||||
expected = {map.at("sc/cm.tx")};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("**/sc") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "**/sc") | std::ranges::to<std::unordered_set>();
|
||||
expected = {map.at("sc")};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("in/**") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "in/**") | std::ranges::to<std::unordered_set>();
|
||||
expected = {map.at("in"), map.at("in/nu"), map.at("in/nu/lw"), map.at("in/nu/xx")};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("in/**/**") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "in/**/**") | std::ranges::to<std::unordered_set>();
|
||||
expected = {map.at("in"), map.at("in/nu"), map.at("in/nu/lw"), map.at("in/nu/xx")};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("in/*/*") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "in/*/*") | std::ranges::to<std::unordered_set>();
|
||||
expected = {map.at("in/nu/el.h"), map.at("in/nu/fj.h"), map.at("in/nu/lw"),
|
||||
map.at("in/nu/xx")};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("in/*/*.h") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "in/*/*.h") | std::ranges::to<std::unordered_set>();
|
||||
expected = {map.at("in/nu/el.h"), map.at("in/nu/fj.h")};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("sc/**/*.cp") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "sc/**/*.cp") | std::ranges::to<std::unordered_set>();
|
||||
expected = {map.at("sc/dr.cp"), map.at("sc/hh.cp"), map.at("sc/lr.cp")};
|
||||
EXPECT_EQ(entries, expected);
|
||||
|
||||
entries = fileTree->glob("sc/**/n*.o") | std::ranges::to<std::unordered_set>();
|
||||
entries = glob(fileTree, "sc/**/n*.o") | std::ranges::to<std::unordered_set>();
|
||||
expected = {map.at("sc/nd.o"), map.at("sc/nv.o")};
|
||||
EXPECT_EQ(entries, expected);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user