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 <cctype>
26#include <limits>
27#include <locale>
28#include <stdexcept>
29#include <string>
30
31namespace beast {
32
35{
36 std::string ret;
37
38 for (auto const& x : list)
39 {
40 if (!ret.empty())
41 ret += ".";
42 ret += x;
43 }
44
45 return ret;
46}
47
48bool
50{
51 int n;
52
53 // Must be convertible to an integer
54 if (!lexicalCastChecked(n, s))
55 return false;
56
57 // Must not have leading zeroes
58 return std::to_string(n) == s;
59}
60
61bool
62chop(std::string const& what, std::string& input)
63{
64 auto ret = input.find(what);
65
66 if (ret != 0)
67 return false;
68
69 input.erase(0, what.size());
70 return true;
71}
72
73bool
74chopUInt(int& value, int limit, std::string& input)
75{
76 // Must not be empty
77 if (input.empty())
78 return false;
79
80 auto left_iter = std::find_if_not(
81 input.begin(), input.end(), [](std::string::value_type c) {
82 return std::isdigit(c, std::locale::classic());
83 });
84
85 std::string item(input.begin(), left_iter);
86
87 // Must not be empty
88 if (item.empty())
89 return false;
90
91 int n;
92
93 // Must be convertible to an integer
94 if (!lexicalCastChecked(n, item))
95 return false;
96
97 // Must not have leading zeroes
98 if (std::to_string(n) != item)
99 return false;
100
101 // Must not be out of range
102 if (n < 0 || n > limit)
103 return false;
104
105 input.erase(input.begin(), left_iter);
106 value = n;
107
108 return true;
109}
110
111bool
113 std::string& value,
114 bool allowLeadingZeroes,
115 std::string& input)
116{
117 // Must not be empty
118 if (input.empty())
119 return false;
120
121 // Must not have a leading 0
122 if (!allowLeadingZeroes && input[0] == '0')
123 return false;
124
125 auto last = input.find_first_not_of(
126 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-");
127
128 // Must not be empty
129 if (last == 0)
130 return false;
131
132 value = input.substr(0, last);
133 input.erase(0, last);
134 return true;
135}
136
137bool
140 bool allowLeadingZeroes,
141 std::string& input)
142{
143 if (input.empty())
144 return false;
145
146 do
147 {
148 std::string s;
149
150 if (!extract_identifier(s, allowLeadingZeroes, input))
151 return false;
152 identifiers.push_back(s);
153 } while (chop(".", input));
154
155 return true;
156}
157
158//------------------------------------------------------------------------------
159
161 : majorVersion(0), minorVersion(0), patchVersion(0)
162{
163}
164
166{
167 if (!parse(version))
168 throw std::invalid_argument("invalid version string");
169}
170
171bool
173{
174 // May not have leading or trailing whitespace
175 auto left_iter = std::find_if_not(
176 input.begin(), input.end(), [](std::string::value_type c) {
177 return std::isspace(c, std::locale::classic());
178 });
179
180 auto right_iter = std::find_if_not(
181 input.rbegin(),
182 input.rend(),
183 [](std::string::value_type c) {
184 return std::isspace(c, std::locale::classic());
185 })
186 .base();
187
188 // Must not be empty!
189 if (left_iter >= right_iter)
190 return false;
191
192 std::string version(left_iter, right_iter);
193
194 // May not have leading or trailing whitespace
195 if (version != input)
196 return false;
197
198 // Must have major version number
200 return false;
201 if (!chop(".", version))
202 return false;
203
204 // Must have minor version number
206 return false;
207 if (!chop(".", version))
208 return false;
209
210 // Must have patch version number
212 return false;
213
214 // May have pre-release identifier list
215 if (chop("-", version))
216 {
217 if (!extract_identifiers(preReleaseIdentifiers, false, version))
218 return false;
219
220 // Must not be empty
222 return false;
223 }
224
225 // May have metadata identifier list
226 if (chop("+", version))
227 {
228 if (!extract_identifiers(metaData, true, version))
229 return false;
230
231 // Must not be empty
232 if (metaData.empty())
233 return false;
234 }
235
236 return version.empty();
237}
238
241{
242 std::string s;
243
246
248 {
249 s += "-";
251 }
252
253 if (!metaData.empty())
254 {
255 s += "+";
257 }
258
259 return s;
260}
261
262int
264{
265 if (lhs.majorVersion > rhs.majorVersion)
266 return 1;
267 else if (lhs.majorVersion < rhs.majorVersion)
268 return -1;
269
270 if (lhs.minorVersion > rhs.minorVersion)
271 return 1;
272 else if (lhs.minorVersion < rhs.minorVersion)
273 return -1;
274
275 if (lhs.patchVersion > rhs.patchVersion)
276 return 1;
277 else if (lhs.patchVersion < rhs.patchVersion)
278 return -1;
279
280 if (lhs.isPreRelease() || rhs.isPreRelease())
281 {
282 // Pre-releases have a lower precedence
283 if (lhs.isRelease() && rhs.isPreRelease())
284 return 1;
285 else if (lhs.isPreRelease() && rhs.isRelease())
286 return -1;
287
288 // Compare pre-release identifiers
289 for (int i = 0; i <
292 ++i)
293 {
294 // A larger list of identifiers has a higher precedence
295 if (i >= rhs.preReleaseIdentifiers.size())
296 return 1;
297 else if (i >= lhs.preReleaseIdentifiers.size())
298 return -1;
299
300 std::string const& left(lhs.preReleaseIdentifiers[i]);
301 std::string const& right(rhs.preReleaseIdentifiers[i]);
302
303 // Numeric identifiers have lower precedence
304 if (!isNumeric(left) && isNumeric(right))
305 return 1;
306 else if (isNumeric(left) && !isNumeric(right))
307 return -1;
308
309 if (isNumeric(left))
310 {
311 XRPL_ASSERT(
312 isNumeric(right), "beast::compare : both inputs numeric");
313
314 int const iLeft(lexicalCastThrow<int>(left));
315 int const iRight(lexicalCastThrow<int>(right));
316
317 if (iLeft > iRight)
318 return 1;
319 else if (iLeft < iRight)
320 return -1;
321 }
322 else
323 {
324 XRPL_ASSERT(
325 !isNumeric(right),
326 "beast::compare : both inputs non-numeric");
327
328 int result = left.compare(right);
329
330 if (result != 0)
331 return result;
332 }
333 }
334 }
335
336 // metadata is ignored
337
338 return 0;
339}
340
341} // 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:202
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)