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