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(input.begin(), input.end(), [](std::string::value_type c) {
62 return std::isdigit(c, std::locale::classic());
63 });
64
65 std::string item(input.begin(), left_iter);
66
67 // Must not be empty
68 if (item.empty())
69 return false;
70
71 int n;
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 return 1;
238 else if (lhs.majorVersion < rhs.majorVersion)
239 return -1;
240
241 if (lhs.minorVersion > rhs.minorVersion)
242 return 1;
243 else if (lhs.minorVersion < rhs.minorVersion)
244 return -1;
245
246 if (lhs.patchVersion > rhs.patchVersion)
247 return 1;
248 else if (lhs.patchVersion < rhs.patchVersion)
249 return -1;
250
251 if (lhs.isPreRelease() || rhs.isPreRelease())
252 {
253 // Pre-releases have a lower precedence
254 if (lhs.isRelease() && rhs.isPreRelease())
255 return 1;
256 else if (lhs.isPreRelease() && rhs.isRelease())
257 return -1;
258
259 // Compare pre-release identifiers
260 for (int i = 0;
262 ++i)
263 {
264 // A larger list of identifiers has a higher precedence
265 if (i >= rhs.preReleaseIdentifiers.size())
266 return 1;
267 else if (i >= lhs.preReleaseIdentifiers.size())
268 return -1;
269
270 std::string const& left(lhs.preReleaseIdentifiers[i]);
271 std::string const& right(rhs.preReleaseIdentifiers[i]);
272
273 // Numeric identifiers have lower precedence
274 if (!isNumeric(left) && isNumeric(right))
275 return 1;
276 else if (isNumeric(left) && !isNumeric(right))
277 return -1;
278
279 if (isNumeric(left))
280 {
281 XRPL_ASSERT(isNumeric(right), "beast::compare : both inputs numeric");
282
283 int const iLeft(lexicalCastThrow<int>(left));
284 int const iRight(lexicalCastThrow<int>(right));
285
286 if (iLeft > iRight)
287 return 1;
288 else if (iLeft < iRight)
289 return -1;
290 }
291 else
292 {
293 XRPL_ASSERT(!isNumeric(right), "beast::compare : both inputs non-numeric");
294
295 int result = left.compare(right);
296
297 if (result != 0)
298 return result;
299 }
300 }
301 }
302
303 // metadata is ignored
304
305 return 0;
306}
307
308} // 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)