Skip to content

Commit

Permalink
Move to custom semver versioning for MO2. (#154)
Browse files Browse the repository at this point in the history
  • Loading branch information
Holt59 authored Aug 5, 2024
1 parent b57425c commit 7e13c48
Show file tree
Hide file tree
Showing 11 changed files with 562 additions and 45 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
edit
.vscode
CMakeLists.txt.user
/msbuild.log
/*std*.log
Expand Down
1 change: 1 addition & 0 deletions include/uibase/exceptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <stdexcept>

#include <QObject>
#include <QString>

#include "dllimport.h"
Expand Down
8 changes: 7 additions & 1 deletion include/uibase/imoinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#include "imodlist.h"
#include "iprofile.h"
#include "versioninfo.h"
#include "versioning.h"

namespace MOBase
{
Expand Down Expand Up @@ -121,7 +122,12 @@ class QDLLEXPORT IOrganizer : public QObject
/**
* @return the running version of Mod Organizer
*/
virtual VersionInfo appVersion() const = 0;
[[deprecated]] virtual VersionInfo appVersion() const = 0;

/**
* @return the running version of Mod Organizer
*/
virtual Version version() const = 0;

/**
* @brief create a new mod with the specified name
Expand Down
181 changes: 181 additions & 0 deletions include/uibase/versioning.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
#pragma once

#include <compare>
#include <string>
#include <variant>
#include <vector>

#include <QFlags>
#include <QString>

#include "dllimport.h"
#include "exceptions.h"

namespace MOBase
{

class InvalidVersionException : public Exception
{
public:
using Exception::Exception;
};

// class representing a Version object
//
// valid versions are an "extension" of SemVer (see https://semver.org/) with the
// following tweaks:
// - version can have a sub-patch, i.e., x.y.z.p, which are normally not allowed by
// SemVer
// - non-integer pre-release identifiers are limited to dev, alpha (a), beta (b) and rc,
// and dev is lower than alpha (according to SemVer, the pre-release should be
// ordered alphabetically)
// - the '-' between version and pre-release can be made optional, and also the '.'
// between pre-releases segment
//
// the extension from SemVer are only meant to be used by MO2 and USVFS versioning,
// plugins and extensions should follow SemVer standard (and not use dev), this is
// mainly
// - for back-compatibility purposes, because USVFS versioning contains sub-patches and
// there are old MO2 releases with sub-patch
// - because MO2 is not going to become MO3, so having an extra level make sense
//
// unlike VersionInfo, this class is immutable and only hold valid versions
//
class QDLLEXPORT Version
{
public:
enum class ParseMode
{
// official semver parsing with pre-release limited to dev, alpha/a, beta/b and rc
//
SemVer,

// MO2 parsing, e.g., 2.5.1rc1 - this either parse a string with no pre-release
// information (e.g. 2.5.1) or with a single pre-release + a version (e.g., 2.5.1a1
// or 2.5.2rc1)
//
// this mode can parse sub-patch (SemVer mode cannot)
//
MO2
};

enum class FormatMode
{
// show subpatch even if subpatch is 0
//
ForceSubPatch = 0b0001,

// do not add separators between version and pre-release (-) or between pre-release
// segments (.)
//
NoSeparator = 0b0010,

// uses short form for alpha and beta (a/b instead of alpha/beta)
//
ShortAlphaBeta = 0b0100,

// do not add metadata even if present
//
NoMetadata = 0b1000
};
Q_DECLARE_FLAGS(FormatModes, FormatMode);

// condensed format, no separator, short alpha/beta and no metadata
//
static constexpr auto FormatCondensed = FormatModes{
FormatMode::NoSeparator, FormatMode::ShortAlphaBeta, FormatMode::NoMetadata};

enum class ReleaseType
{
Development, // -dev
Alpha, // -alpha, -a
Beta, // -beta, -b
ReleaseCandidate, // -rc
};
using enum ReleaseType;

public: // parsing
// parse version from the given string, throw InvalidVersionException if the string
// cannot be parsed
//
static Version parse(QString const& value, ParseMode mode = ParseMode::SemVer);

public: // constructors
Version(int major, int minor, int patch, QString metadata = {});
Version(int major, int minor, int patch, int subpatch, QString metadata = {});

Version(int major, int minor, int patch, ReleaseType type, QString metadata = {});
Version(int major, int minor, int patch, int subpatch, ReleaseType type,
QString metadata = {});

Version(int major, int minor, int patch, ReleaseType type, int prerelease,
QString metadata = {});
Version(int major, int minor, int patch, int subpatch, ReleaseType type,
int prerelease, QString metadata = {});

Version(int major, int minor, int patch, int subpatch,
std::vector<std::variant<int, ReleaseType>> prereleases,
QString metadata = {});

public: // special member functions
Version(const Version&) = default;
Version(Version&&) = default;

Version& operator=(const Version&) = default;
Version& operator=(Version&&) = default;

public:
// check if this version corresponds to a pre-release version (dev, alpha, beta, etc.)
//
bool isPreRelease() const { return !m_PreReleases.empty(); }

// retrieve major, minor, patch and sub-patch of this version
//
int major() const { return m_Major; }
int minor() const { return m_Minor; }
int patch() const { return m_Patch; }
int subpatch() const { return m_SubPatch; }

// retrieve pre-releases information for this version
//
const auto& preReleases() const { return m_PreReleases; }

// retrieve build metadata, if any, otherwise return an empty string
//
const auto& buildMetadata() const { return m_BuildMetadata; }

// convert this version to a string
//
QString string(const FormatModes& modes = {}) const;

private:
// major.minor.patch
int m_Major, m_Minor, m_Patch, m_SubPatch;

// pre-release information
std::vector<std::variant<int, ReleaseType>> m_PreReleases;

// metadata
QString m_BuildMetadata;
};

QDLLEXPORT std::strong_ordering operator<=>(const Version& lhs, const Version& rhs);

inline bool operator==(const Version& lhs, const Version& rhs)
{
return (lhs <=> rhs) == 0;
}

Q_DECLARE_OPERATORS_FOR_FLAGS(Version::FormatModes);

} // namespace MOBase

template <class CharT>
struct std::formatter<MOBase::Version, CharT> : std::formatter<QString, CharT>
{
template <class FmtContext>
FmtContext::iterator format(const MOBase::Version& v, FmtContext& ctx) const
{
return std::formatter<QString, CharT>::format(v.string(), ctx);
}
};
4 changes: 2 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ set(root_headers
../include/uibase/steamutility.h
../include/uibase/strings.h
../include/uibase/utility.h
../include/uibase/versioning.h
../include/uibase/versioninfo.h
)
set(interface_headers
Expand Down Expand Up @@ -115,14 +116,14 @@ mo2_target_sources(uibase
nxmurl.cpp
pch.cpp
pluginrequirements.cpp
pluginsetting.cpp
registry.cpp
report.cpp
safewritefile.cpp
scopeguard.cpp
steamutility.cpp
strings.cpp
utility.cpp
versioning.cpp
versioninfo.cpp
)

Expand All @@ -133,7 +134,6 @@ mo2_target_sources(uibase
ifiletree.cpp
imodrepositorybridge.cpp
imoinfo.cpp
iplugininstaller.cpp
)

mo2_target_sources(uibase
Expand Down
18 changes: 0 additions & 18 deletions src/iplugininstaller.cpp

This file was deleted.

24 changes: 0 additions & 24 deletions src/pluginsetting.cpp

This file was deleted.

Loading

0 comments on commit 7e13c48

Please sign in to comment.