File indexing completed on 2024-04-21 04:38:09

0001 /*
0002     SPDX-FileCopyrightText: 2018 Anton Anikin <anton@anikin.xyz>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "checksdb.h"
0008 
0009 #include "globalsettings.h"
0010 #include "utils.h"
0011 
0012 #include <KLocalizedString>
0013 
0014 #include <QDir>
0015 #include <QRegularExpression>
0016 
0017 namespace Clazy
0018 {
0019 
0020 ChecksDB::ChecksDB(const QUrl& docsPath)
0021 {
0022     static const QHash<QString, QString> levelName = {
0023         { QStringLiteral("manuallevel"), QStringLiteral("manual") },
0024         { QStringLiteral("hiddenlevel"), QStringLiteral("manual") }
0025     };
0026 
0027     static const QHash<QString, QString> levelDisplayName = {
0028         { QStringLiteral("level0"), i18nc("@item level of checks", "Level 0") },
0029         { QStringLiteral("level1"), i18nc("@item level of checks", "Level 1") },
0030         { QStringLiteral("level2"), i18nc("@item level of checks", "Level 2") },
0031         { QStringLiteral("level3"), i18nc("@item level of checks", "Level 3") },
0032         { QStringLiteral("manual"), i18nc("@item level of checks", "Manual Level") }
0033     };
0034 
0035     static const QHash<QString, QString> levelDescription = {
0036         { QStringLiteral("level0"),
0037           i18n("Very stable checks, 99.99% safe, mostly no false-positives, very desirable.") },
0038 
0039         { QStringLiteral("level1"),
0040           i18n("The default level. Very similar to level 0, slightly more false-positives but very few.") },
0041 
0042         { QStringLiteral("level2"),
0043           i18n("Also very few false-positives, but contains noisy checks which not everyone agree should be default.") },
0044 
0045         { QStringLiteral("level3"),
0046           i18n("Contains checks with high rate of false-positives.") },
0047 
0048         { QStringLiteral("manual"),
0049           i18n("Checks here need to be enabled explicitly, as they don't belong to any level. "
0050                "Checks here are very stable and have very few false-positives.") }
0051     };
0052 
0053     const QString defaultError = i18n(
0054         "Unable to load Clazy checks information from '%1'. Please check your settings.",
0055         docsPath.toLocalFile());
0056 
0057     QDir docsDir(docsPath.toLocalFile());
0058     if (!docsDir.exists()) {
0059         m_error = defaultError;
0060         return;
0061     }
0062 
0063     const QRegularExpression levelRE(QStringLiteral(".*level.*"));
0064     const QRegularExpression checkRE(QStringLiteral("^README-(.+)\\.md$"));
0065 
0066     const auto levelsDirs = docsDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
0067     for (const auto& levelDir : levelsDirs) {
0068         if (!levelRE.match(levelDir).hasMatch()) {
0069             continue;
0070         }
0071 
0072         if (!docsDir.cd(levelDir)) {
0073             continue;
0074         }
0075 
0076         auto level = new Level;
0077         level->name = levelName.value(levelDir, levelDir);
0078         level->displayName = levelDisplayName.value(level->name, levelDir);
0079         level->description = levelDescription.value(level->name, {});
0080 
0081         const auto checksFiles = docsDir.entryList(QDir::Files | QDir::Readable);
0082         for (const auto& checkFile : checksFiles) {
0083             auto match = checkRE.match(checkFile);
0084             if (!match.hasMatch()) {
0085                 continue;
0086             }
0087 
0088             QFile mdFile(docsDir.absoluteFilePath(checkFile));
0089             if (!mdFile.open(QIODevice::ReadOnly)) {
0090                 continue;
0091             }
0092 
0093             auto check = new Check;
0094             check->level = level;
0095             check->name = match.captured(1);
0096             check->description = markdown2html(mdFile.readAll());
0097             level->checks[check->name] = check;
0098 
0099             m_checks[check->name] = check;
0100         }
0101 
0102         if (level->checks.isEmpty()) {
0103             delete level;
0104         } else {
0105             m_levels[level->name] = level;
0106         }
0107 
0108         docsDir.cdUp();
0109     }
0110 
0111     if (m_levels.isEmpty()) {
0112         m_error = defaultError;
0113     }
0114 }
0115 
0116 ChecksDB::~ChecksDB()
0117 {
0118     qDeleteAll(m_levels);
0119     qDeleteAll(m_checks);
0120 }
0121 
0122 bool ChecksDB::isValid() const
0123 {
0124     return m_error.isEmpty();
0125 }
0126 
0127 QString ChecksDB::error() const
0128 {
0129     return m_error;
0130 }
0131 
0132 const QMap<QString, Level*>& ChecksDB::levels() const
0133 {
0134     return m_levels;
0135 }
0136 
0137 const QMap<QString, Check*>& ChecksDB::checks() const
0138 {
0139     return m_checks;
0140 }
0141 
0142 QString ChecksDB::defaultChecks()
0143 {
0144     return QStringLiteral("level1");
0145 }
0146 
0147 }