Unravel Engine C++ Reference
Loading...
Searching...
No Matches
version.cpp
Go to the documentation of this file.
1#include "version.h"
2
3#include <cctype>
4#include <cstddef>
5
6namespace version
7{
8
9namespace
10{
11
12auto ltrim(std::string_view s) -> std::string_view
13{
14 while(!s.empty() && std::isspace(static_cast<unsigned char>(s.front())))
15 {
16 s.remove_prefix(1);
17 }
18 return s;
19}
20
21auto rtrim(std::string_view s) -> std::string_view
22{
23 while(!s.empty() && std::isspace(static_cast<unsigned char>(s.back())))
24 {
25 s.remove_suffix(1);
26 }
27 return s;
28}
29
30auto trim(std::string_view s) -> std::string_view
31{
32 return rtrim(ltrim(s));
33}
34
35auto strip_optional_v(std::string_view s) -> std::string_view
36{
37 s = trim(s);
38 if(!s.empty() && (s.front() == 'v' || s.front() == 'V'))
39 {
40 s.remove_prefix(1);
41 }
42 return s;
43}
44
45// Values over this cap are treated as malformed - protects against numeric
46// overflow on very long digit runs without constraining realistic versions.
47constexpr long long k_int_parse_cap = 1'000'000'000LL;
48
49auto parse_int_token(std::string_view s, std::size_t& pos, int& out) -> bool
50{
51 if(pos >= s.size() || !std::isdigit(static_cast<unsigned char>(s[pos])))
52 {
53 return false;
54 }
55
56 long long val = 0;
57 while(pos < s.size() && std::isdigit(static_cast<unsigned char>(s[pos])))
58 {
59 val = val * 10 + (s[pos] - '0');
60 if(val > k_int_parse_cap)
61 {
62 return false;
63 }
64 ++pos;
65 }
66 out = static_cast<int>(val);
67 return true;
68}
69
70auto expect_char(std::string_view s, std::size_t& pos, char c) -> bool
71{
72 if(pos >= s.size() || s[pos] != c)
73 {
74 return false;
75 }
76 ++pos;
77 return true;
78}
79
80} // namespace
81
82auto engine_version::to_string() const -> std::string
83{
84 std::string out;
85 out.reserve(24);
86 out += std::to_string(major);
87 out += '.';
88 out += std::to_string(minor);
89 out += '.';
90 out += std::to_string(patch);
91
92 // Only emit the commit/sha suffix when present; a plain "1.0.0" should
93 // roundtrip as "1.0.0" (not "1.0.0-0").
94 if(commit_count != 0 || !sha.empty())
95 {
96 out += '-';
97 out += std::to_string(commit_count);
98 if(!sha.empty())
99 {
100 out += '-';
101 out += sha;
102 }
103 }
104 return out;
105}
106
107auto operator==(const engine_version& a, const engine_version& b) -> bool
108{
109 return a.major == b.major && a.minor == b.minor && a.patch == b.patch && a.commit_count == b.commit_count;
110}
111
112auto operator<=>(const engine_version& a, const engine_version& b) -> std::strong_ordering
113{
114 if(auto c = a.major <=> b.major; c != 0)
115 {
116 return c;
117 }
118 if(auto c = a.minor <=> b.minor; c != 0)
119 {
120 return c;
121 }
122 if(auto c = a.patch <=> b.patch; c != 0)
123 {
124 return c;
125 }
126 return a.commit_count <=> b.commit_count;
127}
128
129auto parse(std::string_view text) -> std::optional<engine_version>
130{
132 v.original = std::string(trim(text));
133
134 std::string_view s = trim(strip_optional_v(text));
135 std::size_t pos = 0;
136
137 if(!parse_int_token(s, pos, v.major))
138 {
139 return std::nullopt;
140 }
141 if(!expect_char(s, pos, '.'))
142 {
143 return std::nullopt;
144 }
145 if(!parse_int_token(s, pos, v.minor))
146 {
147 return std::nullopt;
148 }
149 if(!expect_char(s, pos, '.'))
150 {
151 return std::nullopt;
152 }
153 if(!parse_int_token(s, pos, v.patch))
154 {
155 return std::nullopt;
156 }
157
158 // Optional "-commit_count"
159 if(pos < s.size() && s[pos] == '-')
160 {
161 ++pos;
162 if(!parse_int_token(s, pos, v.commit_count))
163 {
164 return std::nullopt;
165 }
166
167 // Optional "-sha" (sha kept verbatim, including any leading 'g')
168 if(pos < s.size() && s[pos] == '-')
169 {
170 ++pos;
171 std::string_view rest = trim(s.substr(pos));
172 if(!rest.empty())
173 {
174 v.sha = std::string(rest);
175 pos = s.size();
176 }
177 }
178 }
179
180 if(!trim(s.substr(pos)).empty())
181 {
182 return std::nullopt;
183 }
184
185 return v;
186}
187
188auto compare(const engine_version& a, const engine_version& b) -> int
189{
190 const auto cmp = a <=> b;
191 if(cmp < 0)
192 {
193 return -1;
194 }
195 if(cmp > 0)
196 {
197 return 1;
198 }
199 return 0;
200}
201
203{
204 if(auto parsed = parse(get_full()))
205 {
206 return *parsed;
207 }
208 return {};
209}
210
211auto get_major() -> std::string
212{
213 return VERSION_MAJOR;
214}
215
216auto get_minor() -> std::string
217{
218 return VERSION_MINOR;
219}
220
221auto get_patch() -> std::string
222{
223 return VERSION_PATCH;
224}
225
226auto get_full() -> std::string
227{
228 return VERSION;
229}
230
231} // namespace version
entt::handle b
entt::handle a
Hash specialization for batch_key to enable use in std::unordered_map.
auto parse(std::string_view text) -> std::optional< engine_version >
Parses a version string. Returns nullopt on malformed input.
Definition version.cpp:129
auto get_full() -> std::string
Definition version.cpp:226
auto get_patch() -> std::string
Definition version.cpp:221
auto compare(const engine_version &a, const engine_version &b) -> int
Definition version.cpp:188
auto get_current() -> engine_version
Definition version.cpp:202
auto get_minor() -> std::string
Definition version.cpp:216
auto operator==(const engine_version &a, const engine_version &b) -> bool
Definition version.cpp:107
auto operator<=>(const engine_version &a, const engine_version &b) -> std::strong_ordering
Definition version.cpp:112
auto get_major() -> std::string
Definition version.cpp:211
std::string sha
Git SHA suffix (including any leading 'g'), or empty if absent.
Definition version.h:25
auto to_string() const -> std::string
Definition version.cpp:82
int commit_count
Commits since the tagged release (e.g. the 66 in 1.0.0-66-g...).
Definition version.h:23
std::string original
Definition version.h:28