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