rippled
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 <ripple/beast/core/LexicalCast.h>
21 #include <ripple/beast/core/SemanticVersion.h>
22 
23 #include <algorithm>
24 #include <cassert>
25 #include <locale>
26 
27 namespace 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 
44 bool
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 
57 bool
58 chop(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 
69 bool
70 chopUInt(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 
107 bool
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 
133 bool
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 
167 bool
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 
258 int
259 compare(SemanticVersion const& lhs, SemanticVersion const& rhs)
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  assert(isNumeric(right));
308 
309  int const iLeft(lexicalCastThrow<int>(left));
310  int const iRight(lexicalCastThrow<int>(right));
311 
312  if (iLeft > iRight)
313  return 1;
314  else if (iLeft < iRight)
315  return -1;
316  }
317  else
318  {
319  assert(!isNumeric(right));
320 
321  int result = left.compare(right);
322 
323  if (result != 0)
324  return result;
325  }
326  }
327  }
328 
329  // metadata is ignored
330 
331  return 0;
332 }
333 
334 } // namespace beast
locale
beast::SemanticVersion::isRelease
bool isRelease() const noexcept
Definition: SemanticVersion.h:63
std::string
STL class.
beast::chop
bool chop(std::string const &what, std::string &input)
Definition: SemanticVersion.cpp:58
std::vector< std::string >
std::string::find
T find(T... args)
std::string::size
T size(T... args)
beast::SemanticVersion::print
std::string print() const
Produce a string from semantic version components.
Definition: SemanticVersion.cpp:236
beast::extract_identifier
bool extract_identifier(std::string &value, bool allowLeadingZeroes, std::string &input)
Definition: SemanticVersion.cpp:108
beast::compare
int compare(SemanticVersion const &lhs, SemanticVersion const &rhs)
Compare two SemanticVersions against each other.
Definition: SemanticVersion.cpp:259
beast::SemanticVersion::isPreRelease
bool isPreRelease() const noexcept
Definition: SemanticVersion.h:68
beast::SemanticVersion
A Semantic Version number.
Definition: SemanticVersion.h:35
std::string::find_first_not_of
T find_first_not_of(T... args)
algorithm
std::vector::push_back
T push_back(T... args)
beast::SemanticVersion::SemanticVersion
SemanticVersion()
Definition: SemanticVersion.cpp:156
beast::SemanticVersion::minorVersion
int minorVersion
Definition: SemanticVersion.h:41
std::to_string
T to_string(T... args)
std::string::erase
T erase(T... args)
beast::SemanticVersion::preReleaseIdentifiers
identifier_list preReleaseIdentifiers
Definition: SemanticVersion.h:44
std::invalid_argument
STL class.
beast::SemanticVersion::metaData
identifier_list metaData
Definition: SemanticVersion.h:45
beast::SemanticVersion::majorVersion
int majorVersion
Definition: SemanticVersion.h:40
std::string::rend
T rend(T... args)
std::string::substr
T substr(T... args)
beast::chopUInt
bool chopUInt(int &value, int limit, std::string &input)
Definition: SemanticVersion.cpp:70
beast::SemanticVersion::parse
bool parse(std::string const &input)
Parse a semantic version string.
Definition: SemanticVersion.cpp:168
beast::extract_identifiers
bool extract_identifiers(SemanticVersion::identifier_list &identifiers, bool allowLeadingZeroes, std::string &input)
Definition: SemanticVersion.cpp:134
beast::lexicalCastChecked
bool lexicalCastChecked(Out &out, In in)
Intelligently convert from one type to another.
Definition: LexicalCast.h:266
std::string::begin
T begin(T... args)
cassert
beast::SemanticVersion::patchVersion
int patchVersion
Definition: SemanticVersion.h:42
std::string::empty
T empty(T... args)
std::string::end
T end(T... args)
beast::print_identifiers
std::string print_identifiers(SemanticVersion::identifier_list const &list)
Definition: SemanticVersion.cpp:30
std::max
T max(T... args)
beast::isNumeric
bool isNumeric(std::string const &s)
Definition: SemanticVersion.cpp:45
std::numeric_limits
std::string::rbegin
T rbegin(T... args)
beast
Definition: base_uint.h:641