Mercurial > minori
changeset 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 | 53211cb1e7f5 |
children | 84e0a3c4737a adc20fa321c1 |
files | .builds/macos.yml dep/semver/semver.hpp include/core/version.h include/library/library.h src/library/library.cc |
diffstat | 5 files changed, 954 insertions(+), 55 deletions(-) [+] |
line wrap: on
line diff
--- a/.builds/macos.yml Mon Jan 08 13:21:08 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -image: debian/bookworm -packages: - - qtbase5-dev - - qttools5-dev - - cmake - - clang - - libcurl4-openssl-dev - - wget - - fuse - - file - - pkg-config - # osxcross deps start - - libxml2-dev - - libssl-dev - - lzma-dev - - python3 - - xz-utils - - bzip2 - - cpio - - libbz2-1.0 - - zlib1g-dev -sources: - - https://hg.sr.ht/~mrpapersonic/minori - - https://github.com/efa/osxcross # use fork until #409 is pulled -environment: - BUILD_SUBMITTER: hg.sr.ht - MACOSX_DEPLOYMENT_TARGET: 10.9 - UNATTENDED: 1 -tasks: - - build: | - # set up osxcross - cd osxcross/tarballs - wget "https://github.com/phracker/MacOSX-SDKs/releases/download/11.3/MacOSX10.9.sdk.tar.xz" - cd .. - export TARGET_DIR="$PWD/target" - ./build.sh - export PATH="$TARGET_DIR/bin:$PATH" - cd .. - - # deps - osxcross-macports install qt5-qtbase qt5-qttools - - # actually build the damn thing - CC="o64-clang" CXX="o64-clang++" CXXFLAGS="-stdlib=libc++" cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER= -DCMAKE_CXX_COMPILER=o64-clang++ - make -triggers: - - action: email - condition: failure - to: Paper <mrpapersonic@gmail.com>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dep/semver/semver.hpp Mon Jan 08 13:22:09 2024 -0500 @@ -0,0 +1,897 @@ +// _____ _ _ +// / ____| | | (_) +// | (___ ___ _ __ ___ __ _ _ __ | |_ _ ___ +// \___ \ / _ \ '_ ` _ \ / _` | '_ \| __| |/ __| +// ____) | __/ | | | | | (_| | | | | |_| | (__ +// |_____/ \___|_| |_| |_|\__,_|_| |_|\__|_|\___| +// __ __ _ _ _____ +// \ \ / / (_) (_) / ____|_ _ +// \ \ / /__ _ __ ___ _ ___ _ __ _ _ __ __ _ | | _| |_ _| |_ +// \ \/ / _ \ '__/ __| |/ _ \| '_ \| | '_ \ / _` | | | |_ _|_ _| +// \ / __/ | \__ \ | (_) | | | | | | | | (_| | | |____|_| |_| +// \/ \___|_| |___/_|\___/|_| |_|_|_| |_|\__, | \_____| +// https://github.com/Neargye/semver __/ | +// version 0.3.0 |___/ +// +// Licensed under the MIT License <http://opensource.org/licenses/MIT>. +// SPDX-License-Identifier: MIT +// Copyright (c) 2018 - 2021 Daniil Goncharov <neargye@gmail.com>. +// Copyright (c) 2020 - 2021 Alexander Gorbunov <naratzul@gmail.com>. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef NEARGYE_SEMANTIC_VERSIONING_HPP +#define NEARGYE_SEMANTIC_VERSIONING_HPP + +#define SEMVER_VERSION_MAJOR 0 +#define SEMVER_VERSION_MINOR 3 +#define SEMVER_VERSION_PATCH 0 + +#include <cstddef> +#include <cstdint> +#include <iosfwd> +#include <limits> +#include <optional> +#include <string> +#include <string_view> +#if __has_include(<charconv>) +#include <charconv> +#else +#include <system_error> +#endif + +#if defined(SEMVER_CONFIG_FILE) +#include SEMVER_CONFIG_FILE +#endif + +#if defined(SEMVER_THROW) +// define SEMVER_THROW(msg) to override semver throw behavior. +#elif defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) +# include <stdexcept> +# define SEMVER_THROW(msg) (throw std::invalid_argument{msg}) +#else +# include <cassert> +# include <cstdlib> +# define SEMVER_THROW(msg) (assert(!msg), std::abort()) +#endif + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wmissing-braces" // Ignore warning: suggest braces around initialization of subobject 'return {first, std::errc::invalid_argument};'. +#endif + +#if __cpp_impl_three_way_comparison >= 201907L +#include <compare> +#endif + + +namespace semver { + +enum struct prerelease : std::uint8_t { + alpha = 0, + beta = 1, + rc = 2, + none = 3 +}; + +#if __has_include(<charconv>) +struct from_chars_result : std::from_chars_result { + [[nodiscard]] constexpr operator bool() const noexcept { return ec == std::errc{}; } +}; + +struct to_chars_result : std::to_chars_result { + [[nodiscard]] constexpr operator bool() const noexcept { return ec == std::errc{}; } +}; +#else +struct from_chars_result { + const char* ptr; + std::errc ec; + + [[nodiscard]] constexpr operator bool() const noexcept { return ec == std::errc{}; } +}; + +struct to_chars_result { + char* ptr; + std::errc ec; + + [[nodiscard]] constexpr operator bool() const noexcept { return ec == std::errc{}; } +}; +#endif + +// Max version string length = 5(<major>) + 1(.) + 5(<minor>) + 1(.) + 5(<patch>) + 1(-) + 5(<prerelease>) + 1(.) + 5(<prereleaseversion>) = 29. +inline constexpr auto max_version_string_length = std::size_t{29}; + +namespace detail { + +inline constexpr auto alpha = std::string_view{"alpha", 5}; +inline constexpr auto beta = std::string_view{"beta", 4}; +inline constexpr auto rc = std::string_view{"rc", 2}; + +// Min version string length = 1(<major>) + 1(.) + 1(<minor>) + 1(.) + 1(<patch>) = 5. +inline constexpr auto min_version_string_length = 5; + +constexpr char to_lower(char c) noexcept { + return (c >= 'A' && c <= 'Z') ? static_cast<char>(c + ('a' - 'A')) : c; +} + +constexpr bool is_digit(char c) noexcept { + return c >= '0' && c <= '9'; +} + +constexpr bool is_space(char c) noexcept { + return c == ' '; +} + +constexpr bool is_operator(char c) noexcept { + return c == '<' || c == '>' || c == '='; +} + +constexpr bool is_dot(char c) noexcept { + return c == '.'; +} + +constexpr bool is_logical_or(char c) noexcept { + return c == '|'; +} + +constexpr bool is_hyphen(char c) noexcept { + return c == '-'; +} + +constexpr bool is_letter(char c) noexcept { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); +} + +constexpr std::uint16_t to_digit(char c) noexcept { + return static_cast<std::uint16_t>(c - '0'); +} + +constexpr std::uint8_t length(std::uint16_t x) noexcept { + if (x < 10) { + return 1; + } + if (x < 100) { + return 2; + } + if (x < 1000) { + return 3; + } + if (x < 10000) { + return 4; + } + return 5; +} + +constexpr std::uint8_t length(prerelease t) noexcept { + if (t == prerelease::alpha) { + return static_cast<std::uint8_t>(alpha.length()); + } else if (t == prerelease::beta) { + return static_cast<std::uint8_t>(beta.length()); + } else if (t == prerelease::rc) { + return static_cast<std::uint8_t>(rc.length()); + } + + return 0; +} + +constexpr bool equals(const char* first, const char* last, std::string_view str) noexcept { + for (std::size_t i = 0; first != last && i < str.length(); ++i, ++first) { + if (to_lower(*first) != to_lower(str[i])) { + return false; + } + } + + return true; +} + +constexpr char* to_chars(char* str, std::uint16_t x, bool dot = true) noexcept { + do { + *(--str) = static_cast<char>('0' + (x % 10)); + x /= 10; + } while (x != 0); + + if (dot) { + *(--str) = '.'; + } + + return str; +} + +constexpr char* to_chars(char* str, prerelease t) noexcept { + const auto p = t == prerelease::alpha + ? alpha + : t == prerelease::beta + ? beta + : t == prerelease::rc + ? rc + : std::string_view{}; + + if (p.size() > 0) { + for (auto it = p.rbegin(); it != p.rend(); ++it) { + *(--str) = *it; + } + *(--str) = '-'; + } + + return str; +} + +constexpr const char* from_chars(const char* first, const char* last, std::uint16_t& d) noexcept { + if (first != last && is_digit(*first)) { + std::int32_t t = 0; + for (; first != last && is_digit(*first); ++first) { + t = t * 10 + to_digit(*first); + } + if (t <= (std::numeric_limits<std::uint16_t>::max)()) { + d = static_cast<std::uint16_t>(t); + return first; + } + } + + return nullptr; +} + +constexpr const char* from_chars(const char* first, const char* last, std::optional<std::uint16_t>& d) noexcept { + if (first != last && is_digit(*first)) { + std::int32_t t = 0; + for (; first != last && is_digit(*first); ++first) { + t = t * 10 + to_digit(*first); + } + if (t <= (std::numeric_limits<std::uint16_t>::max)()) { + d = static_cast<std::uint16_t>(t); + return first; + } + } + + return nullptr; +} + +constexpr const char* from_chars(const char* first, const char* last, prerelease& p) noexcept { + if (is_hyphen(*first)) { + ++first; + } + + if (equals(first, last, alpha)) { + p = prerelease::alpha; + return first + alpha.length(); + } else if (equals(first, last, beta)) { + p = prerelease::beta; + return first + beta.length(); + } else if (equals(first, last, rc)) { + p = prerelease::rc; + return first + rc.length(); + } + + return nullptr; +} + +constexpr bool check_delimiter(const char* first, const char* last, char d) noexcept { + return first != last && first != nullptr && *first == d; +} + +template <typename T, typename = void> +struct resize_uninitialized { + static auto resize(T& str, std::size_t size) -> std::void_t<decltype(str.resize(size))> { + str.resize(size); + } +}; + +template <typename T> +struct resize_uninitialized<T, std::void_t<decltype(std::declval<T>().__resize_default_init(42))>> { + static void resize(T& str, std::size_t size) { + str.__resize_default_init(size); + } +}; + +} // namespace semver::detail + +struct version { + std::uint16_t major = 0; + std::uint16_t minor = 1; + std::uint16_t patch = 0; + prerelease prerelease_type = prerelease::none; + std::optional<std::uint16_t> prerelease_number = std::nullopt; + + constexpr version(std::uint16_t mj, + std::uint16_t mn, + std::uint16_t pt, + prerelease prt = prerelease::none, + std::optional<std::uint16_t> prn = std::nullopt) noexcept + : major{mj}, + minor{mn}, + patch{pt}, + prerelease_type{prt}, + prerelease_number{prt == prerelease::none ? std::nullopt : prn} { + } + + constexpr version(std::uint16_t mj, + std::uint16_t mn, + std::uint16_t pt, + prerelease prt, + std::uint16_t prn) noexcept + : major{mj}, + minor{mn}, + patch{pt}, + prerelease_type{prt}, + prerelease_number{prt == prerelease::none ? std::nullopt : std::make_optional<std::uint16_t>(prn)} { + } + + explicit constexpr version(std::string_view str) : version(0, 0, 0, prerelease::none, std::nullopt) { + from_string(str); + } + + constexpr version() = default; // https://semver.org/#how-should-i-deal-with-revisions-in-the-0yz-initial-development-phase + + constexpr version(const version&) = default; + + constexpr version(version&&) = default; + + ~version() = default; + + version& operator=(const version&) = default; + + version& operator=(version&&) = default; + + [[nodiscard]] constexpr from_chars_result from_chars(const char* first, const char* last) noexcept { + if (first == nullptr || last == nullptr || (last - first) < detail::min_version_string_length) { + return {first, std::errc::invalid_argument}; + } + + auto next = first; + if (next = detail::from_chars(next, last, major); detail::check_delimiter(next, last, '.')) { + if (next = detail::from_chars(++next, last, minor); detail::check_delimiter(next, last, '.')) { + if (next = detail::from_chars(++next, last, patch); next == last) { + prerelease_type = prerelease::none; + prerelease_number = {}; + return {next, std::errc{}}; + } else if (detail::check_delimiter(next, last, '-')) { + if (next = detail::from_chars(next, last, prerelease_type); next == last) { + prerelease_number = {}; + return {next, std::errc{}}; + } else if (detail::check_delimiter(next, last, '.')) { + if (next = detail::from_chars(++next, last, prerelease_number); next == last) { + return {next, std::errc{}}; + } + } + } + } + } + + return {first, std::errc::invalid_argument}; + } + + [[nodiscard]] constexpr to_chars_result to_chars(char* first, char* last) const noexcept { + const auto length = string_length(); + if (first == nullptr || last == nullptr || (last - first) < length) { + return {last, std::errc::value_too_large}; + } + + auto next = first + length; + if (prerelease_type != prerelease::none) { + if (prerelease_number.has_value()) { + next = detail::to_chars(next, prerelease_number.value()); + } + next = detail::to_chars(next, prerelease_type); + } + next = detail::to_chars(next, patch); + next = detail::to_chars(next, minor); + next = detail::to_chars(next, major, false); + + return {first + length, std::errc{}}; + } + + [[nodiscard]] constexpr bool from_string_noexcept(std::string_view str) noexcept { + return from_chars(str.data(), str.data() + str.length()); + } + + constexpr version& from_string(std::string_view str) { + if (!from_string_noexcept(str)) { + SEMVER_THROW("semver::version::from_string invalid version."); + } + + return *this; + } + + [[nodiscard]] std::string to_string() const { + auto str = std::string{}; + detail::resize_uninitialized<std::string>::resize(str, string_length()); + if (!to_chars(str.data(), str.data() + str.length())) { + SEMVER_THROW("semver::version::to_string invalid version."); + } + + return str; + } + + [[nodiscard]] constexpr std::uint8_t string_length() const noexcept { + // (<major>) + 1(.) + (<minor>) + 1(.) + (<patch>) + auto length = detail::length(major) + detail::length(minor) + detail::length(patch) + 2; + if (prerelease_type != prerelease::none) { + // + 1(-) + (<prerelease>) + length += detail::length(prerelease_type) + 1; + if (prerelease_number.has_value()) { + // + 1(.) + (<prereleaseversion>) + length += detail::length(prerelease_number.value()) + 1; + } + } + + return static_cast<std::uint8_t>(length); + } + + [[nodiscard]] constexpr int compare(const version& other) const noexcept { + if (major != other.major) { + return major - other.major; + } + + if (minor != other.minor) { + return minor - other.minor; + } + + if (patch != other.patch) { + return patch - other.patch; + } + + if (prerelease_type != other.prerelease_type) { + return static_cast<std::uint8_t>(prerelease_type) - static_cast<std::uint8_t>(other.prerelease_type); + } + + if (prerelease_number.has_value()) { + if (other.prerelease_number.has_value()) { + return prerelease_number.value() - other.prerelease_number.value(); + } + return 1; + } else if (other.prerelease_number.has_value()) { + return -1; + } + + return 0; + } +}; + +[[nodiscard]] constexpr bool operator==(const version& lhs, const version& rhs) noexcept { + return lhs.compare(rhs) == 0; +} + +[[nodiscard]] constexpr bool operator!=(const version& lhs, const version& rhs) noexcept { + return lhs.compare(rhs) != 0; +} + +[[nodiscard]] constexpr bool operator>(const version& lhs, const version& rhs) noexcept { + return lhs.compare(rhs) > 0; +} + +[[nodiscard]] constexpr bool operator>=(const version& lhs, const version& rhs) noexcept { + return lhs.compare(rhs) >= 0; +} + +[[nodiscard]] constexpr bool operator<(const version& lhs, const version& rhs) noexcept { + return lhs.compare(rhs) < 0; +} + +[[nodiscard]] constexpr bool operator<=(const version& lhs, const version& rhs) noexcept { + return lhs.compare(rhs) <= 0; +} + +#if __cpp_impl_three_way_comparison >= 201907L +[[nodiscard]] constexpr std::strong_ordering operator<=>(const version& lhs, const version& rhs) { + int compare = lhs.compare(rhs); + if ( compare == 0 ) + return std::strong_ordering::equal; + if ( compare > 0 ) + return std::strong_ordering::greater; + return std::strong_ordering::less; +} +#endif + +[[nodiscard]] constexpr version operator""_version(const char* str, std::size_t length) { + return version{std::string_view{str, length}}; +} + +[[nodiscard]] constexpr bool valid(std::string_view str) noexcept { + return version{}.from_string_noexcept(str); +} + +[[nodiscard]] constexpr from_chars_result from_chars(const char* first, const char* last, version& v) noexcept { + return v.from_chars(first, last); +} + +[[nodiscard]] constexpr to_chars_result to_chars(char* first, char* last, const version& v) noexcept { + return v.to_chars(first, last); +} + +[[nodiscard]] constexpr std::optional<version> from_string_noexcept(std::string_view str) noexcept { + if (version v{}; v.from_string_noexcept(str)) { + return v; + } + + return std::nullopt; +} + +[[nodiscard]] constexpr version from_string(std::string_view str) { + return version{str}; +} + +[[nodiscard]] inline std::string to_string(const version& v) { + return v.to_string(); +} + +template <typename Char, typename Traits> +inline std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, const version& v) { + for (const auto c : v.to_string()) { + os.put(c); + } + + return os; +} + +inline namespace comparators { + +enum struct comparators_option : std::uint8_t { + exclude_prerelease, + include_prerelease +}; + +[[nodiscard]] constexpr int compare(const version& lhs, const version& rhs, comparators_option option = comparators_option::include_prerelease) noexcept { + if (option == comparators_option::exclude_prerelease) { + return version{lhs.major, lhs.minor, lhs.patch}.compare(version{rhs.major, rhs.minor, rhs.patch}); + } + return lhs.compare(rhs); +} + +[[nodiscard]] constexpr bool equal_to(const version& lhs, const version& rhs, comparators_option option = comparators_option::include_prerelease) noexcept { + return compare(lhs, rhs, option) == 0; +} + +[[nodiscard]] constexpr bool not_equal_to(const version& lhs, const version& rhs, comparators_option option = comparators_option::include_prerelease) noexcept { + return compare(lhs, rhs, option) != 0; +} + +[[nodiscard]] constexpr bool greater(const version& lhs, const version& rhs, comparators_option option = comparators_option::include_prerelease) noexcept { + return compare(lhs, rhs, option) > 0; +} + +[[nodiscard]] constexpr bool greater_equal(const version& lhs, const version& rhs, comparators_option option = comparators_option::include_prerelease) noexcept { + return compare(lhs, rhs, option) >= 0; +} + +[[nodiscard]] constexpr bool less(const version& lhs, const version& rhs, comparators_option option = comparators_option::include_prerelease) noexcept { + return compare(lhs, rhs, option) < 0; +} + +[[nodiscard]] constexpr bool less_equal(const version& lhs, const version& rhs, comparators_option option = comparators_option::include_prerelease) noexcept { + return compare(lhs, rhs, option) <= 0; +} + +} // namespace semver::comparators + +namespace range { + +namespace detail { + +using namespace semver::detail; + +class range { + public: + constexpr explicit range(std::string_view str) noexcept : parser{str} {} + + constexpr bool satisfies(const version& ver, bool include_prerelease) { + const bool has_prerelease = ver.prerelease_type != prerelease::none; + + do { + if (is_logical_or_token()) { + parser.advance_token(range_token_type::logical_or); + } + + bool contains = true; + bool allow_compare = include_prerelease; + + while (is_operator_token() || is_number_token()) { + const auto range = parser.parse_range(); + const bool equal_without_tags = equal_to(range.ver, ver, comparators_option::exclude_prerelease); + + if (has_prerelease && equal_without_tags) { + allow_compare = true; + } + + if (!range.satisfies(ver)) { + contains = false; + break; + } + } + + if (has_prerelease) { + if (allow_compare && contains) { + return true; + } + } else if (contains) { + return true; + } + + } while (is_logical_or_token()); + + return false; + } + +private: + enum struct range_operator : std::uint8_t { + less, + less_or_equal, + greater, + greater_or_equal, + equal + }; + + struct range_comparator { + range_operator op; + version ver; + + constexpr bool satisfies(const version& version) const { + switch (op) { + case range_operator::equal: + return version == ver; + case range_operator::greater: + return version > ver; + case range_operator::greater_or_equal: + return version >= ver; + case range_operator::less: + return version < ver; + case range_operator::less_or_equal: + return version <= ver; + default: + SEMVER_THROW("semver::range unexpected operator."); + } + } + }; + + enum struct range_token_type : std::uint8_t { + none, + number, + range_operator, + dot, + logical_or, + hyphen, + prerelease, + end_of_line + }; + + struct range_token { + range_token_type type = range_token_type::none; + std::uint16_t number = 0; + range_operator op = range_operator::equal; + prerelease prerelease_type = prerelease::none; + }; + + struct range_lexer { + std::string_view text; + std::size_t pos; + + constexpr explicit range_lexer(std::string_view text) noexcept : text{text}, pos{0} {} + + constexpr range_token get_next_token() noexcept { + while (!end_of_line()) { + + if (is_space(text[pos])) { + advance(1); + continue; + } + + if (is_logical_or(text[pos])) { + advance(2); + return {range_token_type::logical_or}; + } + + if (is_operator(text[pos])) { + const auto op = get_operator(); + return {range_token_type::range_operator, 0, op}; + } + + if (is_digit(text[pos])) { + const auto number = get_number(); + return {range_token_type::number, number}; + } + + if (is_dot(text[pos])) { + advance(1); + return {range_token_type::dot}; + } + + if (is_hyphen(text[pos])) { + advance(1); + return {range_token_type::hyphen}; + } + + if (is_letter(text[pos])) { + const auto prerelease = get_prerelease(); + return {range_token_type::prerelease, 0, range_operator::equal, prerelease}; + } + } + + return {range_token_type::end_of_line}; + } + + constexpr bool end_of_line() const noexcept { return pos >= text.length(); } + + constexpr void advance(std::size_t i) noexcept { + pos += i; + } + + constexpr range_operator get_operator() noexcept { + if (text[pos] == '<') { + advance(1); + if (text[pos] == '=') { + advance(1); + return range_operator::less_or_equal; + } + return range_operator::less; + } else if (text[pos] == '>') { + advance(1); + if (text[pos] == '=') { + advance(1); + return range_operator::greater_or_equal; + } + return range_operator::greater; + } else if (text[pos] == '=') { + advance(1); + return range_operator::equal; + } + + return range_operator::equal; + } + + constexpr std::uint16_t get_number() noexcept { + const auto first = text.data() + pos; + const auto last = text.data() + text.length(); + if (std::uint16_t n{}; from_chars(first, last, n) != nullptr) { + advance(length(n)); + return n; + } + + return 0; + } + + constexpr prerelease get_prerelease() noexcept { + const auto first = text.data() + pos; + const auto last = text.data() + text.length(); + if (first > last) { + advance(1); + return prerelease::none; + } + + if (prerelease p{}; from_chars(first, last, p) != nullptr) { + advance(length(p)); + return p; + } + + advance(1); + + return prerelease::none; + } + }; + + struct range_parser { + range_lexer lexer; + range_token current_token; + + constexpr explicit range_parser(std::string_view str) : lexer{str}, current_token{range_token_type::none} { + advance_token(range_token_type::none); + } + + constexpr void advance_token(range_token_type token_type) { + if (current_token.type != token_type) { + SEMVER_THROW("semver::range unexpected token."); + } + current_token = lexer.get_next_token(); + } + + constexpr range_comparator parse_range() { + if (current_token.type == range_token_type::number) { + const auto version = parse_version(); + return {range_operator::equal, version}; + } else if (current_token.type == range_token_type::range_operator) { + const auto range_operator = current_token.op; + advance_token(range_token_type::range_operator); + const auto version = parse_version(); + return {range_operator, version}; + } + + return {range_operator::equal, version{}}; + } + + constexpr version parse_version() { + const auto major = parse_number(); + + advance_token(range_token_type::dot); + const auto minor = parse_number(); + + advance_token(range_token_type::dot); + const auto patch = parse_number(); + + prerelease prerelease = prerelease::none; + std::optional<std::uint16_t> prerelease_number = std::nullopt; + + if (current_token.type == range_token_type::hyphen) { + advance_token(range_token_type::hyphen); + prerelease = parse_prerelease(); + if (current_token.type == range_token_type::dot) { + advance_token(range_token_type::dot); + prerelease_number = parse_number(); + } + } + + return {major, minor, patch, prerelease, prerelease_number}; + } + + constexpr std::uint16_t parse_number() { + const auto token = current_token; + advance_token(range_token_type::number); + + return token.number; + } + + constexpr prerelease parse_prerelease() { + const auto token = current_token; + advance_token(range_token_type::prerelease); + + return token.prerelease_type; + } + }; + + [[nodiscard]] constexpr bool is_logical_or_token() const noexcept { + return parser.current_token.type == range_token_type::logical_or; + } + [[nodiscard]] constexpr bool is_operator_token() const noexcept { + return parser.current_token.type == range_token_type::range_operator; + } + + [[nodiscard]] constexpr bool is_number_token() const noexcept { + return parser.current_token.type == range_token_type::number; + } + + range_parser parser; +}; + +} // namespace semver::range::detail + +enum struct satisfies_option : std::uint8_t { + exclude_prerelease, + include_prerelease +}; + +constexpr bool satisfies(const version& ver, std::string_view str, satisfies_option option = satisfies_option::exclude_prerelease) { + switch (option) { + case satisfies_option::exclude_prerelease: + return detail::range{str}.satisfies(ver, false); + case satisfies_option::include_prerelease: + return detail::range{str}.satisfies(ver, true); + default: + SEMVER_THROW("semver::range unexpected satisfies_option."); + } +} + +} // namespace semver::range + +// Version lib semver. +inline constexpr auto semver_version = version{SEMVER_VERSION_MAJOR, SEMVER_VERSION_MINOR, SEMVER_VERSION_PATCH}; + +} // namespace semver + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +#endif // NEARGYE_SEMANTIC_VERSIONING_HPP
--- a/include/core/version.h Mon Jan 08 13:21:08 2024 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -#ifndef __core__version_h -#define __core__version_h - -constexpr std::string_view MINORI_VERSION = "v0.1-alpha"; - -#endif // __core__version_h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/library/library.h Mon Jan 08 13:22:09 2024 -0500 @@ -0,0 +1,14 @@ +#ifndef __library__library_h +#define __library__library_h + +#include "library/library.h" + +#include <unordered_map> +#include <string> + +// int = anime id, map = episode, paths +extern std::unordered_map<int, std::unordered_map<int, std::string>> library; + +void SearchLibraryFolders(); + +#endif // __library__library_h \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/library/library.cc Mon Jan 08 13:22:09 2024 -0500 @@ -0,0 +1,43 @@ +#include "library/library.h" +#include "core/anime_db.h" +#include "core/strings.h" +#include "core/session.h" + +#include "anitomy/anitomy.h" + +#include <filesystem> +#include <unordered_map> +#include <string> + +// int = anime id, map = episode, paths +std::unordered_map<int, std::unordered_map<int, std::string>> library; + +void SearchLibraryFolders() { + library.clear(); + + for (const auto& spath : session.config.library.paths) { + const std::filesystem::path path(spath); + for (const auto& entry : std::filesystem::recursive_directory_iterator(path)) { + if (!std::filesystem::is_regular_file(entry.path())) + continue; + + const std::string basename = path.filename(); + + anitomy::Anitomy anitomy; + anitomy.Parse(Strings::ToWstring(basename)); + + const auto& elements = anitomy.elements(); + + const std::string title = Strings::ToUtf8String(elements.get(anitomy::kElementAnimeTitle)); + + const int id = Anime::db.GetAnimeFromTitle(title); + if (id <= 0) + continue; + + const int episode = Strings::ToInt(Strings::ToUtf8String(elements.get(anitomy::kElementAnimeTitle))); + + // we have an ID now! + library[id][episode] = entry.path(); + } + } +}