rippled
Loading...
Searching...
No Matches
SemanticVersion.cpp
1#include <xrpl/beast/core/LexicalCast.h>
2#include <xrpl/beast/core/SemanticVersion.h>
3#include <xrpl/beast/utility/instrumentation.h>
4
5#include <algorithm>
6#include <cctype>
7#include <limits>
8#include <locale>
9#include <stdexcept>
10#include <string>
11
12namespace beast {
13
16{
17 std::string ret;
18
19 for (auto const& x : list)
20 {
21 if (!ret.empty())
22 ret += ".";
23 ret += x;
24 }
25
26 return ret;
27}
28
29bool
31{
32 int n = 0;
33
34 // Must be convertible to an integer
35 if (!lexicalCastChecked(n, s))
36 return false;
37
38 // Must not have leading zeroes
39 return std::to_string(n) == s;
40}
41
42bool
43chop(std::string const& what, std::string& input)
44{
45 auto ret = input.find(what);
46
47 if (ret != 0)
48 return false;
49
50 input.erase(0, what.size());
51 return true;
52}
53
54bool
55chopUInt(int& value, int limit, std::string& input)
56{
57 // Must not be empty
58 if (input.empty())
59 return false;
60
61 auto left_iter = std::find_if_not(input.begin(), input.end(), [](std::string::value_type c) {
62 return std::isdigit(c, std::locale::classic());
63 });
64
65 std::string const item(input.begin(), left_iter);
66
67 // Must not be empty
68 if (item.empty())
69 return false;
70
71 int n = 0;
72
73 // Must be convertible to an integer
74 if (!lexicalCastChecked(n, item))
75 return false;
76
77 // Must not have leading zeroes
78 if (std::to_string(n) != item)
79 return false;
80
81 // Must not be out of range
82 if (n < 0 || n > limit)
83 return false;
84
85 input.erase(input.begin(), left_iter);
86 value = n;
87
88 return true;
89}
90
91bool
92extract_identifier(std::string& value, bool allowLeadingZeroes, std::string& input)
93{
94 // Must not be empty
95 if (input.empty())
96 return false;
97
98 // Must not have a leading 0
99 if (!allowLeadingZeroes && input[0] == '0')
100 return false;
101
102 auto last =
103 input.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-");
104
105 // Must not be empty
106 if (last == 0)
107 return false;
108
109 value = input.substr(0, last);
110 input.erase(0, last);
111 return true;
112}
113
114bool
117 bool allowLeadingZeroes,
118 std::string& input)
119{
120 if (input.empty())
121 return false;
122
123 do
124 {
125 std::string s;
126
127 if (!extract_identifier(s, allowLeadingZeroes, input))
128 return false;
129 identifiers.push_back(s);
130 } while (chop(".", input));
131
132 return true;
133}
134
135//------------------------------------------------------------------------------
136
137SemanticVersion::SemanticVersion() : majorVersion(0), minorVersion(0), patchVersion(0)
138{
139}
140
142{
143 if (!parse(version))
144 throw std::invalid_argument("invalid version string");
145}
146
147bool
149{
150 // May not have leading or trailing whitespace
151 auto left_iter = std::find_if_not(input.begin(), input.end(), [](std::string::value_type c) {
152 return std::isspace(c, std::locale::classic());
153 });
154
155 auto right_iter = std::find_if_not(input.rbegin(), input.rend(), [](std::string::value_type c) {
156 return std::isspace(c, std::locale::classic());
157 }).base();
158
159 // Must not be empty!
160 if (left_iter >= right_iter)
161 return false;
162
163 std::string version(left_iter, right_iter);
164
165 // May not have leading or trailing whitespace
166 if (version != input)
167 return false;
168
169 // Must have major version number
171 return false;
172 if (!chop(".", version))
173 return false;
174
175 // Must have minor version number
177 return false;
178 if (!chop(".", version))
179 return false;
180
181 // Must have patch version number
183 return false;
184
185 // May have pre-release identifier list
186 if (chop("-", version))
187 {
188 if (!extract_identifiers(preReleaseIdentifiers, false, version))
189 return false;
190
191 // Must not be empty
193 return false;
194 }
195
196 // May have metadata identifier list
197 if (chop("+", version))
198 {
199 if (!extract_identifiers(metaData, true, version))
200 return false;
201
202 // Must not be empty
203 if (metaData.empty())
204 return false;
205 }
206
207 return version.empty();
208}
209
212{
213 std::string s;
214
217
219 {
220 s += "-";
222 }
223
224 if (!metaData.empty())
225 {
226 s += "+";
228 }
229
230 return s;
231}
232
233int
235{
236 if (lhs.majorVersion > rhs.majorVersion)
237 {
238 return 1;
239 }
240 if (lhs.majorVersion < rhs.majorVersion)
241 {
242 return -1;
243 }
244
245 if (lhs.minorVersion > rhs.minorVersion)
246 {
247 return 1;
248 }
249 if (lhs.minorVersion < rhs.minorVersion)
250 {
251 return -1;
252 }
253
254 if (lhs.patchVersion > rhs.patchVersion)
255 {
256 return 1;
257 }
258 if (lhs.patchVersion < rhs.patchVersion)
259 {
260 return -1;
261 }
262
263 if (lhs.isPreRelease() || rhs.isPreRelease())
264 {
265 // Pre-releases have a lower precedence
266 if (lhs.isRelease() && rhs.isPreRelease())
267 {
268 return 1;
269 }
270 if (lhs.isPreRelease() && rhs.isRelease())
271 {
272 return -1;
273 }
274
275 // Compare pre-release identifiers
276 for (int i = 0;
278 ++i)
279 {
280 // A larger list of identifiers has a higher precedence
281 if (i >= rhs.preReleaseIdentifiers.size())
282 {
283 return 1;
284 }
285 if (i >= lhs.preReleaseIdentifiers.size())
286 {
287 return -1;
288 }
289
290 std::string const& left(lhs.preReleaseIdentifiers[i]);
291 std::string const& right(rhs.preReleaseIdentifiers[i]);
292
293 // Numeric identifiers have lower precedence
294 if (!isNumeric(left) && isNumeric(right))
295 {
296 return 1;
297 }
298 if (isNumeric(left) && !isNumeric(right))
299 {
300 return -1;
301 }
302
303 if (isNumeric(left))
304 {
305 XRPL_ASSERT(isNumeric(right), "beast::compare : both inputs numeric");
306
307 int const iLeft(lexicalCastThrow<int>(left));
308 int const iRight(lexicalCastThrow<int>(right));
309
310 if (iLeft > iRight)
311 {
312 return 1;
313 }
314 if (iLeft < iRight)
315 {
316 return -1;
317 }
318 }
319 else
320 {
321 XRPL_ASSERT(!isNumeric(right), "beast::compare : both inputs non-numeric");
322
323 int const result = left.compare(right);
324
325 if (result != 0)
326 return result;
327 }
328 }
329 }
330
331 // metadata is ignored
332
333 return 0;
334}
335
336} // namespace beast
T begin(T... args)
A Semantic Version number.
identifier_list preReleaseIdentifiers
bool isPreRelease() const noexcept
bool parse(std::string_view input)
Parse a semantic version string.
std::string print() const
Produce a string from semantic version components.
bool isRelease() const noexcept
identifier_list metaData
T empty(T... args)
T end(T... args)
T erase(T... args)
T find_first_not_of(T... args)
T find(T... args)
T max(T... args)
int compare(SemanticVersion const &lhs, SemanticVersion const &rhs)
Compare two SemanticVersions against each other.
bool isNumeric(std::string const &s)
bool chop(std::string const &what, std::string &input)
std::string print_identifiers(SemanticVersion::identifier_list const &list)
bool extract_identifiers(SemanticVersion::identifier_list &identifiers, bool allowLeadingZeroes, std::string &input)
bool extract_identifier(std::string &value, bool allowLeadingZeroes, std::string &input)
bool lexicalCastChecked(Out &out, In in)
Intelligently convert from one type to another.
bool chopUInt(int &value, int limit, std::string &input)
T push_back(T... args)
T rbegin(T... args)
T rend(T... args)
T size(T... args)
T substr(T... args)
T to_string(T... args)