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();
+		}
+	}
+}