summaryrefslogtreecommitdiffstats
path: root/source/core/slang-semantic-version.h
blob: 096606e087ca38ab73f4d61b530776548343e41f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// 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