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;
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(
62 input.begin(), input.end(), [](std::string::value_type c) {
63 return std::isdigit(c, std::locale::classic());
64 });
65
66 std::string item(input.begin(), left_iter);
67
68 // Must not be empty
69 if (item.empty())
70 return false;
71
72 int n;
73
74 // Must be convertible to an integer
75 if (!lexicalCastChecked(n, item))
76 return false;
77
78 // Must not have leading zeroes
79 if (std::to_string(n) != item)
80 return false;
81
82 // Must not be out of range
83 if (n < 0 || n > limit)
84 return false;
85
86 input.erase(input.begin(), left_iter);
87 value = n;
88
89 return true;
90}
91
92bool
94 std::string& value,
95 bool allowLeadingZeroes,
96 std::string& input)
97{
98 // Must not be empty
99 if (input.empty())
100 return false;
101
102 // Must not have a leading 0
103 if (!allowLeadingZeroes && input[0] == '0')
104 return false;
105
106 auto last = input.find_first_not_of(
107 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-");
108
109 // Must not be empty
110 if (last == 0)
111 return false;
112
113 value = input.substr(0, last);
114 input.erase(0, last);
115 return true;
116}
117
118bool
121 bool allowLeadingZeroes,
122 std::string& input)
123{
124 if (input.empty())
125 return false;
126
127 do
128 {
129 std::string s;
130
131 if (!extract_identifier(s, allowLeadingZeroes, input))
132 return false;
133 identifiers.push_back(s);
134 } while (chop(".", input));
135
136 return true;
137}
138
139//------------------------------------------------------------------------------
140
142 : majorVersion(0), minorVersion(0), patchVersion(0)
143{
144}
145
147{
148 if (!parse(version))
149 throw std::invalid_argument("invalid version string");
150}
151
152bool
154{
155 // May not have leading or trailing whitespace
156 auto left_iter = std::find_if_not(
157 input.begin(), input.end(), [](std::string::value_type c) {
158 return std::isspace(c, std::locale::classic());
159 });
160
161 auto right_iter = std::find_if_not(
162 input.rbegin(),
163 input.rend(),
164 [](std::string::value_type c) {
165 return std::isspace(c, std::locale::classic());
166 })
167 .base();
168
169 // Must not be empty!
170 if (left_iter >= right_iter)
171 return false;
172
173 std::string version(left_iter, right_iter);
174
175 // May not have leading or trailing whitespace
176 if (version != input)
177 return false;
178
179 // Must have major version number
181 return false;
182 if (!chop(".", version))
183 return false;
184
185 // Must have minor version number
187 return false;
188 if (!chop(".", version))
189 return false;
190
191 // Must have patch version number
193 return false;
194
195 // May have pre-release identifier list
196 if (chop("-", version))
197 {
198 if (!extract_identifiers(preReleaseIdentifiers, false, version))
199 return false;
200
201 // Must not be empty
203 return false;
204 }
205
206 // May have metadata identifier list
207 if (chop("+", version))
208 {
209 if (!extract_identifiers(metaData, true, version))
210 return false;
211
212 // Must not be empty
213 if (metaData.empty())
214 return false;
215 }
216
217 return version.empty();
218}
219
222{
223 std::string s;
224
227
229 {
230 s += "-";
232 }
233
234 if (!metaData.empty())
235 {
236 s += "+";
238 }
239
240 return s;
241}
242
243int
245{
246 if (lhs.majorVersion > rhs.majorVersion)
247 return 1;
248 else if (lhs.majorVersion < rhs.majorVersion)
249 return -1;
250
251 if (lhs.minorVersion > rhs.minorVersion)
252 return 1;
253 else if (lhs.minorVersion < rhs.minorVersion)
254 return -1;
255
256 if (lhs.patchVersion > rhs.patchVersion)
257 return 1;
258 else if (lhs.patchVersion < rhs.patchVersion)
259 return -1;
260
261 if (lhs.isPreRelease() || rhs.isPreRelease())
262 {
263 // Pre-releases have a lower precedence
264 if (lhs.isRelease() && rhs.isPreRelease())
265 return 1;
266 else if (lhs.isPreRelease() && rhs.isRelease())
267 return -1;
268
269 // Compare pre-release identifiers
270 for (int i = 0; i <
273 ++i)
274 {
275 // A larger list of identifiers has a higher precedence
276 if (i >= rhs.preReleaseIdentifiers.size())
277 return 1;
278 else if (i >= lhs.preReleaseIdentifiers.size())
279 return -1;
280
281 std::string const& left(lhs.preReleaseIdentifiers[i]);
282 std::string const& right(rhs.preReleaseIdentifiers[i]);
283
284 // Numeric identifiers have lower precedence
285 if (!isNumeric(left) && isNumeric(right))
286 return 1;
287 else if (isNumeric(left) && !isNumeric(right))
288 return -1;
289
290 if (isNumeric(left))
291 {
292 XRPL_ASSERT(
293 isNumeric(right), "beast::compare : both inputs numeric");
294
295 int const iLeft(lexicalCastThrow<int>(left));
296 int const iRight(lexicalCastThrow<int>(right));
297
298 if (iLeft > iRight)
299 return 1;
300 else if (iLeft < iRight)
301 return -1;
302 }
303 else
304 {
305 XRPL_ASSERT(
306 !isNumeric(right),
307 "beast::compare : both inputs non-numeric");
308
309 int result = left.compare(right);
310
311 if (result != 0)
312 return result;
313 }
314 }
315 }
316
317 // metadata is ignored
318
319 return 0;
320}
321
322} // 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.
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)