rippled
Loading...
Searching...
No Matches
SemanticVersion.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of Beast: https://github.com/vinniefalco/Beast
4 Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpl/beast/core/LexicalCast.h>
21#include <xrpl/beast/core/SemanticVersion.h>
22#include <xrpl/beast/utility/instrumentation.h>
23
24#include <algorithm>
25#include <locale>
26
27namespace beast {
28
31{
32 std::string ret;
33
34 for (auto const& x : list)
35 {
36 if (!ret.empty())
37 ret += ".";
38 ret += x;
39 }
40
41 return ret;
42}
43
44bool
46{
47 int n;
48
49 // Must be convertible to an integer
50 if (!lexicalCastChecked(n, s))
51 return false;
52
53 // Must not have leading zeroes
54 return std::to_string(n) == s;
55}
56
57bool
58chop(std::string const& what, std::string& input)
59{
60 auto ret = input.find(what);
61
62 if (ret != 0)
63 return false;
64
65 input.erase(0, what.size());
66 return true;
67}
68
69bool
70chopUInt(int& value, int limit, std::string& input)
71{
72 // Must not be empty
73 if (input.empty())
74 return false;
75
76 auto left_iter = std::find_if_not(
77 input.begin(), input.end(), [](std::string::value_type c) {
78 return std::isdigit(c, std::locale::classic());
79 });
80
81 std::string item(input.begin(), left_iter);
82
83 // Must not be empty
84 if (item.empty())
85 return false;
86
87 int n;
88
89 // Must be convertible to an integer
90 if (!lexicalCastChecked(n, item))
91 return false;
92
93 // Must not have leading zeroes
94 if (std::to_string(n) != item)
95 return false;
96
97 // Must not be out of range
98 if (n < 0 || n > limit)
99 return false;
100
101 input.erase(input.begin(), left_iter);
102 value = n;
103
104 return true;
105}
106
107bool
109 std::string& value,
110 bool allowLeadingZeroes,
111 std::string& input)
112{
113 // Must not be empty
114 if (input.empty())
115 return false;
116
117 // Must not have a leading 0
118 if (!allowLeadingZeroes && input[0] == '0')
119 return false;
120
121 auto last = input.find_first_not_of(
122 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-");
123
124 // Must not be empty
125 if (last == 0)
126 return false;
127
128 value = input.substr(0, last);
129 input.erase(0, last);
130 return true;
131}
132
133bool
136 bool allowLeadingZeroes,
137 std::string& input)
138{
139 if (input.empty())
140 return false;
141
142 do
143 {
144 std::string s;
145
146 if (!extract_identifier(s, allowLeadingZeroes, input))
147 return false;
148 identifiers.push_back(s);
149 } while (chop(".", input));
150
151 return true;
152}
153
154//------------------------------------------------------------------------------
155
157 : majorVersion(0), minorVersion(0), patchVersion(0)
158{
159}
160
162{
163 if (!parse(version))
164 throw std::invalid_argument("invalid version string");
165}
166
167bool
169{
170 // May not have leading or trailing whitespace
171 auto left_iter = std::find_if_not(
172 input.begin(), input.end(), [](std::string::value_type c) {
173 return std::isspace(c, std::locale::classic());
174 });
175
176 auto right_iter = std::find_if_not(
177 input.rbegin(),
178 input.rend(),
179 [](std::string::value_type c) {
180 return std::isspace(c, std::locale::classic());
181 })
182 .base();
183
184 // Must not be empty!
185 if (left_iter >= right_iter)
186 return false;
187
188 std::string version(left_iter, right_iter);
189
190 // May not have leading or trailing whitespace
191 if (version != input)
192 return false;
193
194 // Must have major version number
196 return false;
197 if (!chop(".", version))
198 return false;
199
200 // Must have minor version number
202 return false;
203 if (!chop(".", version))
204 return false;
205
206 // Must have patch version number
208 return false;
209
210 // May have pre-release identifier list
211 if (chop("-", version))
212 {
213 if (!extract_identifiers(preReleaseIdentifiers, false, version))
214 return false;
215
216 // Must not be empty
218 return false;
219 }
220
221 // May have metadata identifier list
222 if (chop("+", version))
223 {
224 if (!extract_identifiers(metaData, true, version))
225 return false;
226
227 // Must not be empty
228 if (metaData.empty())
229 return false;
230 }
231
232 return version.empty();
233}
234
237{
238 std::string s;
239
242
244 {
245 s += "-";
247 }
248
249 if (!metaData.empty())
250 {
251 s += "+";
253 }
254
255 return s;
256}
257
258int
260{
261 if (lhs.majorVersion > rhs.majorVersion)
262 return 1;
263 else if (lhs.majorVersion < rhs.majorVersion)
264 return -1;
265
266 if (lhs.minorVersion > rhs.minorVersion)
267 return 1;
268 else if (lhs.minorVersion < rhs.minorVersion)
269 return -1;
270
271 if (lhs.patchVersion > rhs.patchVersion)
272 return 1;
273 else if (lhs.patchVersion < rhs.patchVersion)
274 return -1;
275
276 if (lhs.isPreRelease() || rhs.isPreRelease())
277 {
278 // Pre-releases have a lower precedence
279 if (lhs.isRelease() && rhs.isPreRelease())
280 return 1;
281 else if (lhs.isPreRelease() && rhs.isRelease())
282 return -1;
283
284 // Compare pre-release identifiers
285 for (int i = 0; i <
288 ++i)
289 {
290 // A larger list of identifiers has a higher precedence
291 if (i >= rhs.preReleaseIdentifiers.size())
292 return 1;
293 else if (i >= lhs.preReleaseIdentifiers.size())
294 return -1;
295
296 std::string const& left(lhs.preReleaseIdentifiers[i]);
297 std::string const& right(rhs.preReleaseIdentifiers[i]);
298
299 // Numeric identifiers have lower precedence
300 if (!isNumeric(left) && isNumeric(right))
301 return 1;
302 else if (isNumeric(left) && !isNumeric(right))
303 return -1;
304
305 if (isNumeric(left))
306 {
307 XRPL_ASSERT(
308 isNumeric(right), "beast::compare : both inputs numeric");
309
310 int const iLeft(lexicalCastThrow<int>(left));
311 int const iRight(lexicalCastThrow<int>(right));
312
313 if (iLeft > iRight)
314 return 1;
315 else if (iLeft < iRight)
316 return -1;
317 }
318 else
319 {
320 XRPL_ASSERT(
321 !isNumeric(right),
322 "beast::compare : both inputs non-numeric");
323
324 int result = left.compare(right);
325
326 if (result != 0)
327 return result;
328 }
329 }
330 }
331
332 // metadata is ignored
333
334 return 0;
335}
336
337} // namespace beast
T begin(T... args)
A Semantic Version number.
bool parse(std::string const &input)
Parse a semantic version string.
identifier_list preReleaseIdentifiers
bool isPreRelease() const noexcept
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.
Definition: LexicalCast.h:201
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)