feat+fix(Version): make comparsion FlexVer-compatible
... and fixes a minor issue in the parsing. This changes the expected behavior of Versions in one significant way: Now, Versions like 1.2 or 1.5 evaluate to LESS THAN 1.2.0 and 1.5.0 respectively. This makes sense for sorting versions, since one expects the versions without patch release to 'contain' the ones with, so the ones without should be evaluated uniformily with the ones with the patch. Signed-off-by: flow <flowlnlnln@gmail.com>
This commit is contained in:
parent
bcebb1920f
commit
445f9e5f71
@ -10,33 +10,47 @@ Version::Version(QString str) : m_string(std::move(str))
|
|||||||
parse();
|
parse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define VERSION_OPERATOR(return_on_different) \
|
||||||
|
bool exclude_our_sections = false; \
|
||||||
|
bool exclude_their_sections = false; \
|
||||||
|
\
|
||||||
|
const auto size = qMax(m_sections.size(), other.m_sections.size()); \
|
||||||
|
for (int i = 0; i < size; ++i) { \
|
||||||
|
Section sec1 = (i >= m_sections.size()) ? Section() : m_sections.at(i); \
|
||||||
|
Section sec2 = (i >= other.m_sections.size()) ? Section() : other.m_sections.at(i); \
|
||||||
|
\
|
||||||
|
{ /* Don't include appendixes in the comparison */ \
|
||||||
|
if (sec1.isAppendix()) \
|
||||||
|
exclude_our_sections = true; \
|
||||||
|
if (sec2.isAppendix()) \
|
||||||
|
exclude_their_sections = true; \
|
||||||
|
\
|
||||||
|
if (exclude_our_sections) { \
|
||||||
|
sec1 = Section(); \
|
||||||
|
if (sec2.m_isNull) \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
if (exclude_their_sections) { \
|
||||||
|
sec2 = Section(); \
|
||||||
|
if (sec1.m_isNull) \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
if (sec1 != sec2) \
|
||||||
|
return return_on_different; \
|
||||||
|
}
|
||||||
|
|
||||||
bool Version::operator<(const Version& other) const
|
bool Version::operator<(const Version& other) const
|
||||||
{
|
{
|
||||||
const auto size = qMax(m_sections.size(), other.m_sections.size());
|
VERSION_OPERATOR(sec1 < sec2)
|
||||||
for (int i = 0; i < size; ++i) {
|
|
||||||
const Section sec1 =
|
|
||||||
(i >= m_sections.size()) ? Section("") : m_sections.at(i);
|
|
||||||
const Section sec2 =
|
|
||||||
(i >= other.m_sections.size()) ? Section("") : other.m_sections.at(i);
|
|
||||||
|
|
||||||
if (sec1 != sec2)
|
|
||||||
return sec1 < sec2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool Version::operator==(const Version& other) const
|
bool Version::operator==(const Version& other) const
|
||||||
{
|
{
|
||||||
const auto size = qMax(m_sections.size(), other.m_sections.size());
|
VERSION_OPERATOR(false)
|
||||||
for (int i = 0; i < size; ++i) {
|
|
||||||
const Section sec1 =
|
|
||||||
(i >= m_sections.size()) ? Section("") : m_sections.at(i);
|
|
||||||
const Section sec2 =
|
|
||||||
(i >= other.m_sections.size()) ? Section("") : other.m_sections.at(i);
|
|
||||||
|
|
||||||
if (sec1 != sec2)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -62,25 +76,37 @@ void Version::parse()
|
|||||||
m_sections.clear();
|
m_sections.clear();
|
||||||
QString currentSection;
|
QString currentSection;
|
||||||
|
|
||||||
auto classChange = [](QChar lastChar, QChar currentChar) {
|
if (m_string.isEmpty())
|
||||||
return !lastChar.isNull() && ((!lastChar.isDigit() && currentChar.isDigit()) || (lastChar.isDigit() && !currentChar.isDigit()));
|
return;
|
||||||
|
|
||||||
|
auto classChange = [&](QChar lastChar, QChar currentChar) {
|
||||||
|
if (lastChar.isNull())
|
||||||
|
return false;
|
||||||
|
if (lastChar.isDigit() != currentChar.isDigit())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const QList<QChar> s_separators{ '.', '-', '+' };
|
||||||
|
if (s_separators.contains(currentChar) && currentSection.at(0) != currentChar)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
for (int i = 0; i < m_string.size(); ++i) {
|
currentSection += m_string.at(0);
|
||||||
|
for (int i = 1; i < m_string.size(); ++i) {
|
||||||
const auto& current_char = m_string.at(i);
|
const auto& current_char = m_string.at(i);
|
||||||
if ((i > 0 && classChange(m_string.at(i - 1), current_char)) || current_char == '.' || current_char == '-' || current_char == '+') {
|
if (classChange(m_string.at(i - 1), current_char)) {
|
||||||
if (!currentSection.isEmpty()) {
|
if (!currentSection.isEmpty())
|
||||||
m_sections.append(Section(currentSection));
|
m_sections.append(Section(currentSection));
|
||||||
}
|
|
||||||
currentSection = "";
|
currentSection = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
currentSection += current_char;
|
currentSection += current_char;
|
||||||
}
|
}
|
||||||
if (!currentSection.isEmpty()) {
|
|
||||||
|
if (!currentSection.isEmpty())
|
||||||
m_sections.append(Section(currentSection));
|
m_sections.append(Section(currentSection));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// qDebug print support for the Version class
|
/// qDebug print support for the Version class
|
||||||
QDebug operator<<(QDebug debug, const Version& v)
|
QDebug operator<<(QDebug debug, const Version& v)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* PolyMC - Minecraft Launcher
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2023 flowln <flowlnlnln@gmail.com>
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
@ -60,7 +61,7 @@ class Version {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
struct Section {
|
struct Section {
|
||||||
explicit Section(QString fullString) : m_isNull(true), m_fullString(std::move(fullString))
|
explicit Section(QString fullString) : m_fullString(std::move(fullString))
|
||||||
{
|
{
|
||||||
int cutoff = m_fullString.size();
|
int cutoff = m_fullString.size();
|
||||||
for (int i = 0; i < m_fullString.size(); i++) {
|
for (int i = 0; i < m_fullString.size(); i++) {
|
||||||
@ -95,45 +96,54 @@ class Version {
|
|||||||
|
|
||||||
explicit Section() = default;
|
explicit Section() = default;
|
||||||
|
|
||||||
bool m_isNull = false;
|
bool m_isNull = true;
|
||||||
int m_numPart = 0;
|
|
||||||
|
|
||||||
|
int m_numPart = 0;
|
||||||
QString m_stringPart;
|
QString m_stringPart;
|
||||||
|
|
||||||
QString m_fullString;
|
QString m_fullString;
|
||||||
|
|
||||||
|
[[nodiscard]] inline bool isAppendix() const { return m_stringPart.startsWith('+'); }
|
||||||
|
[[nodiscard]] inline bool isPreRelease() const { return m_stringPart.startsWith('-') && m_stringPart.length() > 1; }
|
||||||
|
|
||||||
inline bool operator==(const Section& other) const
|
inline bool operator==(const Section& other) const
|
||||||
{
|
{
|
||||||
if (m_isNull && !other.m_isNull)
|
if (m_isNull && !other.m_isNull)
|
||||||
return other.m_numPart == 0;
|
return false;
|
||||||
|
|
||||||
if (!m_isNull && other.m_isNull)
|
if (!m_isNull && other.m_isNull)
|
||||||
return m_numPart == 0;
|
return false;
|
||||||
|
|
||||||
if (m_isNull || other.m_isNull)
|
if (!m_isNull && !other.m_isNull) {
|
||||||
return (m_stringPart == ".") || (other.m_stringPart == ".");
|
|
||||||
|
|
||||||
if (!m_isNull && !other.m_isNull)
|
|
||||||
return (m_numPart == other.m_numPart) && (m_stringPart == other.m_stringPart);
|
return (m_numPart == other.m_numPart) && (m_stringPart == other.m_stringPart);
|
||||||
|
}
|
||||||
|
|
||||||
return m_fullString == other.m_fullString;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator<(const Section& other) const
|
inline bool operator<(const Section& other) const
|
||||||
{
|
{
|
||||||
if (m_isNull && !other.m_isNull)
|
static auto unequal_is_less = [](Section const& non_null) -> bool {
|
||||||
return other.m_numPart > 0;
|
if (non_null.m_stringPart.isEmpty())
|
||||||
|
return non_null.m_numPart == 0;
|
||||||
|
return (non_null.m_stringPart != QLatin1Char('.')) && non_null.isPreRelease();
|
||||||
|
};
|
||||||
|
|
||||||
if (!m_isNull && other.m_isNull)
|
if (!m_isNull && other.m_isNull)
|
||||||
return m_numPart < 0;
|
return unequal_is_less(*this);
|
||||||
|
if (m_isNull && !other.m_isNull)
|
||||||
if (m_isNull || other.m_isNull)
|
return !unequal_is_less(other);
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!m_isNull && !other.m_isNull) {
|
if (!m_isNull && !other.m_isNull) {
|
||||||
if (m_numPart < other.m_numPart)
|
if (m_numPart < other.m_numPart)
|
||||||
return true;
|
return true;
|
||||||
if (m_numPart == other.m_numPart && m_stringPart < other.m_stringPart)
|
if (m_numPart == other.m_numPart && m_stringPart < other.m_stringPart)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (!m_stringPart.isEmpty() && other.m_stringPart.isEmpty())
|
||||||
|
return false;
|
||||||
|
if (m_stringPart.isEmpty() && !other.m_stringPart.isEmpty())
|
||||||
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,24 +33,24 @@ class VersionTest : public QObject {
|
|||||||
addDataColumns();
|
addDataColumns();
|
||||||
|
|
||||||
QTest::newRow("equal, explicit") << "1.2.0" << "1.2.0" << false << true;
|
QTest::newRow("equal, explicit") << "1.2.0" << "1.2.0" << false << true;
|
||||||
QTest::newRow("equal, implicit 1") << "1.2" << "1.2.0" << false << true;
|
|
||||||
QTest::newRow("equal, implicit 2") << "1.2.0" << "1.2" << false << true;
|
|
||||||
QTest::newRow("equal, two-digit") << "1.42" << "1.42" << false << true;
|
QTest::newRow("equal, two-digit") << "1.42" << "1.42" << false << true;
|
||||||
|
|
||||||
QTest::newRow("lessThan, explicit 1") << "1.2.0" << "1.2.1" << true << false;
|
QTest::newRow("lessThan, explicit 1") << "1.2.0" << "1.2.1" << true << false;
|
||||||
QTest::newRow("lessThan, explicit 2") << "1.2.0" << "1.3.0" << true << false;
|
QTest::newRow("lessThan, explicit 2") << "1.2.0" << "1.3.0" << true << false;
|
||||||
QTest::newRow("lessThan, explicit 3") << "1.2.0" << "2.2.0" << true << false;
|
QTest::newRow("lessThan, explicit 3") << "1.2.0" << "2.2.0" << true << false;
|
||||||
QTest::newRow("lessThan, implicit 1") << "1.2" << "1.2.1" << true << false;
|
QTest::newRow("lessThan, implicit 1") << "1.2" << "1.2.0" << true << false;
|
||||||
QTest::newRow("lessThan, implicit 2") << "1.2" << "1.3.0" << true << false;
|
QTest::newRow("lessThan, implicit 2") << "1.2" << "1.2.1" << true << false;
|
||||||
QTest::newRow("lessThan, implicit 3") << "1.2" << "2.2.0" << true << false;
|
QTest::newRow("lessThan, implicit 3") << "1.2" << "1.3.0" << true << false;
|
||||||
|
QTest::newRow("lessThan, implicit 4") << "1.2" << "2.2.0" << true << false;
|
||||||
QTest::newRow("lessThan, two-digit") << "1.41" << "1.42" << true << false;
|
QTest::newRow("lessThan, two-digit") << "1.41" << "1.42" << true << false;
|
||||||
|
|
||||||
QTest::newRow("greaterThan, explicit 1") << "1.2.1" << "1.2.0" << false << false;
|
QTest::newRow("greaterThan, explicit 1") << "1.2.1" << "1.2.0" << false << false;
|
||||||
QTest::newRow("greaterThan, explicit 2") << "1.3.0" << "1.2.0" << false << false;
|
QTest::newRow("greaterThan, explicit 2") << "1.3.0" << "1.2.0" << false << false;
|
||||||
QTest::newRow("greaterThan, explicit 3") << "2.2.0" << "1.2.0" << false << false;
|
QTest::newRow("greaterThan, explicit 3") << "2.2.0" << "1.2.0" << false << false;
|
||||||
QTest::newRow("greaterThan, implicit 1") << "1.2.1" << "1.2" << false << false;
|
QTest::newRow("greaterThan, implicit 1") << "1.2.0" << "1.2" << false << false;
|
||||||
QTest::newRow("greaterThan, implicit 2") << "1.3.0" << "1.2" << false << false;
|
QTest::newRow("greaterThan, implicit 2") << "1.2.1" << "1.2" << false << false;
|
||||||
QTest::newRow("greaterThan, implicit 3") << "2.2.0" << "1.2" << false << false;
|
QTest::newRow("greaterThan, implicit 3") << "1.3.0" << "1.2" << false << false;
|
||||||
|
QTest::newRow("greaterThan, implicit 4") << "2.2.0" << "1.2" << false << false;
|
||||||
QTest::newRow("greaterThan, two-digit") << "1.42" << "1.41" << false << false;
|
QTest::newRow("greaterThan, two-digit") << "1.42" << "1.41" << false << false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user