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 |
