126 lines
3.6 KiB
C++
126 lines
3.6 KiB
C++
#include "WorkspaceScanner.h"
|
|
|
|
#include <QDir>
|
|
#include <QFileInfo>
|
|
#include <QStringList>
|
|
#include <QVector>
|
|
|
|
namespace
|
|
{
|
|
struct PendingDirectory
|
|
{
|
|
QString path;
|
|
int depth = 0;
|
|
};
|
|
}
|
|
|
|
WorkspaceScanner::WorkspaceScanner(int maxDepth, int maxEntries)
|
|
: m_maxDepth(maxDepth)
|
|
, m_maxEntries(maxEntries)
|
|
{
|
|
}
|
|
|
|
QVector<WorkspaceFileInfo> WorkspaceScanner::scan(const QString &workspaceRoot, QString *errorMessage) const
|
|
{
|
|
if (!WorkspaceAccessPolicy::validateWorkspaceRoot(workspaceRoot, errorMessage))
|
|
{
|
|
return {};
|
|
}
|
|
|
|
const WorkspaceAccessPolicy policy(workspaceRoot);
|
|
QVector<WorkspaceFileInfo> files;
|
|
QVector<PendingDirectory> pending;
|
|
pending.append({policy.workspaceRoot(), 0});
|
|
|
|
while (!pending.isEmpty() && files.size() < m_maxEntries)
|
|
{
|
|
const PendingDirectory current = pending.takeLast();
|
|
QString directoryError;
|
|
if (!policy.canReadDirectory(current.path, &directoryError))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const QFileInfoList entries = QDir(current.path).entryInfoList(
|
|
QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden,
|
|
QDir::DirsFirst | QDir::Name);
|
|
|
|
for (const QFileInfo &entry : entries)
|
|
{
|
|
if (files.size() >= m_maxEntries)
|
|
{
|
|
break;
|
|
}
|
|
if (entry.isHidden())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const QString absolutePath = entry.absoluteFilePath();
|
|
if (entry.isDir())
|
|
{
|
|
if (shouldSkipDirectory(policy, absolutePath))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
files.append(fileInfoForPath(policy, absolutePath));
|
|
if (current.depth + 1 <= m_maxDepth)
|
|
{
|
|
pending.append({absolutePath, current.depth + 1});
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (entry.isFile())
|
|
{
|
|
if (policy.hasSymlinkSegment(absolutePath))
|
|
{
|
|
continue;
|
|
}
|
|
files.append(fileInfoForPath(policy, absolutePath));
|
|
}
|
|
}
|
|
}
|
|
|
|
return files;
|
|
}
|
|
|
|
bool WorkspaceScanner::shouldSkipDirectory(const WorkspaceAccessPolicy &policy, const QString &path) const
|
|
{
|
|
const QFileInfo info(path);
|
|
const QString name = info.fileName().toLower();
|
|
static const QStringList skipped = {
|
|
QStringLiteral(".git"),
|
|
QStringLiteral(".vs"),
|
|
QStringLiteral(".idea"),
|
|
QStringLiteral("build"),
|
|
QStringLiteral("cmake-build-debug"),
|
|
QStringLiteral("cmake-build-release"),
|
|
QStringLiteral("node_modules"),
|
|
QStringLiteral("dist"),
|
|
QStringLiteral("release"),
|
|
QStringLiteral("release_packages"),
|
|
QStringLiteral("logs"),
|
|
};
|
|
return skipped.contains(name)
|
|
|| policy.isSystemPath(path)
|
|
|| policy.hasSymlinkSegment(path);
|
|
}
|
|
|
|
WorkspaceFileInfo WorkspaceScanner::fileInfoForPath(const WorkspaceAccessPolicy &policy, const QString &path) const
|
|
{
|
|
const QFileInfo info(path);
|
|
WorkspaceFileInfo file;
|
|
file.absolutePath = policy.normalizePath(path);
|
|
file.relativePath = policy.toRelativePath(file.absolutePath);
|
|
file.fileName = info.fileName();
|
|
file.suffix = info.suffix();
|
|
file.sizeBytes = info.size();
|
|
file.lastModified = info.lastModified();
|
|
file.isDirectory = info.isDir();
|
|
file.isTextFile = !file.isDirectory && policy.isSupportedTextFile(file.absolutePath) && !policy.isSensitiveFile(file.absolutePath);
|
|
file.isHidden = info.isHidden();
|
|
return file;
|
|
}
|