Mercurial > minori
comparison dep/semver/semver.hpp @ 222:b9f111d84d95
*: move to semver, remove old macos build, add library files
I'm retarded and can't commit files right
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Mon, 08 Jan 2024 13:22:09 -0500 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
221:53211cb1e7f5 | 222:b9f111d84d95 |
---|---|
1 // _____ _ _ | |
2 // / ____| | | (_) | |
3 // | (___ ___ _ __ ___ __ _ _ __ | |_ _ ___ | |
4 // \___ \ / _ \ '_ ` _ \ / _` | '_ \| __| |/ __| | |
5 // ____) | __/ | | | | | (_| | | | | |_| | (__ | |
6 // |_____/ \___|_| |_| |_|\__,_|_| |_|\__|_|\___| | |
7 // __ __ _ _ _____ | |
8 // \ \ / / (_) (_) / ____|_ _ | |
9 // \ \ / /__ _ __ ___ _ ___ _ __ _ _ __ __ _ | | _| |_ _| |_ | |
10 // \ \/ / _ \ '__/ __| |/ _ \| '_ \| | '_ \ / _` | | | |_ _|_ _| | |
11 // \ / __/ | \__ \ | (_) | | | | | | | | (_| | | |____|_| |_| | |
12 // \/ \___|_| |___/_|\___/|_| |_|_|_| |_|\__, | \_____| | |
13 // https://github.com/Neargye/semver __/ | | |
14 // version 0.3.0 |___/ | |
15 // | |
16 // Licensed under the MIT License <http://opensource.org/licenses/MIT>. | |
17 // SPDX-License-Identifier: MIT | |
18 // Copyright (c) 2018 - 2021 Daniil Goncharov <neargye@gmail.com>. | |
19 // Copyright (c) 2020 - 2021 Alexander Gorbunov <naratzul@gmail.com>. | |
20 // | |
21 // Permission is hereby granted, free of charge, to any person obtaining a copy | |
22 // of this software and associated documentation files (the "Software"), to deal | |
23 // in the Software without restriction, including without limitation the rights | |
24 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
25 // copies of the Software, and to permit persons to whom the Software is | |
26 // furnished to do so, subject to the following conditions: | |
27 // | |
28 // The above copyright notice and this permission notice shall be included in all | |
29 // copies or substantial portions of the Software. | |
30 // | |
31 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
32 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
33 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
34 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
35 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
36 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
37 // SOFTWARE. | |
38 | |
39 #ifndef NEARGYE_SEMANTIC_VERSIONING_HPP | |
40 #define NEARGYE_SEMANTIC_VERSIONING_HPP | |
41 | |
42 #define SEMVER_VERSION_MAJOR 0 | |
43 #define SEMVER_VERSION_MINOR 3 | |
44 #define SEMVER_VERSION_PATCH 0 | |
45 | |
46 #include <cstddef> | |
47 #include <cstdint> | |
48 #include <iosfwd> | |
49 #include <limits> | |
50 #include <optional> | |
51 #include <string> | |
52 #include <string_view> | |
53 #if __has_include(<charconv>) | |
54 #include <charconv> | |
55 #else | |
56 #include <system_error> | |
57 #endif | |
58 | |
59 #if defined(SEMVER_CONFIG_FILE) | |
60 #include SEMVER_CONFIG_FILE | |
61 #endif | |
62 | |
63 #if defined(SEMVER_THROW) | |
64 // define SEMVER_THROW(msg) to override semver throw behavior. | |
65 #elif defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) | |
66 # include <stdexcept> | |
67 # define SEMVER_THROW(msg) (throw std::invalid_argument{msg}) | |
68 #else | |
69 # include <cassert> | |
70 # include <cstdlib> | |
71 # define SEMVER_THROW(msg) (assert(!msg), std::abort()) | |
72 #endif | |
73 | |
74 #if defined(__clang__) | |
75 # pragma clang diagnostic push | |
76 # pragma clang diagnostic ignored "-Wmissing-braces" // Ignore warning: suggest braces around initialization of subobject 'return {first, std::errc::invalid_argument};'. | |
77 #endif | |
78 | |
79 #if __cpp_impl_three_way_comparison >= 201907L | |
80 #include <compare> | |
81 #endif | |
82 | |
83 | |
84 namespace semver { | |
85 | |
86 enum struct prerelease : std::uint8_t { | |
87 alpha = 0, | |
88 beta = 1, | |
89 rc = 2, | |
90 none = 3 | |
91 }; | |
92 | |
93 #if __has_include(<charconv>) | |
94 struct from_chars_result : std::from_chars_result { | |
95 [[nodiscard]] constexpr operator bool() const noexcept { return ec == std::errc{}; } | |
96 }; | |
97 | |
98 struct to_chars_result : std::to_chars_result { | |
99 [[nodiscard]] constexpr operator bool() const noexcept { return ec == std::errc{}; } | |
100 }; | |
101 #else | |
102 struct from_chars_result { | |
103 const char* ptr; | |
104 std::errc ec; | |
105 | |
106 [[nodiscard]] constexpr operator bool() const noexcept { return ec == std::errc{}; } | |
107 }; | |
108 | |
109 struct to_chars_result { | |
110 char* ptr; | |
111 std::errc ec; | |
112 | |
113 [[nodiscard]] constexpr operator bool() const noexcept { return ec == std::errc{}; } | |
114 }; | |
115 #endif | |
116 | |
117 // Max version string length = 5(<major>) + 1(.) + 5(<minor>) + 1(.) + 5(<patch>) + 1(-) + 5(<prerelease>) + 1(.) + 5(<prereleaseversion>) = 29. | |
118 inline constexpr auto max_version_string_length = std::size_t{29}; | |
119 | |
120 namespace detail { | |
121 | |
122 inline constexpr auto alpha = std::string_view{"alpha", 5}; | |
123 inline constexpr auto beta = std::string_view{"beta", 4}; | |
124 inline constexpr auto rc = std::string_view{"rc", 2}; | |
125 | |
126 // Min version string length = 1(<major>) + 1(.) + 1(<minor>) + 1(.) + 1(<patch>) = 5. | |
127 inline constexpr auto min_version_string_length = 5; | |
128 | |
129 constexpr char to_lower(char c) noexcept { | |
130 return (c >= 'A' && c <= 'Z') ? static_cast<char>(c + ('a' - 'A')) : c; | |
131 } | |
132 | |
133 constexpr bool is_digit(char c) noexcept { | |
134 return c >= '0' && c <= '9'; | |
135 } | |
136 | |
137 constexpr bool is_space(char c) noexcept { | |
138 return c == ' '; | |
139 } | |
140 | |
141 constexpr bool is_operator(char c) noexcept { | |
142 return c == '<' || c == '>' || c == '='; | |
143 } | |
144 | |
145 constexpr bool is_dot(char c) noexcept { | |
146 return c == '.'; | |
147 } | |
148 | |
149 constexpr bool is_logical_or(char c) noexcept { | |
150 return c == '|'; | |
151 } | |
152 | |
153 constexpr bool is_hyphen(char c) noexcept { | |
154 return c == '-'; | |
155 } | |
156 | |
157 constexpr bool is_letter(char c) noexcept { | |
158 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); | |
159 } | |
160 | |
161 constexpr std::uint16_t to_digit(char c) noexcept { | |
162 return static_cast<std::uint16_t>(c - '0'); | |
163 } | |
164 | |
165 constexpr std::uint8_t length(std::uint16_t x) noexcept { | |
166 if (x < 10) { | |
167 return 1; | |
168 } | |
169 if (x < 100) { | |
170 return 2; | |
171 } | |
172 if (x < 1000) { | |
173 return 3; | |
174 } | |
175 if (x < 10000) { | |
176 return 4; | |
177 } | |
178 return 5; | |
179 } | |
180 | |
181 constexpr std::uint8_t length(prerelease t) noexcept { | |
182 if (t == prerelease::alpha) { | |
183 return static_cast<std::uint8_t>(alpha.length()); | |
184 } else if (t == prerelease::beta) { | |
185 return static_cast<std::uint8_t>(beta.length()); | |
186 } else if (t == prerelease::rc) { | |
187 return static_cast<std::uint8_t>(rc.length()); | |
188 } | |
189 | |
190 return 0; | |
191 } | |
192 | |
193 constexpr bool equals(const char* first, const char* last, std::string_view str) noexcept { | |
194 for (std::size_t i = 0; first != last && i < str.length(); ++i, ++first) { | |
195 if (to_lower(*first) != to_lower(str[i])) { | |
196 return false; | |
197 } | |
198 } | |
199 | |
200 return true; | |
201 } | |
202 | |
203 constexpr char* to_chars(char* str, std::uint16_t x, bool dot = true) noexcept { | |
204 do { | |
205 *(--str) = static_cast<char>('0' + (x % 10)); | |
206 x /= 10; | |
207 } while (x != 0); | |
208 | |
209 if (dot) { | |
210 *(--str) = '.'; | |
211 } | |
212 | |
213 return str; | |
214 } | |
215 | |
216 constexpr char* to_chars(char* str, prerelease t) noexcept { | |
217 const auto p = t == prerelease::alpha | |
218 ? alpha | |
219 : t == prerelease::beta | |
220 ? beta | |
221 : t == prerelease::rc | |
222 ? rc | |
223 : std::string_view{}; | |
224 | |
225 if (p.size() > 0) { | |
226 for (auto it = p.rbegin(); it != p.rend(); ++it) { | |
227 *(--str) = *it; | |
228 } | |
229 *(--str) = '-'; | |
230 } | |
231 | |
232 return str; | |
233 } | |
234 | |
235 constexpr const char* from_chars(const char* first, const char* last, std::uint16_t& d) noexcept { | |
236 if (first != last && is_digit(*first)) { | |
237 std::int32_t t = 0; | |
238 for (; first != last && is_digit(*first); ++first) { | |
239 t = t * 10 + to_digit(*first); | |
240 } | |
241 if (t <= (std::numeric_limits<std::uint16_t>::max)()) { | |
242 d = static_cast<std::uint16_t>(t); | |
243 return first; | |
244 } | |
245 } | |
246 | |
247 return nullptr; | |
248 } | |
249 | |
250 constexpr const char* from_chars(const char* first, const char* last, std::optional<std::uint16_t>& d) noexcept { | |
251 if (first != last && is_digit(*first)) { | |
252 std::int32_t t = 0; | |
253 for (; first != last && is_digit(*first); ++first) { | |
254 t = t * 10 + to_digit(*first); | |
255 } | |
256 if (t <= (std::numeric_limits<std::uint16_t>::max)()) { | |
257 d = static_cast<std::uint16_t>(t); | |
258 return first; | |
259 } | |
260 } | |
261 | |
262 return nullptr; | |
263 } | |
264 | |
265 constexpr const char* from_chars(const char* first, const char* last, prerelease& p) noexcept { | |
266 if (is_hyphen(*first)) { | |
267 ++first; | |
268 } | |
269 | |
270 if (equals(first, last, alpha)) { | |
271 p = prerelease::alpha; | |
272 return first + alpha.length(); | |
273 } else if (equals(first, last, beta)) { | |
274 p = prerelease::beta; | |
275 return first + beta.length(); | |
276 } else if (equals(first, last, rc)) { | |
277 p = prerelease::rc; | |
278 return first + rc.length(); | |
279 } | |
280 | |
281 return nullptr; | |
282 } | |
283 | |
284 constexpr bool check_delimiter(const char* first, const char* last, char d) noexcept { | |
285 return first != last && first != nullptr && *first == d; | |
286 } | |
287 | |
288 template <typename T, typename = void> | |
289 struct resize_uninitialized { | |
290 static auto resize(T& str, std::size_t size) -> std::void_t<decltype(str.resize(size))> { | |
291 str.resize(size); | |
292 } | |
293 }; | |
294 | |
295 template <typename T> | |
296 struct resize_uninitialized<T, std::void_t<decltype(std::declval<T>().__resize_default_init(42))>> { | |
297 static void resize(T& str, std::size_t size) { | |
298 str.__resize_default_init(size); | |
299 } | |
300 }; | |
301 | |
302 } // namespace semver::detail | |
303 | |
304 struct version { | |
305 std::uint16_t major = 0; | |
306 std::uint16_t minor = 1; | |
307 std::uint16_t patch = 0; | |
308 prerelease prerelease_type = prerelease::none; | |
309 std::optional<std::uint16_t> prerelease_number = std::nullopt; | |
310 | |
311 constexpr version(std::uint16_t mj, | |
312 std::uint16_t mn, | |
313 std::uint16_t pt, | |
314 prerelease prt = prerelease::none, | |
315 std::optional<std::uint16_t> prn = std::nullopt) noexcept | |
316 : major{mj}, | |
317 minor{mn}, | |
318 patch{pt}, | |
319 prerelease_type{prt}, | |
320 prerelease_number{prt == prerelease::none ? std::nullopt : prn} { | |
321 } | |
322 | |
323 constexpr version(std::uint16_t mj, | |
324 std::uint16_t mn, | |
325 std::uint16_t pt, | |
326 prerelease prt, | |
327 std::uint16_t prn) noexcept | |
328 : major{mj}, | |
329 minor{mn}, | |
330 patch{pt}, | |
331 prerelease_type{prt}, | |
332 prerelease_number{prt == prerelease::none ? std::nullopt : std::make_optional<std::uint16_t>(prn)} { | |
333 } | |
334 | |
335 explicit constexpr version(std::string_view str) : version(0, 0, 0, prerelease::none, std::nullopt) { | |
336 from_string(str); | |
337 } | |
338 | |
339 constexpr version() = default; // https://semver.org/#how-should-i-deal-with-revisions-in-the-0yz-initial-development-phase | |
340 | |
341 constexpr version(const version&) = default; | |
342 | |
343 constexpr version(version&&) = default; | |
344 | |
345 ~version() = default; | |
346 | |
347 version& operator=(const version&) = default; | |
348 | |
349 version& operator=(version&&) = default; | |
350 | |
351 [[nodiscard]] constexpr from_chars_result from_chars(const char* first, const char* last) noexcept { | |
352 if (first == nullptr || last == nullptr || (last - first) < detail::min_version_string_length) { | |
353 return {first, std::errc::invalid_argument}; | |
354 } | |
355 | |
356 auto next = first; | |
357 if (next = detail::from_chars(next, last, major); detail::check_delimiter(next, last, '.')) { | |
358 if (next = detail::from_chars(++next, last, minor); detail::check_delimiter(next, last, '.')) { | |
359 if (next = detail::from_chars(++next, last, patch); next == last) { | |
360 prerelease_type = prerelease::none; | |
361 prerelease_number = {}; | |
362 return {next, std::errc{}}; | |
363 } else if (detail::check_delimiter(next, last, '-')) { | |
364 if (next = detail::from_chars(next, last, prerelease_type); next == last) { | |
365 prerelease_number = {}; | |
366 return {next, std::errc{}}; | |
367 } else if (detail::check_delimiter(next, last, '.')) { | |
368 if (next = detail::from_chars(++next, last, prerelease_number); next == last) { | |
369 return {next, std::errc{}}; | |
370 } | |
371 } | |
372 } | |
373 } | |
374 } | |
375 | |
376 return {first, std::errc::invalid_argument}; | |
377 } | |
378 | |
379 [[nodiscard]] constexpr to_chars_result to_chars(char* first, char* last) const noexcept { | |
380 const auto length = string_length(); | |
381 if (first == nullptr || last == nullptr || (last - first) < length) { | |
382 return {last, std::errc::value_too_large}; | |
383 } | |
384 | |
385 auto next = first + length; | |
386 if (prerelease_type != prerelease::none) { | |
387 if (prerelease_number.has_value()) { | |
388 next = detail::to_chars(next, prerelease_number.value()); | |
389 } | |
390 next = detail::to_chars(next, prerelease_type); | |
391 } | |
392 next = detail::to_chars(next, patch); | |
393 next = detail::to_chars(next, minor); | |
394 next = detail::to_chars(next, major, false); | |
395 | |
396 return {first + length, std::errc{}}; | |
397 } | |
398 | |
399 [[nodiscard]] constexpr bool from_string_noexcept(std::string_view str) noexcept { | |
400 return from_chars(str.data(), str.data() + str.length()); | |
401 } | |
402 | |
403 constexpr version& from_string(std::string_view str) { | |
404 if (!from_string_noexcept(str)) { | |
405 SEMVER_THROW("semver::version::from_string invalid version."); | |
406 } | |
407 | |
408 return *this; | |
409 } | |
410 | |
411 [[nodiscard]] std::string to_string() const { | |
412 auto str = std::string{}; | |
413 detail::resize_uninitialized<std::string>::resize(str, string_length()); | |
414 if (!to_chars(str.data(), str.data() + str.length())) { | |
415 SEMVER_THROW("semver::version::to_string invalid version."); | |
416 } | |
417 | |
418 return str; | |
419 } | |
420 | |
421 [[nodiscard]] constexpr std::uint8_t string_length() const noexcept { | |
422 // (<major>) + 1(.) + (<minor>) + 1(.) + (<patch>) | |
423 auto length = detail::length(major) + detail::length(minor) + detail::length(patch) + 2; | |
424 if (prerelease_type != prerelease::none) { | |
425 // + 1(-) + (<prerelease>) | |
426 length += detail::length(prerelease_type) + 1; | |
427 if (prerelease_number.has_value()) { | |
428 // + 1(.) + (<prereleaseversion>) | |
429 length += detail::length(prerelease_number.value()) + 1; | |
430 } | |
431 } | |
432 | |
433 return static_cast<std::uint8_t>(length); | |
434 } | |
435 | |
436 [[nodiscard]] constexpr int compare(const version& other) const noexcept { | |
437 if (major != other.major) { | |
438 return major - other.major; | |
439 } | |
440 | |
441 if (minor != other.minor) { | |
442 return minor - other.minor; | |
443 } | |
444 | |
445 if (patch != other.patch) { | |
446 return patch - other.patch; | |
447 } | |
448 | |
449 if (prerelease_type != other.prerelease_type) { | |
450 return static_cast<std::uint8_t>(prerelease_type) - static_cast<std::uint8_t>(other.prerelease_type); | |
451 } | |
452 | |
453 if (prerelease_number.has_value()) { | |
454 if (other.prerelease_number.has_value()) { | |
455 return prerelease_number.value() - other.prerelease_number.value(); | |
456 } | |
457 return 1; | |
458 } else if (other.prerelease_number.has_value()) { | |
459 return -1; | |
460 } | |
461 | |
462 return 0; | |
463 } | |
464 }; | |
465 | |
466 [[nodiscard]] constexpr bool operator==(const version& lhs, const version& rhs) noexcept { | |
467 return lhs.compare(rhs) == 0; | |
468 } | |
469 | |
470 [[nodiscard]] constexpr bool operator!=(const version& lhs, const version& rhs) noexcept { | |
471 return lhs.compare(rhs) != 0; | |
472 } | |
473 | |
474 [[nodiscard]] constexpr bool operator>(const version& lhs, const version& rhs) noexcept { | |
475 return lhs.compare(rhs) > 0; | |
476 } | |
477 | |
478 [[nodiscard]] constexpr bool operator>=(const version& lhs, const version& rhs) noexcept { | |
479 return lhs.compare(rhs) >= 0; | |
480 } | |
481 | |
482 [[nodiscard]] constexpr bool operator<(const version& lhs, const version& rhs) noexcept { | |
483 return lhs.compare(rhs) < 0; | |
484 } | |
485 | |
486 [[nodiscard]] constexpr bool operator<=(const version& lhs, const version& rhs) noexcept { | |
487 return lhs.compare(rhs) <= 0; | |
488 } | |
489 | |
490 #if __cpp_impl_three_way_comparison >= 201907L | |
491 [[nodiscard]] constexpr std::strong_ordering operator<=>(const version& lhs, const version& rhs) { | |
492 int compare = lhs.compare(rhs); | |
493 if ( compare == 0 ) | |
494 return std::strong_ordering::equal; | |
495 if ( compare > 0 ) | |
496 return std::strong_ordering::greater; | |
497 return std::strong_ordering::less; | |
498 } | |
499 #endif | |
500 | |
501 [[nodiscard]] constexpr version operator""_version(const char* str, std::size_t length) { | |
502 return version{std::string_view{str, length}}; | |
503 } | |
504 | |
505 [[nodiscard]] constexpr bool valid(std::string_view str) noexcept { | |
506 return version{}.from_string_noexcept(str); | |
507 } | |
508 | |
509 [[nodiscard]] constexpr from_chars_result from_chars(const char* first, const char* last, version& v) noexcept { | |
510 return v.from_chars(first, last); | |
511 } | |
512 | |
513 [[nodiscard]] constexpr to_chars_result to_chars(char* first, char* last, const version& v) noexcept { | |
514 return v.to_chars(first, last); | |
515 } | |
516 | |
517 [[nodiscard]] constexpr std::optional<version> from_string_noexcept(std::string_view str) noexcept { | |
518 if (version v{}; v.from_string_noexcept(str)) { | |
519 return v; | |
520 } | |
521 | |
522 return std::nullopt; | |
523 } | |
524 | |
525 [[nodiscard]] constexpr version from_string(std::string_view str) { | |
526 return version{str}; | |
527 } | |
528 | |
529 [[nodiscard]] inline std::string to_string(const version& v) { | |
530 return v.to_string(); | |
531 } | |
532 | |
533 template <typename Char, typename Traits> | |
534 inline std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, const version& v) { | |
535 for (const auto c : v.to_string()) { | |
536 os.put(c); | |
537 } | |
538 | |
539 return os; | |
540 } | |
541 | |
542 inline namespace comparators { | |
543 | |
544 enum struct comparators_option : std::uint8_t { | |
545 exclude_prerelease, | |
546 include_prerelease | |
547 }; | |
548 | |
549 [[nodiscard]] constexpr int compare(const version& lhs, const version& rhs, comparators_option option = comparators_option::include_prerelease) noexcept { | |
550 if (option == comparators_option::exclude_prerelease) { | |
551 return version{lhs.major, lhs.minor, lhs.patch}.compare(version{rhs.major, rhs.minor, rhs.patch}); | |
552 } | |
553 return lhs.compare(rhs); | |
554 } | |
555 | |
556 [[nodiscard]] constexpr bool equal_to(const version& lhs, const version& rhs, comparators_option option = comparators_option::include_prerelease) noexcept { | |
557 return compare(lhs, rhs, option) == 0; | |
558 } | |
559 | |
560 [[nodiscard]] constexpr bool not_equal_to(const version& lhs, const version& rhs, comparators_option option = comparators_option::include_prerelease) noexcept { | |
561 return compare(lhs, rhs, option) != 0; | |
562 } | |
563 | |
564 [[nodiscard]] constexpr bool greater(const version& lhs, const version& rhs, comparators_option option = comparators_option::include_prerelease) noexcept { | |
565 return compare(lhs, rhs, option) > 0; | |
566 } | |
567 | |
568 [[nodiscard]] constexpr bool greater_equal(const version& lhs, const version& rhs, comparators_option option = comparators_option::include_prerelease) noexcept { | |
569 return compare(lhs, rhs, option) >= 0; | |
570 } | |
571 | |
572 [[nodiscard]] constexpr bool less(const version& lhs, const version& rhs, comparators_option option = comparators_option::include_prerelease) noexcept { | |
573 return compare(lhs, rhs, option) < 0; | |
574 } | |
575 | |
576 [[nodiscard]] constexpr bool less_equal(const version& lhs, const version& rhs, comparators_option option = comparators_option::include_prerelease) noexcept { | |
577 return compare(lhs, rhs, option) <= 0; | |
578 } | |
579 | |
580 } // namespace semver::comparators | |
581 | |
582 namespace range { | |
583 | |
584 namespace detail { | |
585 | |
586 using namespace semver::detail; | |
587 | |
588 class range { | |
589 public: | |
590 constexpr explicit range(std::string_view str) noexcept : parser{str} {} | |
591 | |
592 constexpr bool satisfies(const version& ver, bool include_prerelease) { | |
593 const bool has_prerelease = ver.prerelease_type != prerelease::none; | |
594 | |
595 do { | |
596 if (is_logical_or_token()) { | |
597 parser.advance_token(range_token_type::logical_or); | |
598 } | |
599 | |
600 bool contains = true; | |
601 bool allow_compare = include_prerelease; | |
602 | |
603 while (is_operator_token() || is_number_token()) { | |
604 const auto range = parser.parse_range(); | |
605 const bool equal_without_tags = equal_to(range.ver, ver, comparators_option::exclude_prerelease); | |
606 | |
607 if (has_prerelease && equal_without_tags) { | |
608 allow_compare = true; | |
609 } | |
610 | |
611 if (!range.satisfies(ver)) { | |
612 contains = false; | |
613 break; | |
614 } | |
615 } | |
616 | |
617 if (has_prerelease) { | |
618 if (allow_compare && contains) { | |
619 return true; | |
620 } | |
621 } else if (contains) { | |
622 return true; | |
623 } | |
624 | |
625 } while (is_logical_or_token()); | |
626 | |
627 return false; | |
628 } | |
629 | |
630 private: | |
631 enum struct range_operator : std::uint8_t { | |
632 less, | |
633 less_or_equal, | |
634 greater, | |
635 greater_or_equal, | |
636 equal | |
637 }; | |
638 | |
639 struct range_comparator { | |
640 range_operator op; | |
641 version ver; | |
642 | |
643 constexpr bool satisfies(const version& version) const { | |
644 switch (op) { | |
645 case range_operator::equal: | |
646 return version == ver; | |
647 case range_operator::greater: | |
648 return version > ver; | |
649 case range_operator::greater_or_equal: | |
650 return version >= ver; | |
651 case range_operator::less: | |
652 return version < ver; | |
653 case range_operator::less_or_equal: | |
654 return version <= ver; | |
655 default: | |
656 SEMVER_THROW("semver::range unexpected operator."); | |
657 } | |
658 } | |
659 }; | |
660 | |
661 enum struct range_token_type : std::uint8_t { | |
662 none, | |
663 number, | |
664 range_operator, | |
665 dot, | |
666 logical_or, | |
667 hyphen, | |
668 prerelease, | |
669 end_of_line | |
670 }; | |
671 | |
672 struct range_token { | |
673 range_token_type type = range_token_type::none; | |
674 std::uint16_t number = 0; | |
675 range_operator op = range_operator::equal; | |
676 prerelease prerelease_type = prerelease::none; | |
677 }; | |
678 | |
679 struct range_lexer { | |
680 std::string_view text; | |
681 std::size_t pos; | |
682 | |
683 constexpr explicit range_lexer(std::string_view text) noexcept : text{text}, pos{0} {} | |
684 | |
685 constexpr range_token get_next_token() noexcept { | |
686 while (!end_of_line()) { | |
687 | |
688 if (is_space(text[pos])) { | |
689 advance(1); | |
690 continue; | |
691 } | |
692 | |
693 if (is_logical_or(text[pos])) { | |
694 advance(2); | |
695 return {range_token_type::logical_or}; | |
696 } | |
697 | |
698 if (is_operator(text[pos])) { | |
699 const auto op = get_operator(); | |
700 return {range_token_type::range_operator, 0, op}; | |
701 } | |
702 | |
703 if (is_digit(text[pos])) { | |
704 const auto number = get_number(); | |
705 return {range_token_type::number, number}; | |
706 } | |
707 | |
708 if (is_dot(text[pos])) { | |
709 advance(1); | |
710 return {range_token_type::dot}; | |
711 } | |
712 | |
713 if (is_hyphen(text[pos])) { | |
714 advance(1); | |
715 return {range_token_type::hyphen}; | |
716 } | |
717 | |
718 if (is_letter(text[pos])) { | |
719 const auto prerelease = get_prerelease(); | |
720 return {range_token_type::prerelease, 0, range_operator::equal, prerelease}; | |
721 } | |
722 } | |
723 | |
724 return {range_token_type::end_of_line}; | |
725 } | |
726 | |
727 constexpr bool end_of_line() const noexcept { return pos >= text.length(); } | |
728 | |
729 constexpr void advance(std::size_t i) noexcept { | |
730 pos += i; | |
731 } | |
732 | |
733 constexpr range_operator get_operator() noexcept { | |
734 if (text[pos] == '<') { | |
735 advance(1); | |
736 if (text[pos] == '=') { | |
737 advance(1); | |
738 return range_operator::less_or_equal; | |
739 } | |
740 return range_operator::less; | |
741 } else if (text[pos] == '>') { | |
742 advance(1); | |
743 if (text[pos] == '=') { | |
744 advance(1); | |
745 return range_operator::greater_or_equal; | |
746 } | |
747 return range_operator::greater; | |
748 } else if (text[pos] == '=') { | |
749 advance(1); | |
750 return range_operator::equal; | |
751 } | |
752 | |
753 return range_operator::equal; | |
754 } | |
755 | |
756 constexpr std::uint16_t get_number() noexcept { | |
757 const auto first = text.data() + pos; | |
758 const auto last = text.data() + text.length(); | |
759 if (std::uint16_t n{}; from_chars(first, last, n) != nullptr) { | |
760 advance(length(n)); | |
761 return n; | |
762 } | |
763 | |
764 return 0; | |
765 } | |
766 | |
767 constexpr prerelease get_prerelease() noexcept { | |
768 const auto first = text.data() + pos; | |
769 const auto last = text.data() + text.length(); | |
770 if (first > last) { | |
771 advance(1); | |
772 return prerelease::none; | |
773 } | |
774 | |
775 if (prerelease p{}; from_chars(first, last, p) != nullptr) { | |
776 advance(length(p)); | |
777 return p; | |
778 } | |
779 | |
780 advance(1); | |
781 | |
782 return prerelease::none; | |
783 } | |
784 }; | |
785 | |
786 struct range_parser { | |
787 range_lexer lexer; | |
788 range_token current_token; | |
789 | |
790 constexpr explicit range_parser(std::string_view str) : lexer{str}, current_token{range_token_type::none} { | |
791 advance_token(range_token_type::none); | |
792 } | |
793 | |
794 constexpr void advance_token(range_token_type token_type) { | |
795 if (current_token.type != token_type) { | |
796 SEMVER_THROW("semver::range unexpected token."); | |
797 } | |
798 current_token = lexer.get_next_token(); | |
799 } | |
800 | |
801 constexpr range_comparator parse_range() { | |
802 if (current_token.type == range_token_type::number) { | |
803 const auto version = parse_version(); | |
804 return {range_operator::equal, version}; | |
805 } else if (current_token.type == range_token_type::range_operator) { | |
806 const auto range_operator = current_token.op; | |
807 advance_token(range_token_type::range_operator); | |
808 const auto version = parse_version(); | |
809 return {range_operator, version}; | |
810 } | |
811 | |
812 return {range_operator::equal, version{}}; | |
813 } | |
814 | |
815 constexpr version parse_version() { | |
816 const auto major = parse_number(); | |
817 | |
818 advance_token(range_token_type::dot); | |
819 const auto minor = parse_number(); | |
820 | |
821 advance_token(range_token_type::dot); | |
822 const auto patch = parse_number(); | |
823 | |
824 prerelease prerelease = prerelease::none; | |
825 std::optional<std::uint16_t> prerelease_number = std::nullopt; | |
826 | |
827 if (current_token.type == range_token_type::hyphen) { | |
828 advance_token(range_token_type::hyphen); | |
829 prerelease = parse_prerelease(); | |
830 if (current_token.type == range_token_type::dot) { | |
831 advance_token(range_token_type::dot); | |
832 prerelease_number = parse_number(); | |
833 } | |
834 } | |
835 | |
836 return {major, minor, patch, prerelease, prerelease_number}; | |
837 } | |
838 | |
839 constexpr std::uint16_t parse_number() { | |
840 const auto token = current_token; | |
841 advance_token(range_token_type::number); | |
842 | |
843 return token.number; | |
844 } | |
845 | |
846 constexpr prerelease parse_prerelease() { | |
847 const auto token = current_token; | |
848 advance_token(range_token_type::prerelease); | |
849 | |
850 return token.prerelease_type; | |
851 } | |
852 }; | |
853 | |
854 [[nodiscard]] constexpr bool is_logical_or_token() const noexcept { | |
855 return parser.current_token.type == range_token_type::logical_or; | |
856 } | |
857 [[nodiscard]] constexpr bool is_operator_token() const noexcept { | |
858 return parser.current_token.type == range_token_type::range_operator; | |
859 } | |
860 | |
861 [[nodiscard]] constexpr bool is_number_token() const noexcept { | |
862 return parser.current_token.type == range_token_type::number; | |
863 } | |
864 | |
865 range_parser parser; | |
866 }; | |
867 | |
868 } // namespace semver::range::detail | |
869 | |
870 enum struct satisfies_option : std::uint8_t { | |
871 exclude_prerelease, | |
872 include_prerelease | |
873 }; | |
874 | |
875 constexpr bool satisfies(const version& ver, std::string_view str, satisfies_option option = satisfies_option::exclude_prerelease) { | |
876 switch (option) { | |
877 case satisfies_option::exclude_prerelease: | |
878 return detail::range{str}.satisfies(ver, false); | |
879 case satisfies_option::include_prerelease: | |
880 return detail::range{str}.satisfies(ver, true); | |
881 default: | |
882 SEMVER_THROW("semver::range unexpected satisfies_option."); | |
883 } | |
884 } | |
885 | |
886 } // namespace semver::range | |
887 | |
888 // Version lib semver. | |
889 inline constexpr auto semver_version = version{SEMVER_VERSION_MAJOR, SEMVER_VERSION_MINOR, SEMVER_VERSION_PATCH}; | |
890 | |
891 } // namespace semver | |
892 | |
893 #if defined(__clang__) | |
894 # pragma clang diagnostic pop | |
895 #endif | |
896 | |
897 #endif // NEARGYE_SEMANTIC_VERSIONING_HPP |