diff --git a/src/base/search/searchpluginmanager.cpp b/src/base/search/searchpluginmanager.cpp index 2e4ad2348..2d7cae0a0 100644 --- a/src/base/search/searchpluginmanager.cpp +++ b/src/base/search/searchpluginmanager.cpp @@ -528,7 +528,7 @@ void SearchPluginManager::parseVersionInfo(const QByteArray &info) if (list.size() != 2) continue; const auto pluginName = QString::fromUtf8(list.first().trimmed()); - const PluginVersion version = PluginVersion::tryParse(list.last().trimmed(), {}); + const auto version = PluginVersion::fromString(QString::fromLatin1(list.last().trimmed())); if (!version.isValid()) continue; @@ -577,7 +577,7 @@ PluginVersion SearchPluginManager::getPluginVersion(const Path &filePath) if (!line.startsWith(u"#VERSION:", Qt::CaseInsensitive)) continue; const QString versionStr = line.mid(9); - const PluginVersion version = PluginVersion::tryParse(versionStr, {}); + const auto version = PluginVersion::fromString(versionStr); if (version.isValid()) return version; diff --git a/src/base/search/searchpluginmanager.h b/src/base/search/searchpluginmanager.h index 8b4a0efd1..0f3aa64dd 100644 --- a/src/base/search/searchpluginmanager.h +++ b/src/base/search/searchpluginmanager.h @@ -36,7 +36,7 @@ #include "base/path.h" #include "base/utils/version.h" -using PluginVersion = Utils::Version; +using PluginVersion = Utils::Version<2>; Q_DECLARE_METATYPE(PluginVersion) namespace Net diff --git a/src/base/utils/foreignapps.cpp b/src/base/utils/foreignapps.cpp index 37e36d3b0..cecbd4591 100644 --- a/src/base/utils/foreignapps.cpp +++ b/src/base/utils/foreignapps.cpp @@ -72,16 +72,11 @@ namespace // So trim off unrelated characters const auto versionStr = QString::fromLocal8Bit(outputSplit[1]); const int idx = versionStr.indexOf(QRegularExpression(u"[^\\.\\d]"_qs)); - - try - { - info = {exeName, versionStr.left(idx)}; - } - catch (const RuntimeError &) - { + const auto version = PythonInfo::Version::fromString(versionStr.left(idx)); + if (!version.isValid()) return false; - } + info = {exeName, version}; LogMsg(QCoreApplication::translate("Utils::ForeignApps", "Python detected, executable name: '%1', version: %2") .arg(info.executableName, info.version.toString()), Log::INFO); return true; diff --git a/src/base/utils/foreignapps.h b/src/base/utils/foreignapps.h index a99db5c08..1c3a68bc9 100644 --- a/src/base/utils/foreignapps.h +++ b/src/base/utils/foreignapps.h @@ -37,7 +37,7 @@ namespace Utils::ForeignApps { struct PythonInfo { - using Version = Utils::Version; + using Version = Utils::Version<3, 1>; bool isValid() const; bool isSupportedVersion() const; diff --git a/src/base/utils/version.h b/src/base/utils/version.h index 5dc45189e..98bff202f 100644 --- a/src/base/utils/version.h +++ b/src/base/utils/version.h @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2022 Mike Tzou (Chocobo1) * Copyright (C) 2016 Eugene Shalygin * * This program is free software; you can redistribute it and/or @@ -31,84 +32,78 @@ #include #include -#include +#include #include +#include -#include "base/exceptions.h" -#include "base/global.h" #include "base/interfaces/istringable.h" namespace Utils { - template + // This class provides a default implementation of `isValid()` that should work for most cases + // It is ultimately up to the user to decide whether the version numbers are useful/meaningful + template class Version final : public IStringable { static_assert((N > 0), "The number of version components may not be smaller than 1"); + static_assert((Mandatory > 0), "The number of mandatory components may not be smaller than 1"); static_assert((N >= Mandatory), "The number of mandatory components may not be larger than the total number of components"); public: - typedef T ComponentType; - typedef Version ThisType; + using ThisType = Version; constexpr Version() = default; - template ...>, int> = 0> - constexpr Version(Other ... components) - : m_components {{static_cast(components) ...}} + template ...>, int> = 0> + constexpr Version(Ts ... params) + : m_components {{params ...}} { - static_assert((sizeof...(Other) <= N), "Too many parameters provided"); - static_assert((sizeof...(Other) >= Mandatory), "Not enough parameters provided"); + static_assert((sizeof...(Ts) <= N), "Too many parameters provided"); + static_assert((sizeof...(Ts) >= Mandatory), "Not enough parameters provided"); } - /** - * @brief Creates version from string in format "x.y.z" - * - * @param version Version string in format "x.y.z" - * @throws RuntimeError if parsing fails - */ - Version(const QString &version) - : m_components {parseList(version.split(u'.'))} + constexpr bool isValid() const { + bool hasValid = false; + for (const int i : m_components) + { + if (i < 0) + return false; + if (i > 0) + hasValid = true; + } + return hasValid; } - /** - * @brief Creates version from byte array in format "x.y.z" - * - * @param version Version string in format "x.y.z" - * @throws RuntimeError if parsing fails - */ - Version(const QByteArray &version) - : m_components {parseList(version.split('.'))} + constexpr int majorNumber() const { - } - - constexpr ComponentType majorNumber() const - { - static_assert(N >= 1, "The number of version components is too small"); return m_components[0]; } - constexpr ComponentType minorNumber() const + constexpr int minorNumber() const { - static_assert(N >= 2, "The number of version components is too small"); + static_assert((N >= 2), "The number of version components is too small"); + return m_components[1]; } - constexpr ComponentType revisionNumber() const + constexpr int revisionNumber() const { - static_assert(N >= 3, "The number of version components is too small"); + static_assert((N >= 3), "The number of version components is too small"); + return m_components[2]; } - constexpr ComponentType patchNumber() const + constexpr int patchNumber() const { - static_assert(N >= 4, "The number of version components is too small"); + static_assert((N >= 4), "The number of version components is too small"); + return m_components[3]; } - constexpr ComponentType operator[](const std::size_t i) const + constexpr int operator[](const int i) const { return m_components.at(i); } @@ -116,24 +111,19 @@ namespace Utils QString toString() const override { // find the last one non-zero component - std::size_t lastSignificantIndex = N - 1; - while ((lastSignificantIndex > 0) && ((*this)[lastSignificantIndex] == 0)) + int lastSignificantIndex = N - 1; + while ((lastSignificantIndex > 0) && (m_components[lastSignificantIndex] == 0)) --lastSignificantIndex; - if (lastSignificantIndex + 1 < Mandatory) // lastSignificantIndex >= 0 + if ((lastSignificantIndex + 1) < Mandatory) // lastSignificantIndex >= 0 lastSignificantIndex = Mandatory - 1; // and Mandatory >= 1 - QString res = QString::number((*this)[0]); - for (std::size_t i = 1; i <= lastSignificantIndex; ++i) - res += (u'.' + QString::number((*this)[i])); + QString res = QString::number(m_components[0]); + for (int i = 1; i <= lastSignificantIndex; ++i) + res += (u'.' + QString::number(m_components[i])); return res; } - constexpr bool isValid() const - { - return (*this != ThisType {}); - } - // TODO: remove manually defined operators and use compiler generated `operator<=>()` in C++20 friend bool operator==(const ThisType &left, const ThisType &right) { @@ -145,66 +135,50 @@ namespace Utils return (left.m_components < right.m_components); } - template - static Version tryParse(const StringClassWithSplitMethod &s, const Version &defaultVersion) + static Version fromString(const QStringView string, const Version &defaultVersion = {}) { - try - { - return Version(s); - } - catch (const RuntimeError &error) - { - qDebug() << "Error parsing version:" << error.message(); + const QList stringParts = string.split(u'.'); + const int count = stringParts.size(); + + if ((count > N) || (count < Mandatory)) return defaultVersion; + + Version version; + for (int i = 0; i < count; ++i) + { + bool ok = false; + version.m_components[i] = stringParts[i].toInt(&ok); + if (!ok) + return defaultVersion; } + + return version; } private: - using ComponentsArray = std::array; - - template - static ComponentsArray parseList(const StringList &versionParts) - { - if ((static_cast(versionParts.size()) > N) - || (static_cast(versionParts.size()) < Mandatory)) - { - throw RuntimeError(u"Incorrect number of version components"_qs); - } - - bool ok = false; - ComponentsArray res {{}}; - for (std::size_t i = 0; i < static_cast(versionParts.size()); ++i) - { - res[i] = static_cast(versionParts[static_cast(i)].toInt(&ok)); - if (!ok) - throw RuntimeError(u"Can not parse version component"_qs); - } - return res; - } - - ComponentsArray m_components {{}}; + std::array m_components {{}}; }; - template - constexpr bool operator!=(const Version &left, const Version &right) + template + constexpr bool operator!=(const Version &left, const Version &right) { return !(left == right); } - template - constexpr bool operator>(const Version &left, const Version &right) + template + constexpr bool operator>(const Version &left, const Version &right) { return (right < left); } - template - constexpr bool operator<=(const Version &left, const Version &right) + template + constexpr bool operator<=(const Version &left, const Version &right) { return !(left > right); } - template - constexpr bool operator>=(const Version &left, const Version &right) + template + constexpr bool operator>=(const Version &left, const Version &right) { return !(left < right); } diff --git a/src/gui/programupdater.cpp b/src/gui/programupdater.cpp index 51b436600..1c745a1cc 100644 --- a/src/gui/programupdater.cpp +++ b/src/gui/programupdater.cpp @@ -52,25 +52,21 @@ namespace { bool isVersionMoreRecent(const QString &remoteVersion) { - using Version = Utils::Version; + using Version = Utils::Version<4, 3>; - try - { - const Version newVersion {remoteVersion}; - const Version currentVersion {QBT_VERSION_MAJOR, QBT_VERSION_MINOR, QBT_VERSION_BUGFIX, QBT_VERSION_BUILD}; - if (newVersion == currentVersion) - { - const bool isDevVersion = QStringLiteral(QBT_VERSION_STATUS).contains( - QRegularExpression(u"(alpha|beta|rc)"_qs)); - if (isDevVersion) - return true; - } - return (newVersion > currentVersion); - } - catch (const RuntimeError &) - { + const auto newVersion = Version::fromString(remoteVersion); + if (!newVersion.isValid()) return false; + + const Version currentVersion {QBT_VERSION_MAJOR, QBT_VERSION_MINOR, QBT_VERSION_BUGFIX, QBT_VERSION_BUILD}; + if (newVersion == currentVersion) + { + const bool isDevVersion = QStringLiteral(QBT_VERSION_STATUS).contains( + QRegularExpression(u"(alpha|beta|rc)"_qs)); + if (isDevVersion) + return true; } + return (newVersion > currentVersion); } } diff --git a/src/gui/utils.cpp b/src/gui/utils.cpp index 69f27d18e..b40dcbbf6 100644 --- a/src/gui/utils.cpp +++ b/src/gui/utils.cpp @@ -193,8 +193,8 @@ void Utils::Gui::openFolderSelect(const Path &path) proc.start(u"nautilus"_qs, {u"--version"_qs}); proc.waitForFinished(); const auto nautilusVerStr = QString::fromLocal8Bit(proc.readLine()).remove(QRegularExpression(u"[^0-9.]"_qs)); - using NautilusVersion = Utils::Version; - if (NautilusVersion::tryParse(nautilusVerStr, {1, 0, 0}) > NautilusVersion {3, 28, 0}) + using NautilusVersion = Utils::Version<3>; + if (NautilusVersion::fromString(nautilusVerStr, {1, 0, 0}) > NautilusVersion(3, 28, 0)) proc.startDetached(u"nautilus"_qs, {(Fs::isDir(path) ? path.parentPath() : path).toString()}); else proc.startDetached(u"nautilus"_qs, {u"--no-desktop"_qs, (Fs::isDir(path) ? path.parentPath() : path).toString()}); diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h index 239951058..db494cd9f 100644 --- a/src/webui/webapplication.h +++ b/src/webui/webapplication.h @@ -49,7 +49,7 @@ #include "base/utils/version.h" #include "api/isessionmanager.h" -inline const Utils::Version API_VERSION {2, 8, 14}; +inline const Utils::Version<3, 2> API_VERSION {2, 8, 14}; class APIController; class AuthController; diff --git a/test/testutilsversion.cpp b/test/testutilsversion.cpp index f83a977bc..a5381a3e9 100644 --- a/test/testutilsversion.cpp +++ b/test/testutilsversion.cpp @@ -42,96 +42,131 @@ public: private slots: void testConstructors() const { - using TwoDigits = Utils::Version; - TwoDigits(); + // should not compile: + // Utils::Version<-1>(); + // Utils::Version<0>(); + // Utils::Version<2, 3>(); + // Utils::Version<2, -1>(); + // Utils::Version<2, 0>(); + + using TwoDigits = Utils::Version<2, 1>; TwoDigits(0); + TwoDigits(50); TwoDigits(0, 1); - using ThreeDigits = Utils::Version; + using ThreeDigits = Utils::Version<3, 3>; // should not compile: // ThreeDigits(1); // ThreeDigits(1, 2); + // ThreeDigits(1.0, 2, 3); // ThreeDigits(1, 2, 3, 4); - QCOMPARE(ThreeDigits(u"1.2.3"_qs), ThreeDigits(1, 2, 3)); - QCOMPARE(ThreeDigits(QByteArrayLiteral("1.2.3")), ThreeDigits(1, 2, 3)); + ThreeDigits(1, 2, 3); + } -#if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)) - QVERIFY_THROWS_EXCEPTION(RuntimeError, ThreeDigits(u""_qs)); - QVERIFY_THROWS_EXCEPTION(RuntimeError, ThreeDigits(u"1"_qs)); - QVERIFY_THROWS_EXCEPTION(RuntimeError, ThreeDigits(u"1.0"_qs)); - QVERIFY_THROWS_EXCEPTION(RuntimeError, ThreeDigits(u"1.0.1.1"_qs)); - QVERIFY_THROWS_EXCEPTION(RuntimeError, ThreeDigits(u"random_string"_qs)); - - QVERIFY_THROWS_EXCEPTION(RuntimeError, ThreeDigits(QByteArrayLiteral("1"))); - QVERIFY_THROWS_EXCEPTION(RuntimeError, ThreeDigits(QByteArrayLiteral("1.0"))); - QVERIFY_THROWS_EXCEPTION(RuntimeError, ThreeDigits(QByteArrayLiteral("1.0.1.1"))); - QVERIFY_THROWS_EXCEPTION(RuntimeError, ThreeDigits(QByteArrayLiteral("random_string"))); -#endif + void testIsValid() const + { + using ThreeDigits = Utils::Version<3>; + QCOMPARE(ThreeDigits().isValid(), false); + QCOMPARE(ThreeDigits(0, 0, 0).isValid(), false); + QCOMPARE(ThreeDigits(0, 0, -1).isValid(), false); + QCOMPARE(ThreeDigits(0, 0, 1).isValid(), true); + QCOMPARE(ThreeDigits(0, 1, 0).isValid(), true); + QCOMPARE(ThreeDigits(1, 0, 0).isValid(), true); + QCOMPARE(ThreeDigits(10, 11, 12).isValid(), true); } void testVersionComponents() const { - const Utils::Version version1 {1}; + const Utils::Version<1> version1 {1}; QCOMPARE(version1[0], 1); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)) + QVERIFY_THROWS_EXCEPTION(std::out_of_range, version1[1]); +#endif QCOMPARE(version1.majorNumber(), 1); // should not compile: // version1.minorNumber(); // version1.revisionNumber(); // version1.patchNumber(); -#if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)) - QVERIFY_THROWS_EXCEPTION(std::out_of_range, version1[1]); - QVERIFY_THROWS_EXCEPTION(std::out_of_range, version1[2]); -#endif - const Utils::Version version2 {10, 11, 12, 13}; - QCOMPARE(version2[0], 10); - QCOMPARE(version2[1], 11); - QCOMPARE(version2[2], 12); - QCOMPARE(version2[3], 13); - QCOMPARE(version2.majorNumber(), 10); - QCOMPARE(version2.minorNumber(), 11); - QCOMPARE(version2.revisionNumber(), 12); - QCOMPARE(version2.patchNumber(), 13); + const Utils::Version<2, 1> version2 {2}; + QCOMPARE(version2[0], 2); + QCOMPARE(version2[1], 0); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)) + QVERIFY_THROWS_EXCEPTION(std::out_of_range, version2[2]); +#endif + QCOMPARE(version2.majorNumber(), 2); + QCOMPARE(version2.minorNumber(), 0); + // should not compile: + // version2.revisionNumber(); + // version2.patchNumber(); + + const Utils::Version<3, 2> version3 {3, 2}; + QCOMPARE(version3[0], 3); + QCOMPARE(version3[1], 2); + QCOMPARE(version3[2], 0); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)) + QVERIFY_THROWS_EXCEPTION(std::out_of_range, version3[3]); +#endif + QCOMPARE(version3.majorNumber(), 3); + QCOMPARE(version3.minorNumber(), 2); + QCOMPARE(version3.revisionNumber(), 0); + // should not compile: + // version3.patchNumber(); + + const Utils::Version<4> version4 {10, 11, 12, 13}; + QCOMPARE(version4[0], 10); + QCOMPARE(version4[1], 11); + QCOMPARE(version4[2], 12); + QCOMPARE(version4[3], 13); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)) + QVERIFY_THROWS_EXCEPTION(std::out_of_range, version4[4]); +#endif + QCOMPARE(version4.majorNumber(), 10); + QCOMPARE(version4.minorNumber(), 11); + QCOMPARE(version4.revisionNumber(), 12); + QCOMPARE(version4.patchNumber(), 13); } void testToString() const { - using OneMandatory = Utils::Version; - QCOMPARE(OneMandatory(u"10"_qs).toString(), u"10"_qs); + using OneMandatory = Utils::Version<2, 1>; + QCOMPARE(OneMandatory(10).toString(), u"10"_qs); + QCOMPARE(OneMandatory(2).toString(), u"2"_qs); + QCOMPARE(OneMandatory(2, 0).toString(), u"2"_qs); + QCOMPARE(OneMandatory(2, 2).toString(), u"2.2"_qs); - using FourDigits = Utils::Version; - QCOMPARE(FourDigits().toString(), u"0.0.0.0"_qs); + using FourDigits = Utils::Version<4>; QCOMPARE(FourDigits(10, 11, 12, 13).toString(), u"10.11.12.13"_qs); } - void testIsValid() const + void testFromString() const { - using ThreeDigits = Utils::Version; - QCOMPARE(ThreeDigits().isValid(), false); - QCOMPARE(ThreeDigits(10, 11, 12).isValid(), true); - } - - void testTryParse() const - { - using OneMandatory = Utils::Version; + using OneMandatory = Utils::Version<2, 1>; const OneMandatory default1 {10, 11}; - QCOMPARE(OneMandatory::tryParse(u"1"_qs, default1), OneMandatory(1)); - QCOMPARE(OneMandatory::tryParse(u"1.2"_qs, default1), OneMandatory(1, 2)); - QCOMPARE(OneMandatory::tryParse(u"1,2"_qs, default1), default1); + QCOMPARE(OneMandatory::fromString(u"1"_qs, default1), OneMandatory(1)); + QCOMPARE(OneMandatory::fromString(u"1.2"_qs, default1), OneMandatory(1, 2)); + QCOMPARE(OneMandatory::fromString(u"100.2000"_qs, default1), OneMandatory(100, 2000)); + QCOMPARE(OneMandatory::fromString(u"1,2"_qs), OneMandatory()); + QCOMPARE(OneMandatory::fromString(u"1,2"_qs, default1), default1); + QCOMPARE(OneMandatory::fromString(u"1.2a"_qs), OneMandatory()); + QCOMPARE(OneMandatory::fromString(u"1.2.a"_qs), OneMandatory()); + QCOMPARE(OneMandatory::fromString(u""_qs), OneMandatory()); + QCOMPARE(OneMandatory::fromString(u""_qs, default1), default1); + QCOMPARE(OneMandatory::fromString(u"random_string"_qs), OneMandatory()); + QCOMPARE(OneMandatory::fromString(u"random_string"_qs, default1), default1); - QCOMPARE(OneMandatory::tryParse(u""_qs, default1), default1); - QCOMPARE(OneMandatory::tryParse(u"random_string"_qs, default1), default1); - - using FourDigits = Utils::Version; - const FourDigits default4 {10, 11, 12, 13}; - QCOMPARE(FourDigits::tryParse(u"1"_qs, default4), default4); - QCOMPARE(FourDigits::tryParse(u"1.2.3.4"_qs, default4), FourDigits(1, 2, 3, 4)); - QCOMPARE(FourDigits::tryParse(u"1,2.3.4"_qs, default4), default4); + using FourDigits = Utils::Version<4, 3>; + const FourDigits default2 {10, 11, 12, 13}; + QCOMPARE(FourDigits::fromString(u"1"_qs, default2), default2); + QCOMPARE(FourDigits::fromString(u"1.2"_qs), FourDigits()); + QCOMPARE(FourDigits::fromString(u"1.2.3"_qs), FourDigits(1, 2, 3)); + QCOMPARE(FourDigits::fromString(u"1.2.3.0"_qs), FourDigits(1, 2, 3)); + QCOMPARE(FourDigits::fromString(u"1.2.3.4"_qs), FourDigits(1, 2, 3, 4)); } void testComparisons() const { - using ThreeDigits = Utils::Version; + using ThreeDigits = Utils::Version<3>; QVERIFY(ThreeDigits() == ThreeDigits()); QVERIFY(!(ThreeDigits() != ThreeDigits()));