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