2022-07-30 12:24:03 -04:00
|
|
|
#pragma once
|
|
|
|
|
2022-11-28 12:09:39 -05:00
|
|
|
#include "../DefaultInclude.hpp"
|
2022-07-30 12:24:03 -04:00
|
|
|
#include <string_view>
|
2023-12-30 12:42:53 -05:00
|
|
|
#include <matjson.hpp>
|
2022-12-11 13:44:38 -05:00
|
|
|
#include <tuple>
|
2022-12-12 11:44:17 -05:00
|
|
|
#include "../utils/Result.hpp"
|
2022-07-30 12:24:03 -04:00
|
|
|
|
|
|
|
namespace geode {
|
2022-12-08 17:28:05 -05:00
|
|
|
enum class VersionCompare {
|
|
|
|
LessEq,
|
|
|
|
Exact,
|
|
|
|
MoreEq,
|
2023-05-01 09:06:06 -04:00
|
|
|
Less,
|
|
|
|
More,
|
2023-08-07 14:13:40 -04:00
|
|
|
Any
|
2022-12-08 17:28:05 -05:00
|
|
|
};
|
|
|
|
|
2024-02-15 15:34:20 -05:00
|
|
|
enum class VersionCompareResult {
|
|
|
|
TooOld,
|
|
|
|
Match,
|
|
|
|
TooNew,
|
|
|
|
MajorMismatch,
|
|
|
|
GenericMismatch
|
|
|
|
};
|
|
|
|
|
2022-07-30 12:24:03 -04:00
|
|
|
/**
|
2023-02-22 05:19:17 -05:00
|
|
|
* A version label, like v1.0.0-alpha or v2.3.4-prerelease. Limited to these
|
|
|
|
* options; arbitary identifiers are not supported. Additional numbering
|
|
|
|
* may be added after the identifier, such as v1.0.0-beta.1
|
2022-12-12 08:45:27 -05:00
|
|
|
*/
|
2023-02-22 05:19:17 -05:00
|
|
|
struct VersionTag {
|
|
|
|
enum {
|
|
|
|
Alpha,
|
|
|
|
Beta,
|
|
|
|
Prerelease,
|
|
|
|
} value;
|
|
|
|
std::optional<size_t> number;
|
|
|
|
|
|
|
|
using Type = decltype(value);
|
|
|
|
|
|
|
|
constexpr VersionTag(Type const& value) : value(value) {}
|
|
|
|
constexpr VersionTag(Type const& value, std::optional<size_t> number)
|
|
|
|
: value(value), number(number) {}
|
|
|
|
|
|
|
|
constexpr bool operator==(VersionTag const& other) const {
|
|
|
|
return value == other.value && number == other.number;
|
|
|
|
}
|
|
|
|
constexpr bool operator<(VersionTag const& other) const {
|
|
|
|
if (value == other.value) {
|
|
|
|
if (number && other.number) return number < other.number;
|
|
|
|
if (number) return true;
|
|
|
|
if (other.number) return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return value < other.value;
|
|
|
|
}
|
|
|
|
constexpr bool operator<=(VersionTag const& other) const {
|
|
|
|
if (value == other.value) {
|
|
|
|
if (number && other.number) return number <= other.number;
|
|
|
|
if (number) return true;
|
|
|
|
if (other.number) return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return value <= other.value;
|
|
|
|
}
|
|
|
|
constexpr bool operator>(VersionTag const& other) const {
|
|
|
|
if (value == other.value) {
|
|
|
|
if (number && other.number) return number > other.number;
|
2023-03-06 13:59:30 -05:00
|
|
|
if (number) return false;
|
|
|
|
if (other.number) return true;
|
2023-02-22 05:19:17 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return value > other.value;
|
|
|
|
}
|
|
|
|
constexpr bool operator>=(VersionTag const& other) const {
|
|
|
|
if (value == other.value) {
|
|
|
|
if (number && other.number) return number >= other.number;
|
2023-03-06 13:59:30 -05:00
|
|
|
if (number) return false;
|
|
|
|
if (other.number) return true;
|
2023-02-22 05:19:17 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return value >= other.value;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Result<VersionTag> parse(std::stringstream& str);
|
|
|
|
std::string toSuffixString() const;
|
|
|
|
std::string toString() const;
|
2022-12-12 08:45:27 -05:00
|
|
|
};
|
|
|
|
|
2023-03-06 13:59:30 -05:00
|
|
|
constexpr bool operator<(std::optional<VersionTag> const& a, std::optional<VersionTag> const& b) {
|
2023-03-07 03:56:36 -05:00
|
|
|
if (a && b) return *a < *b;
|
2023-03-06 13:59:30 -05:00
|
|
|
if (a) return true;
|
|
|
|
if (b) return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr bool operator<=(std::optional<VersionTag> const& a, std::optional<VersionTag> const& b) {
|
2023-03-07 03:56:36 -05:00
|
|
|
if (a && b) return *a <= *b;
|
2023-03-06 13:59:30 -05:00
|
|
|
if (a) return true;
|
|
|
|
if (b) return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr bool operator>(std::optional<VersionTag> const& a, std::optional<VersionTag> const& b) {
|
2023-03-07 03:56:36 -05:00
|
|
|
if (a && b) return *a > *b;
|
2023-03-06 13:59:30 -05:00
|
|
|
if (a) return false;
|
|
|
|
if (b) return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr bool operator>=(std::optional<VersionTag> const& a, std::optional<VersionTag> const& b) {
|
2023-03-07 03:56:36 -05:00
|
|
|
if (a && b) return *a >= *b;
|
2023-03-06 13:59:30 -05:00
|
|
|
if (a) return false;
|
|
|
|
if (b) return true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-12-12 08:45:27 -05:00
|
|
|
/**
|
2023-02-22 05:19:17 -05:00
|
|
|
* Class representing version information. Uses a limited subset of SemVer;
|
|
|
|
* identifiers are restricted to a few predefined ones, and only one
|
|
|
|
* identifier is allowed. See VersionTag for details
|
2022-07-30 12:24:03 -04:00
|
|
|
*/
|
2022-12-08 17:28:05 -05:00
|
|
|
class GEODE_DLL VersionInfo final {
|
2022-07-30 12:24:03 -04:00
|
|
|
protected:
|
2022-12-12 08:45:27 -05:00
|
|
|
size_t m_major = 1;
|
|
|
|
size_t m_minor = 0;
|
|
|
|
size_t m_patch = 0;
|
|
|
|
std::optional<VersionTag> m_tag;
|
2022-10-30 14:59:20 -04:00
|
|
|
|
2022-07-30 12:24:03 -04:00
|
|
|
public:
|
2022-12-08 15:04:02 -05:00
|
|
|
constexpr VersionInfo() = default;
|
2022-12-12 08:45:27 -05:00
|
|
|
constexpr VersionInfo(size_t major, size_t minor, size_t patch) {
|
|
|
|
m_major = major;
|
|
|
|
m_minor = minor;
|
|
|
|
m_patch = patch;
|
|
|
|
}
|
|
|
|
constexpr VersionInfo(
|
|
|
|
size_t major, size_t minor, size_t patch,
|
|
|
|
std::optional<VersionTag> tag
|
|
|
|
) {
|
2022-07-30 12:24:03 -04:00
|
|
|
m_major = major;
|
|
|
|
m_minor = minor;
|
|
|
|
m_patch = patch;
|
2022-12-12 08:45:27 -05:00
|
|
|
m_tag = tag;
|
2022-07-30 12:24:03 -04:00
|
|
|
}
|
2022-12-12 11:44:17 -05:00
|
|
|
|
|
|
|
static Result<VersionInfo> parse(std::string const& string);
|
2022-07-30 12:24:03 -04:00
|
|
|
|
2022-12-12 08:45:27 -05:00
|
|
|
constexpr size_t getMajor() const {
|
2022-12-08 15:04:02 -05:00
|
|
|
return m_major;
|
|
|
|
}
|
2022-07-30 12:24:03 -04:00
|
|
|
|
2022-12-12 08:45:27 -05:00
|
|
|
constexpr size_t getMinor() const {
|
2022-12-08 15:04:02 -05:00
|
|
|
return m_minor;
|
|
|
|
}
|
2022-07-30 12:24:03 -04:00
|
|
|
|
2022-12-12 08:45:27 -05:00
|
|
|
constexpr size_t getPatch() const {
|
2022-12-08 15:04:02 -05:00
|
|
|
return m_patch;
|
|
|
|
}
|
|
|
|
|
2022-12-12 08:45:27 -05:00
|
|
|
constexpr std::optional<VersionTag> getTag() const {
|
|
|
|
return m_tag;
|
|
|
|
}
|
|
|
|
|
2022-12-11 14:01:52 -05:00
|
|
|
// Apple clang does not support operator<=>! Yippee!
|
|
|
|
|
|
|
|
constexpr bool operator==(VersionInfo const& other) const {
|
2023-02-22 05:19:17 -05:00
|
|
|
return std::tie(m_major, m_minor, m_patch, m_tag) ==
|
|
|
|
std::tie(other.m_major, other.m_minor, other.m_patch, other.m_tag);
|
2022-12-11 14:01:52 -05:00
|
|
|
}
|
|
|
|
constexpr bool operator<(VersionInfo const& other) const {
|
2023-02-22 05:19:17 -05:00
|
|
|
return std::tie(m_major, m_minor, m_patch, m_tag) <
|
|
|
|
std::tie(other.m_major, other.m_minor, other.m_patch, other.m_tag);
|
2022-12-11 14:01:52 -05:00
|
|
|
}
|
|
|
|
constexpr bool operator<=(VersionInfo const& other) const {
|
2023-02-22 05:19:17 -05:00
|
|
|
return std::tie(m_major, m_minor, m_patch, m_tag) <=
|
|
|
|
std::tie(other.m_major, other.m_minor, other.m_patch, other.m_tag);
|
2022-12-11 14:01:52 -05:00
|
|
|
}
|
|
|
|
constexpr bool operator>(VersionInfo const& other) const {
|
2023-02-22 05:19:17 -05:00
|
|
|
return std::tie(m_major, m_minor, m_patch, m_tag) >
|
|
|
|
std::tie(other.m_major, other.m_minor, other.m_patch, other.m_tag);
|
2022-12-11 14:01:52 -05:00
|
|
|
}
|
|
|
|
constexpr bool operator>=(VersionInfo const& other) const {
|
2023-02-22 05:19:17 -05:00
|
|
|
return std::tie(m_major, m_minor, m_patch, m_tag) >=
|
|
|
|
std::tie(other.m_major, other.m_minor, other.m_patch, other.m_tag);
|
2022-12-08 15:04:02 -05:00
|
|
|
}
|
2022-07-30 12:24:03 -04:00
|
|
|
|
2024-04-25 10:50:00 -04:00
|
|
|
[[deprecated("Use toNonVString or toVString instead")]]
|
2022-12-12 08:45:27 -05:00
|
|
|
std::string toString(bool includeTag = true) const;
|
2024-04-25 10:50:00 -04:00
|
|
|
std::string toVString(bool includeTag = true) const;
|
|
|
|
std::string toNonVString(bool includeTag = true) const;
|
|
|
|
|
2023-12-25 13:18:38 -05:00
|
|
|
friend GEODE_DLL std::string format_as(VersionInfo const& version);
|
2022-07-30 12:24:03 -04:00
|
|
|
};
|
2022-11-22 17:35:08 -05:00
|
|
|
|
2022-12-08 17:28:05 -05:00
|
|
|
class GEODE_DLL ComparableVersionInfo final {
|
|
|
|
protected:
|
|
|
|
VersionInfo m_version;
|
|
|
|
VersionCompare m_compare = VersionCompare::Exact;
|
2023-08-07 14:13:40 -04:00
|
|
|
|
2022-12-08 17:28:05 -05:00
|
|
|
public:
|
|
|
|
constexpr ComparableVersionInfo() = default;
|
|
|
|
constexpr ComparableVersionInfo(
|
|
|
|
VersionInfo const& version,
|
|
|
|
VersionCompare const& compare
|
|
|
|
) : m_version(version), m_compare(compare) {}
|
2022-12-12 11:44:17 -05:00
|
|
|
|
|
|
|
static Result<ComparableVersionInfo> parse(std::string const& string);
|
2022-12-08 17:28:05 -05:00
|
|
|
|
|
|
|
constexpr bool compare(VersionInfo const& version) const {
|
2024-02-15 15:34:20 -05:00
|
|
|
return compareWithReason(version) == VersionCompareResult::Match;
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr VersionCompareResult compareWithReason(VersionInfo const& version) const {
|
2023-08-07 14:13:40 -04:00
|
|
|
if (m_compare == VersionCompare::Any) {
|
2024-02-15 15:34:20 -05:00
|
|
|
return VersionCompareResult::Match;
|
2023-08-07 14:13:40 -04:00
|
|
|
}
|
|
|
|
|
2023-01-31 07:48:34 -05:00
|
|
|
// opposing major versions never match
|
|
|
|
if (m_version.getMajor() != version.getMajor()) {
|
2024-02-15 15:34:20 -05:00
|
|
|
return VersionCompareResult::MajorMismatch;
|
2023-01-31 07:48:34 -05:00
|
|
|
}
|
2023-02-09 10:45:06 -05:00
|
|
|
|
2023-08-07 14:13:40 -04:00
|
|
|
// the comparison works invertedly as a version like "v1.2.0"
|
2023-04-27 11:50:45 -04:00
|
|
|
// should return true for "<=v1.3.0"
|
2022-12-08 17:28:05 -05:00
|
|
|
switch (m_compare) {
|
2023-02-09 10:45:06 -05:00
|
|
|
case VersionCompare::LessEq:
|
2024-02-15 15:34:20 -05:00
|
|
|
return version <= m_version ? VersionCompareResult::Match : VersionCompareResult::TooNew;
|
2023-02-09 10:45:06 -05:00
|
|
|
case VersionCompare::MoreEq:
|
2024-02-15 15:34:20 -05:00
|
|
|
return version >= m_version ? VersionCompareResult::Match : VersionCompareResult::TooOld;
|
2023-05-01 09:06:06 -04:00
|
|
|
case VersionCompare::Less:
|
2024-02-15 15:34:20 -05:00
|
|
|
return version < m_version ? VersionCompareResult::Match : VersionCompareResult::TooNew;
|
2023-05-01 09:06:06 -04:00
|
|
|
case VersionCompare::More:
|
2024-02-15 15:34:20 -05:00
|
|
|
return version > m_version ? VersionCompareResult::Match : VersionCompareResult::TooOld;
|
2023-05-01 09:06:06 -04:00
|
|
|
case VersionCompare::Exact:
|
2024-02-15 15:34:20 -05:00
|
|
|
return version == m_version ? VersionCompareResult::Match :
|
|
|
|
(version > m_version) ? VersionCompareResult::TooOld : VersionCompareResult::TooNew;
|
2023-06-04 10:23:31 -04:00
|
|
|
default:
|
2024-02-15 15:34:20 -05:00
|
|
|
return VersionCompareResult::GenericMismatch;
|
2022-12-08 17:28:05 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-23 15:24:08 -04:00
|
|
|
constexpr VersionInfo getUnderlyingVersion() const {
|
|
|
|
return m_version;
|
|
|
|
}
|
|
|
|
|
2022-12-08 17:28:05 -05:00
|
|
|
std::string toString() const;
|
2023-12-25 13:18:38 -05:00
|
|
|
friend GEODE_DLL std::string format_as(ComparableVersionInfo const& version);
|
2022-12-08 17:28:05 -05:00
|
|
|
};
|
2024-01-30 16:47:30 -05:00
|
|
|
|
|
|
|
bool GEODE_DLL semverCompare(VersionInfo const& current, VersionInfo const& target);
|
2022-07-30 12:24:03 -04:00
|
|
|
}
|
2023-01-27 18:25:19 -05:00
|
|
|
|
|
|
|
template <class V>
|
|
|
|
requires std::is_same_v<V, geode::VersionInfo> || std::is_same_v<V, geode::ComparableVersionInfo>
|
2023-12-30 12:42:53 -05:00
|
|
|
struct matjson::Serialize<V> {
|
|
|
|
static matjson::Value to_json(V const& info) {
|
2023-01-27 18:25:19 -05:00
|
|
|
return info.toString();
|
|
|
|
}
|
|
|
|
|
2024-01-24 09:17:42 -05:00
|
|
|
static bool is_json(matjson::Value const& json) {
|
2024-02-12 10:39:46 -05:00
|
|
|
if (json.is_string()) {
|
|
|
|
auto ver = V::parse(json.as_string());
|
|
|
|
return !ver.isErr();
|
|
|
|
}
|
|
|
|
return false;
|
2024-01-24 09:17:42 -05:00
|
|
|
}
|
|
|
|
|
2023-12-30 12:42:53 -05:00
|
|
|
static V from_json(matjson::Value const& json) {
|
2023-01-27 18:25:19 -05:00
|
|
|
auto ver = V::parse(json.as_string());
|
|
|
|
if (!ver) {
|
2023-12-30 12:42:53 -05:00
|
|
|
throw matjson::JsonException(
|
2023-01-27 18:25:19 -05:00
|
|
|
"Invalid version format: " + ver.unwrapErr()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return ver.unwrap();
|
|
|
|
}
|
2023-03-07 03:56:36 -05:00
|
|
|
};
|