// slang-semantic-version.h #ifndef SLANG_SEMANTIC_VERSION_H #define SLANG_SEMANTIC_VERSION_H #include "../core/slang-basic.h" #include "../core/slang-hash.h" namespace Slang { struct SemanticVersion { typedef SemanticVersion ThisType; typedef UInt64 RawValue; SemanticVersion() : m_major(0), m_minor(0), m_patch(0) { } SemanticVersion(int inMajor, int inMinor = 0, int inPatch = 0) : m_major(uint16_t(inMajor)), m_minor(uint16_t(inMinor)), m_patch(uint32_t(inPatch)) { } void reset() { m_major = 0; m_minor = 0; m_patch = 0; } /// All zeros means nothing is set bool isSet() const { return m_major || m_minor || m_patch; } RawValue getRawValue() const { return (RawValue(m_major) << 48) | (RawValue(m_minor) << 32) | m_patch; } void setRawValue(RawValue v) { set(int(v >> 48), int((v >> 32) & 0xffff), int(v & 0xffffffff)); } static SemanticVersion fromRaw(RawValue rawValue) { SemanticVersion result; result.setRawValue(rawValue); return result; } void set(int major, int minor, int patch = 0) { SLANG_ASSERT(major >= 0 && minor >= 0 && patch >= 0); m_major = uint16_t(major); m_minor = uint16_t(minor); m_patch = uint32_t(patch); } /// Get hash value HashCode getHashCode() const { return Slang::getHashCode(getRawValue()); } static SlangResult parse(const UnownedStringSlice& value, SemanticVersion& outVersion); static SlangResult parse( const UnownedStringSlice& value, char separatorChar, SemanticVersion& outVersion); static ThisType getEarliest(const ThisType* versions, Count count); static ThisType getLatest(const ThisType* versions, Count count); /// Determine if this version is backwards-compatible with `otherVersion`. /// /// Examples of when to apply this: /// /// * Code for this version is asked to load data produced by `otherVersion`. /// /// * Code for this version is asked if it provides the API of `otherVersion`. /// /// This uses the rules of semantic versioning, so it should only /// be applied to versions that obey those rules. /// bool isBackwardsCompatibleWith(const ThisType& otherVersion) const; void append(StringBuilder& buf) const; bool operator>(const ThisType& rhs) const { return getRawValue() > rhs.getRawValue(); } bool operator>=(const ThisType& rhs) const { return getRawValue() >= rhs.getRawValue(); } bool operator<(const ThisType& rhs) const { return getRawValue() < rhs.getRawValue(); } bool operator<=(const ThisType& rhs) const { return getRawValue() <= rhs.getRawValue(); } bool operator==(const ThisType& rhs) const { return getRawValue() == rhs.getRawValue(); } bool operator!=(const ThisType& rhs) const { return getRawValue() != rhs.getRawValue(); } uint16_t m_major; uint16_t m_minor; uint32_t m_patch; ///< Patch number. Can actually be quite large for some code bases. }; /* Adds to the semantic versioning information for an incomplete version that can be matched */ struct MatchSemanticVersion { typedef MatchSemanticVersion ThisType; enum class Kind { Unknown, ///< Not known Past, ///< Some unknown past version Future, ///< Some future unknown version Major, ///< Major version is defined (minor is in effect undefined) MajorMinor, ///< Major and minor version are defined MajorMinorPatch, ///< All elements of semantic version are defined }; /// True if has a complete version bool hasCompleteVersion() const { return m_kind == Kind::MajorMinorPatch; } /// True if has some version information bool hasVersion() const { return Index(m_kind) >= Index(Kind::Major); } void set(Index major) { m_kind = Kind::Major; m_version = SemanticVersion(int(major), 0, 0); } void set(Index major, Index minor) { m_kind = Kind::MajorMinor; m_version = SemanticVersion(int(major), int(minor), 0); } void set(Index major, Index minor, Index patch) { m_kind = Kind::MajorMinorPatch; m_version = SemanticVersion(int(major), int(minor), int(patch)); } void append(StringBuilder& buf) const; static MatchSemanticVersion makeFuture() { MatchSemanticVersion version; version.m_kind = Kind::Future; return version; } /// Finds the 'best' version based on the versions passed. /// Doesn't follow strict semantic rules as will attempt to return the closest 'any' in past or /// future If none can be found, returns an empty semantic version static SemanticVersion findAnyBest( const SemanticVersion* versions, Count count, const ThisType& matchVersion); MatchSemanticVersion() : m_kind(Kind::Unknown) { } MatchSemanticVersion(Kind kind, const SemanticVersion& version) : m_kind(kind), m_version(version) { } Kind m_kind; SemanticVersion m_version; }; } // namespace Slang #endif