Home · Blog · Projects


My C++ Lib

PS is completly in progress currently, so plese dont forget that and feel free to propose changes here!

Source Code
cppSize: 36.85 KB
#pragma once

// INFO if you find bugs in this libary, please send them
// to shadowdara74@gmail.com

// This Shadowdara Personal Library is a collection of utility
// functions and classes for various purposes, including
// configuration management, file loading, rendering, and string
// manipulation. It is designed to be used in conjunction with SDL2,
// OpenGL, and ImGui for creating graphical applications.

// for C++ 20

// LICENSE MIT for the libary
// and when using the libary in project which will be published,
// it would be cool if you could send an email to email down below,
// i want to see what you have created with it!
//
// shadowdara74@gmail.com
//

// CREDIT WHEN USING THIS LIB (COPY THE LINE DOWN BELOW)

/*
used shadowdara's C++ Lib by Shadowdara which is licensed under MIT
LICENSE
https://shadowdara.github.io/blog/my-cpp-lib-for-stuff/
*/

// PS SOME CREDITS WHICH I USED TO CREATE THE LIBARY
//
// https://stackoverflow.com/questions/13172158/c-split-string-by-line

#pragma region includes

// CPP Libs
#include <algorithm>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <numeric>
#include <sstream>
#include <stdexcept>
#include <string>
#include <system_error>
#include <unordered_map>
#include <vector>

// C Libs
#include <cctype>
#include <cstdint>

#pragma endregion

#pragma region Macros

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

#pragma endregion

#pragma region colors

// Header File with all ANSI Colors as Macros
// by Shadowdara
//
// licensed under Appache 2.0
//
// by
// https://github.com/ShadowDara/samengine
//
//
// More Infos
// https://shadowdara.github.io/docs#/cpplibs/ansicolors

#if USE_COLORS_AS_MACRO == 0
    #if RM_ANSI_FROM_COLORNAME == 0

    // Use as Raylib Macros

        #define ANSI_END "\x1b[0m"
        #define ANSI_BOLD "\x1b[1m"

        #define ANSI_ITALIC "\x1b[3m"
        #define ANSI_UNDERLINED "\x1b[4m"

        #define ANSI_REVERSE_TEXT "\x1b[7m"

        #define ANSI_NOT_UNDERLINED "\x1b[24m"

        #define ANSI_POSITIVE_TEXT "\x1b[27m"

        #define ANSI_BLACK "\x1b[30m"
        #define ANSI_RED "\x1b[31m"
        #define ANSI_GREEN "\x1b[32m"
        #define ANSI_YELLOW "\x1b[33m"
        #define ANSI_BLUE "\x1b[34m"
        #define ANSI_PURPLE "\x1b[35m"
        #define ANSI_CYAN "\x1b[36m"
        #define ANSI_WHITE "\x1b[37m"

        #define ANSI_BG_BLACK "\x1b[40m"
        #define ANSI_BG_RED "\x1b[41m"
        #define ANSI_BG_GREEN "\x1b[42m"
        #define ANSI_BG_YELLOW "\x1b[43m"
        #define ANSI_BG_BLUE "\x1b[44m"
        #define ANSI_BG_PURPLE "\x1b[45m"
        #define ANSI_BG_CYAN "\x1b[46m"
        #define ANSI_BG_WHITE "\x1b[47m"

        #define ANSI_BRIGHT_BLACK "\x1b[90m"
        #define ANSI_BRIGHT_RED "\x1b[91m"
        #define ANSI_BRIGHT_GREEM "\x1b[92m"
        #define ANSI_BRIGHT_YELLOW "\x1b[93m"
        #define ANSI_BRIGHT_BLUE "\x1b[94m"
        #define ANSI_BRIGHT_PURPLE "\x1b[95m"
        #define ANSI_BRIGHT_CYAN "\x1b[96m"
        #define ANSI_BRIGHT_WHITE "\x1b[97m"

        #define ANSI_BG_BRIGHT_BLACK "\x1b[100m"
        #define ANSI_BG_BRIGHT_RED "\x1b[101m"
        #define ANSI_BG_BRIGHT_GREEM "\x1b[102m"
        #define ANSI_BG_BRIGHT_YELLOW "\x1b[103m"
        #define ANSI_BG_BRIGHT_BLUE "\x1b[104m"
        #define ANSI_BG_BRIGHT_PURPLE "\x1b[105m"
        #define ANSI_BG_BRIGHT_CYAN "\x1b[106m"
        #define ANSI_BG_BRIGHT_WHITE "\x1b[107m"

    #else

    // Use as Normal Macros

        #define END "\x1b[0m"
        #define BOLD "\x1b[1m"

        #define ITALIC "\x1b[3m"
        #define UNDERLINED "\x1b[4m"

        #define REVERSE_TEXT "\x1b[7m"

        #define NOT_UNDERLINED "\x1b[24m"

        #define POSITIVE_TEXT "\x1b[27m"

        #define BLACK "\x1b[30m"
        #define RED "\x1b[31m"
        #define GREEN "\x1b[32m"
        #define YELLOW "\x1b[33m"
        #define BLUE "\x1b[34m"
        #define PURPLE "\x1b[35m"
        #define CYAN "\x1b[36m"
        #define WHITE "\x1b[37m"

        #define BG_BLACK "\x1b[40m"
        #define BG_RED "\x1b[41m"
        #define BG_GREEN "\x1b[42m"
        #define BG_YELLOW "\x1b[43m"
        #define BG_BLUE "\x1b[44m"
        #define BG_PURPLE "\x1b[45m"
        #define BG_CYAN "\x1b[46m"
        #define BG_WHITE "\x1b[47m"

        #define BRIGHT_BLACK "\x1b[90m"
        #define BRIGHT_RED "\x1b[91m"
        #define BRIGHT_GREEM "\x1b[92m"
        #define BRIGHT_YELLOW "\x1b[93m"
        #define BRIGHT_BLUE "\x1b[94m"
        #define BRIGHT_PURPLE "\x1b[95m"
        #define BRIGHT_CYAN "\x1b[96m"
        #define BRIGHT_WHITE "\x1b[97m"

        #define BG_BRIGHT_BLACK "\x1b[100m"
        #define BG_BRIGHT_RED "\x1b[101m"
        #define BG_BRIGHT_GREEM "\x1b[102m"
        #define BG_BRIGHT_YELLOW "\x1b[103m"
        #define BG_BRIGHT_BLUE "\x1b[104m"
        #define BG_BRIGHT_PURPLE "\x1b[105m"
        #define BG_BRIGHT_CYAN "\x1b[106m"
        #define BG_BRIGHT_WHITE "\x1b[107m"

    #endif

// Use as const

#else

// Use as Const

    #if RM_ANSI_FROM_COLORNAME == 1

// Use as Raylib Name Const

inline constexpr const char *ANSI_END = "\x1b[0m";
inline constexpr const char *ANSI_BOLD = "\x1b[1m";

inline constexpr const char *ANSI_ITALIC = "\x1b[3m";
inline constexpr const char *ANSI_UNDERLINED = "\x1b[4m";

inline constexpr const char *ANSI_REVERSE_TEXT = "\x1b[7m";

inline constexpr const char *ANSI_NOT_UNDERLINED = "\x1b[24m";

inline constexpr const char *ANSI_POSITIVE_TEXT = "\x1b[27m";

inline constexpr const char *ANSI_BLACK = "\x1b[30m";
inline constexpr const char *ANSI_RED = "\x1b[31m";
inline constexpr const char *ANSI_GREEN = "\x1b[32m";
inline constexpr const char *ANSI_YELLOW = "\x1b[33m";
inline constexpr const char *ANSI_BLUE = "\x1b[34m";
inline constexpr const char *ANSI_PURPLE = "\x1b[35m";
inline constexpr const char *ANSI_CYAN = "\x1b[36m";
inline constexpr const char *ANSI_WHITE = "\x1b[37m";

inline constexpr const char *ANSI_BG_BLACK = "\x1b[40m";
inline constexpr const char *ANSI_BG_RED = "\x1b[41m";
inline constexpr const char *ANSI_BG_GREEN = "\x1b[42m";
inline constexpr const char *ANSI_BG_YELLOW = "\x1b[43m";
inline constexpr const char *ANSI_BG_BLUE = "\x1b[44m";
inline constexpr const char *ANSI_BG_PURPLE = "\x1b[45m";
inline constexpr const char *ANSI_BG_CYAN = "\x1b[46m";
inline constexpr const char *ANSI_BG_WHITE = "\x1b[47m";

inline constexpr const char *ANSI_BRIGHT_BLACK = "\x1b[90m";
inline constexpr const char *ANSI_BRIGHT_RED = "\x1b[91m";
inline constexpr const char *ANSI_BRIGHT_GREEN = "\x1b[92m";
inline constexpr const char *ANSI_BRIGHT_YELLOW = "\x1b[93m";
inline constexpr const char *ANSI_BRIGHT_BLUE = "\x1b[94m";
inline constexpr const char *ANSI_BRIGHT_PURPLE = "\x1b[95m";
inline constexpr const char *ANSI_BRIGHT_CYAN = "\x1b[96m";
inline constexpr const char *ANSI_BRIGHT_WHITE = "\x1b[97m";

inline constexpr const char *ANSI_BG_BRIGHT_BLACK = "\x1b[100m";
inline constexpr const char *ANSI_BG_BRIGHT_RED = "\x1b[101m";
inline constexpr const char *ANSI_BG_BRIGHT_GREEN = "\x1b[102m";
inline constexpr const char *ANSI_BG_BRIGHT_YELLOW = "\x1b[103m";
inline constexpr const char *ANSI_BG_BRIGHT_BLUE = "\x1b[104m";
inline constexpr const char *ANSI_BG_BRIGHT_PURPLE = "\x1b[105m";
inline constexpr const char *ANSI_BG_BRIGHT_CYAN = "\x1b[106m";
inline constexpr const char *ANSI_BG_BRIGHT_WHITE = "\x1b[107m";

    #else

// Use as Normal name Const

inline constexpr const char *END = "\x1b[0m";
inline constexpr const char *OLD = "\x1b[1m";

inline constexpr const char *ITALIC = "\x1b[3m";
inline constexpr const char *UNDERLINED = "\x1b[4m";

inline constexpr const char *REVERSE_TEXT = "\x1b[7m";

inline constexpr const char *NOT_UNDERLINED = "\x1b[24m";

inline constexpr const char *POSITIVE_TEXT = "\x1b[27m";

inline constexpr const char *BLACK = "\x1b[30m";
inline constexpr const char *RED = "\x1b[31m";
inline constexpr const char *GREEN = "\x1b[32m";
inline constexpr const char *YELLOW = "\x1b[33m";
inline constexpr const char *BLUE = "\x1b[34m";
inline constexpr const char *PURPLE = "\x1b[35m";
inline constexpr const char *CYAN = "\x1b[36m";
inline constexpr const char *WHITE = "\x1b[37m";

inline constexpr const char *BG_BLACK = "\x1b[40m";
inline constexpr const char *BG_RED = "\x1b[41m";
inline constexpr const char *BG_GREEN = "\x1b[42m";
inline constexpr const char *BG_YELLOW = "\x1b[43m";
inline constexpr const char *BG_BLUE = "\x1b[44m";
inline constexpr const char *BG_PURPLE = "\x1b[45m";
inline constexpr const char *BG_CYAN = "\x1b[46m";
inline constexpr const char *BG_WHITE = "\x1b[47m";

inline constexpr const char *BRIGHT_BLACK = "\x1b[90m";
inline constexpr const char *BRIGHT_RED = "\x1b[91m";
inline constexpr const char *BRIGHT_GREEN = "\x1b[92m";
inline constexpr const char *BRIGHT_YELLOW = "\x1b[93m";
inline constexpr const char *BRIGHT_BLUE = "\x1b[94m";
inline constexpr const char *BRIGHT_PURPLE = "\x1b[95m";
inline constexpr const char *BRIGHT_CYAN = "\x1b[96m";
inline constexpr const char *BRIGHT_WHITE = "\x1b[97m";

inline constexpr const char *BG_BRIGHT_BLACK = "\x1b[100m";
inline constexpr const char *BG_BRIGHT_RED = "\x1b[101m";
inline constexpr const char *BG_BRIGHT_GREEN = "\x1b[102m";
inline constexpr const char *BG_BRIGHT_YELLOW = "\x1b[103m";
inline constexpr const char *BG_BRIGHT_BLUE = "\x1b[104m";
inline constexpr const char *BG_BRIGHT_PURPLE = "\x1b[105m";
inline constexpr const char *BG_BRIGHT_CYAN = "\x1b[106m";
inline constexpr const char *BG_BRIGHT_WHITE = "\x1b[107m";

    #endif

#endif

#pragma endregion

#pragma region sorting

// bublsort algorithm, which is a simple sorting algorithm that
// repeatedly steps through the list, compares adjacent elements
// and swaps them if they are in the wrong order. The pass through
// the list is repeated until no swaps are needed, which means the
// list is sorted.

/*
// Example usage:
std::vector<int> values = { 5, 2, 8, 1, 3 };

bubblesort(values, [](int a, int b)
    {
        return a < b;
    });
*/
template <typename T, typename Compare>
inline void bubblesort(std::vector<T> &values, Compare comp)
{
    bool swapped;

    do
    {
        swapped = false;

        for (size_t i = 0; i + 1 < values.size(); ++i)
        {
            if (comp(values[i + 1], values[i]))
            {
                std::swap(values[i], values[i + 1]);
                swapped = true;
            }
        }
    } while (swapped);
}

#pragma endregion

#pragma region strings

// string_utils.hpp
// Header-only C++ Bibliothek mit String-Manipulationsfunktionen,
// wie man sie aus Python, JavaScript, Java etc. kennt.
//
// Einfach includieren:
//   #include "string_utils.hpp"
//   using namespace strutil;
//
// Alle Funktionen sind als `inline` markiert -> keine ODR-Probleme
// beim Einbinden in mehrere ?bersetzungseinheiten.

namespace strutil
{

    // ---------------------------------------------------------------------
    // Trimming / Whitespace  (Python: strip, lstrip, rstrip)
    // ---------------------------------------------------------------------

    inline std::string lstrip(const std::string &s)
    {
        size_t start = 0;
        while (start < s.size() &&
               std::isspace(static_cast<unsigned char>(s[start])))
            ++start;
        return s.substr(start);
    }

    inline std::string rstrip(const std::string &s)
    {
        size_t end = s.size();
        while (end > 0 && std::isspace(static_cast<unsigned char>(s[end - 1])))
            --end;
        return s.substr(0, end);
    }

    inline std::string strip(const std::string &s) { return lstrip(rstrip(s)); }

    // Variante mit eigenem Zeichensatz, der entfernt werden soll (wie Python's
    // strip(chars))
    inline std::string strip(const std::string &s, const std::string &chars)
    {
        size_t start = s.find_first_not_of(chars);
        if (start == std::string::npos)
            return "";
        size_t end = s.find_last_not_of(chars);
        return s.substr(start, end - start + 1);
    }

    // ---------------------------------------------------------------------
    // Gro?-/Kleinschreibung (Python: upper, lower, title, capitalize, swapcase)
    // ---------------------------------------------------------------------

    inline std::string to_upper(std::string s)
    {
        std::transform(s.begin(), s.end(), s.begin(),
                       [](unsigned char c) { return std::toupper(c); });
        return s;
    }

    inline std::string to_lower(std::string s)
    {
        std::transform(s.begin(), s.end(), s.begin(),
                       [](unsigned char c) { return std::tolower(c); });
        return s;
    }

    inline std::string capitalize(std::string s)
    {
        if (!s.empty())
        {
            s[0] = static_cast<char>(
                std::toupper(static_cast<unsigned char>(s[0])));
            for (size_t i = 1; i < s.size(); ++i)
                s[i] = static_cast<char>(
                    std::tolower(static_cast<unsigned char>(s[i])));
        }
        return s;
    }

    inline std::string title(const std::string &s)
    {
        std::string result = s;
        bool new_word = true;
        for (auto &c : result)
        {
            if (std::isalpha(static_cast<unsigned char>(c)))
            {
                c = new_word ? std::toupper(static_cast<unsigned char>(c))
                             : std::tolower(static_cast<unsigned char>(c));
                new_word = false;
            }
            else
            {
                new_word = true;
            }
        }
        return result;
    }

    inline std::string swapcase(std::string s)
    {
        for (auto &c : s)
        {
            if (std::isupper(static_cast<unsigned char>(c)))
                c = static_cast<char>(
                    std::tolower(static_cast<unsigned char>(c)));
            else if (std::islower(static_cast<unsigned char>(c)))
                c = static_cast<char>(
                    std::toupper(static_cast<unsigned char>(c)));
        }
        return s;
    }

    // ---------------------------------------------------------------------
    // Pr?fen / Suchen (Python: startswith, endswith, contains/in, count, find)
    // ---------------------------------------------------------------------

    inline bool starts_with(const std::string &s, const std::string &prefix)
    {
        return s.size() >= prefix.size() &&
               s.compare(0, prefix.size(), prefix) == 0;
    }

    inline bool ends_with(const std::string &s, const std::string &suffix)
    {
        return s.size() >= suffix.size() &&
               s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0;
    }

    inline bool contains(const std::string &s, const std::string &needle)
    {
        return s.find(needle) != std::string::npos;
    }

    inline size_t count(const std::string &s, const std::string &sub)
    {
        if (sub.empty())
            return 0;
        size_t cnt = 0, pos = 0;
        while ((pos = s.find(sub, pos)) != std::string::npos)
        {
            ++cnt;
            pos += sub.size();
        }
        return cnt;
    }

    // ---------------------------------------------------------------------
    // is*-Pr?fungen (Python: isdigit, isalpha, isalnum, isspace, isupper,
    // islower)
    // ---------------------------------------------------------------------

    inline bool is_digit(const std::string &s)
    {
        if (s.empty())
            return false;
        return std::all_of(s.begin(), s.end(),
                           [](unsigned char c) { return std::isdigit(c); });
    }

    inline bool is_alpha(const std::string &s)
    {
        if (s.empty())
            return false;
        return std::all_of(s.begin(), s.end(),
                           [](unsigned char c) { return std::isalpha(c); });
    }

    inline bool is_alnum(const std::string &s)
    {
        if (s.empty())
            return false;
        return std::all_of(s.begin(), s.end(),
                           [](unsigned char c) { return std::isalnum(c); });
    }

    inline bool is_space(const std::string &s)
    {
        if (s.empty())
            return false;
        return std::all_of(s.begin(), s.end(),
                           [](unsigned char c) { return std::isspace(c); });
    }

    inline bool is_upper(const std::string &s)
    {
        bool has_alpha = false;
        for (unsigned char c : s)
        {
            if (std::isalpha(c))
            {
                has_alpha = true;
                if (!std::isupper(c))
                    return false;
            }
        }
        return has_alpha;
    }

    inline bool is_lower(const std::string &s)
    {
        bool has_alpha = false;
        for (unsigned char c : s)
        {
            if (std::isalpha(c))
            {
                has_alpha = true;
                if (!std::islower(c))
                    return false;
            }
        }
        return has_alpha;
    }

    // ---------------------------------------------------------------------
    // Split / Join (Python: split, rsplit, splitlines, join)
    // ---------------------------------------------------------------------

    // Split an jedem beliebigen Zeichen aus `delims` (Standard: Whitespace),
    // analog zu Python's str.split() ohne Argument (mehrfache Trenner werden
    // zusammengefasst)
    inline std::vector<std::string>
    split(const std::string &s, const std::string &delims = " \t\n\r\f\v")
    {
        std::vector<std::string> tokens;
        size_t start = s.find_first_not_of(delims);
        while (start != std::string::npos)
        {
            size_t end = s.find_first_of(delims, start);
            tokens.push_back(s.substr(start, end - start));
            start = (end == std::string::npos)
                        ? std::string::npos
                        : s.find_first_not_of(delims, end);
        }
        return tokens;
    }

    // Split an einem festen String-Trenner (wie Python's str.split(sep))
    inline std::vector<std::string> split_by(const std::string &s,
                                             const std::string &sep)
    {
        if (sep.empty())
            throw std::invalid_argument(
                "split_by: separator darf nicht leer sein");
        std::vector<std::string> tokens;
        size_t start = 0, pos;
        while ((pos = s.find(sep, start)) != std::string::npos)
        {
            tokens.push_back(s.substr(start, pos - start));
            start = pos + sep.size();
        }
        tokens.push_back(s.substr(start));
        return tokens;
    }

    inline std::vector<std::string> splitlines(const std::string &s)
    {
        std::vector<std::string> lines;
        std::istringstream iss(s);
        std::string line;
        while (std::getline(iss, line))
        {
            if (!line.empty() && line.back() == '\r')
                line.pop_back();
            lines.push_back(line);
        }
        return lines;
    }

    inline std::string join(const std::vector<std::string> &parts,
                            const std::string &sep)
    {
        std::string result;
        for (size_t i = 0; i < parts.size(); ++i)
        {
            if (i)
                result += sep;
            result += parts[i];
        }
        return result;
    }

    // ---------------------------------------------------------------------
    // Ersetzen (Python: replace)
    // ---------------------------------------------------------------------

    inline std::string replace(const std::string &s, const std::string &from,
                               const std::string &to, int max_count = -1)
    {
        if (from.empty())
            return s;
        std::string result;
        size_t start = 0, pos;
        int replaced = 0;
        while ((pos = s.find(from, start)) != std::string::npos)
        {
            if (max_count >= 0 && replaced >= max_count)
                break;
            result.append(s, start, pos - start);
            result += to;
            start = pos + from.size();
            ++replaced;
        }
        result.append(s, start, std::string::npos);
        return result;
    }

    // ---------------------------------------------------------------------
    // Padding / Ausrichtung (Python: ljust, rjust, center, zfill)
    // ---------------------------------------------------------------------

    inline std::string ljust(const std::string &s, size_t width,
                             char fill = ' ')
    {
        if (s.size() >= width)
            return s;
        return s + std::string(width - s.size(), fill);
    }

    inline std::string rjust(const std::string &s, size_t width,
                             char fill = ' ')
    {
        if (s.size() >= width)
            return s;
        return std::string(width - s.size(), fill) + s;
    }

    inline std::string center(const std::string &s, size_t width,
                              char fill = ' ')
    {
        if (s.size() >= width)
            return s;
        size_t total_pad = width - s.size();
        size_t left = total_pad / 2;
        size_t right = total_pad - left;
        return std::string(left, fill) + s + std::string(right, fill);
    }

    inline std::string zfill(const std::string &s, size_t width)
    {
        if (s.size() >= width)
            return s;
        bool has_sign = !s.empty() && (s[0] == '+' || s[0] == '-');
        std::string sign = has_sign ? std::string(1, s[0]) : "";
        std::string rest = has_sign ? s.substr(1) : s;
        size_t pad = width - s.size();
        return sign + std::string(pad, '0') + rest;
    }

    // ---------------------------------------------------------------------
    // Sonstiges (Python: str * n, reversed(str), removeprefix, removesuffix)
    // ---------------------------------------------------------------------

    inline std::string repeat(const std::string &s, size_t n)
    {
        std::string result;
        result.reserve(s.size() * n);
        for (size_t i = 0; i < n; ++i)
            result += s;
        return result;
    }

    inline std::string reverse(std::string s)
    {
        std::reverse(s.begin(), s.end());
        return s;
    }

    // Python 3.9+: removeprefix / removesuffix
    inline std::string remove_prefix(const std::string &s,
                                     const std::string &prefix)
    {
        return starts_with(s, prefix) ? s.substr(prefix.size()) : s;
    }

    inline std::string remove_suffix(const std::string &s,
                                     const std::string &suffix)
    {
        return ends_with(s, suffix) ? s.substr(0, s.size() - suffix.size()) : s;
    }

    // Wie Java/JS Array.prototype.slice bzw. Python's s[start:end]
    inline std::string slice(const std::string &s, int start, int end)
    {
        int len = static_cast<int>(s.size());
        if (start < 0)
            start += len;
        if (end < 0)
            end += len;
        start = std::max(0, std::min(start, static_cast<int>(len)));
        end = std::max(0, std::min(end, static_cast<int>(len)));
        if (start >= end)
            return "";
        return s.substr(start, end - start);
    }

} // namespace strutil

#pragma endregion

#pragma region Version

struct Version
{
    int major = 0;
    int minor = 0;
    int patch = 0;

    // to compare the versions
    auto operator<=>(const Version &) const = default;
};

inline Version parseVersion(const std::string &versionStr)
{
    Version version;
    std::stringstream ss(versionStr);
    std::string part;
    std::vector<int> numbers;

    while (std::getline(ss, part, '.'))
    {
        numbers.push_back(std::stoi(part));
    }

    if (numbers.size() != 3)
        throw std::invalid_argument(
            "Invalid version format. Expected MAJOR.MINOR.PATCH");

    version.major = numbers[0];
    version.minor = numbers[1];
    version.patch = numbers[2];

    return version;
}

#pragma endregion

#pragma region Nana UI

// Enable with the Macros
// #define SHADOWDARA_LIB_NANA_UI

#ifdef SHADOWDARA_LIB_NANA_UI

    #include <nana/gui.hpp>
    #include <nana/gui/widgets/button.hpp>
    #include <nana/gui/widgets/checkbox.hpp>
    #include <nana/gui/widgets/combox.hpp>
    #include <nana/gui/widgets/label.hpp>
    #include <nana/gui/widgets/panel.hpp>
    #include <nana/gui/widgets/progress.hpp>
    #include <nana/gui/widgets/slider.hpp>
    #include <nana/gui/widgets/textbox.hpp>

namespace shadowdara::nana_ui
{

    inline void create_nana_button(nana::button &button,
                                   const std::string &name, int desX, int desY,
                                   unsigned sizeX, unsigned sizeY)
    {
        button.caption(name);
        button.move({desX, desY});
        button.size({sizeX, sizeY});
    }

    #define NANA_ON_CLICK(button, code) (button).events().click([&]() code);

    // Easy Size, MOve
    #define NANA_SIMO(object, movX, movY, sizeX, sizeY)                        \
        object.move({movX, movY});                                             \
        object.size({sizeX, sizeY});                                           \
        ;

} // namespace shadowdara::nana_ui

#endif

#pragma endregion

#pragma region Process

#ifdef _WIN32
    #define NOMINMAX
    #include <windows.h>
#else
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <unistd.h>
#endif

inline int runProcess(const std::string &executable,
                      const std::vector<std::string> &args)
{
#ifdef _WIN32

    // ------------------------------------------------------------
    // Windows - Unicode / UTF-16
    // ------------------------------------------------------------

    // UTF-8 -> UTF-16
    auto utf8ToWide = [](const std::string &str) -> std::wstring
    {
        if (str.empty())
            return {};

        int size = MultiByteToWideChar(
            CP_UTF8, 0, str.data(), static_cast<int>(str.size()), nullptr, 0);

        if (size <= 0)
            return {};

        std::wstring result(size, L'\0');

        MultiByteToWideChar(CP_UTF8, 0, str.data(),
                            static_cast<int>(str.size()), result.data(), size);

        return result;
    };

    std::wstring wideExecutable = utf8ToWide(executable);

    std::wstring commandLine = L"\"" + wideExecutable + L"\"";

    for (const auto &arg : args)
    {
        std::wstring wideArg = utf8ToWide(arg);

        commandLine += L" \"";

        // Minimal escaping für Windows command line arguments.
        for (wchar_t c : wideArg)
        {
            if (c == L'"')
                commandLine += L"\\\"";
            else
                commandLine += c;
        }

        commandLine += L"\"";
    }

    STARTUPINFOW startupInfo{};
    startupInfo.cb = sizeof(startupInfo);

    PROCESS_INFORMATION processInfo{};

    std::vector<wchar_t> commandLineBuffer(commandLine.begin(),
                                           commandLine.end());

    commandLineBuffer.push_back(L'\0');

    BOOL success =
        CreateProcessW(nullptr, commandLineBuffer.data(), nullptr, nullptr,
                       FALSE, 0, nullptr, nullptr, &startupInfo, &processInfo);

    if (!success)
        return -1;

    WaitForSingleObject(processInfo.hProcess, INFINITE);

    DWORD exitCode = 0;

    GetExitCodeProcess(processInfo.hProcess, &exitCode);

    CloseHandle(processInfo.hThread);
    CloseHandle(processInfo.hProcess);

    return static_cast<int>(exitCode);

#else

    // ------------------------------------------------------------
    // Linux / macOS
    // ------------------------------------------------------------

    pid_t pid = fork();

    if (pid < 0)
        return -1;

    if (pid == 0)
    {
        std::vector<char *> argv;

        argv.push_back(const_cast<char *>(executable.c_str()));

        for (const auto &arg : args)
        {
            argv.push_back(const_cast<char *>(arg.c_str()));
        }

        argv.push_back(nullptr);

        execvp(executable.c_str(), argv.data());

        _exit(127);
    }

    int status = 0;

    if (waitpid(pid, &status, 0) < 0)
        return -1;

    if (WIFEXITED(status))
        return WEXITSTATUS(status);

    if (WIFSIGNALED(status))
        return 128 + WTERMSIG(status);

    return -1;

#endif
}

// WIndows only function
inline void enable_utf8()
{
#if _WIN32

    // that ansi codes work

    SetConsoleOutputCP(CP_UTF8);

    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);

    DWORD mode = 0;
    if (GetConsoleMode(hOut, &mode))
    {
        SetConsoleMode(hOut, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
    }

#endif
}

// open a file
inline void openFile(const std::string &path)
{
#ifdef _WIN32

    ShellExecuteA(NULL, "open", path.c_str(), NULL, NULL, SW_SHOW);

#elif __APPLE__

    std::string command = "open \"" + path + "\"";
    system(command.c_str());

#elif __linux__

    std::string command = "xdg-open \"" + path + "\"";
    system(command.c_str());

#endif
}

inline std::string loadFile(const std::string &pfad)
{
    std::ifstream datei(pfad);

    if (!datei)
        return "";

    std::stringstream buffer;
    buffer << datei.rdbuf();

    return buffer.str();
}

#pragma endregion

#pragma region ArgParser

class ArgParser
{
  private:
    std::unordered_map<std::string, std::string> options;
    std::vector<std::string> args;

  public:
    inline ArgParser(int argc, char *argv[])
    {
        for (int i = 1; i < argc; i++)
        {
            std::string arg = argv[i];

            // Option mit Wert: --name Bob
            if (arg.rfind("--", 0) == 0 || arg.rfind("-", 0) == 0)
            {
                if (i + 1 < argc && argv[i + 1][0] != '-')
                {
                    options[arg] = argv[++i];
                }
                else
                {
                    // Flag ohne Wert
                    options[arg] = "true";
                }
            }
            else
            {
                args.push_back(arg);
            }
        }
    }

    inline bool has(const std::string &option)
    {
        return options.find(option) != options.end();
    }

    inline std::string get(const std::string &option,
                           const std::string &defaultValue = "")
    {
        if (has(option))
            return options[option];

        return defaultValue;
    }

    inline bool getBool(const std::string &option)
    {
        return get(option) == "true";
    }

    inline std::vector<std::string> positional() { return args; }
};

#pragma endregion

#pragma region splitter

// Get the section of a file with a start and an end point
// the function returns an empty string when the endpoint
// is missing
inline std::string getSection(std::string content, std::string start,
                              std::string end)
{
    std::stringstream ss(content);
    std::string to;

    std::string result;

    bool collect = false;

    while (std::getline(ss, to, '\n'))
    {
        // Collect the string
        if (collect)
        {
            result += to;
        }

        if (to == start)
        {
            collect = true;
        }

        if (to == end)
        {
            return result;
        }
    }

    return "";
}

#pragma endregion

#pragma region base64

inline static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                                               "abcdefghijklmnopqrstuvwxyz"
                                               "0123456789+/";

inline std::string base64_encode(const std::string &input)
{
    std::string output;
    int val = 0;
    int bits = -6;

    for (unsigned char c : input)
    {
        val = (val << 8) + c;
        bits += 8;

        while (bits >= 0)
        {
            output += base64_chars[(val >> bits) & 0x3F];
            bits -= 6;
        }
    }

    if (bits > -6)
        output += base64_chars[((val << 8) >> (bits + 8)) & 0x3F];

    while (output.size() % 4)
        output += '=';

    return output;
}

inline std::string base64_decode(const std::string &input)
{
    std::vector<int> lookup(256, -1);

    for (int i = 0; i < 64; ++i)
        lookup[static_cast<unsigned char>(base64_chars[i])] = i;

    std::string output;
    int val = 0;
    int bits = -8;

    for (unsigned char c : input)
    {
        if (lookup[c] == -1)
            break;

        val = (val << 6) + lookup[c];
        bits += 6;

        if (bits >= 0)
        {
            output += static_cast<char>((val >> bits) & 0xFF);
            bits -= 8;
        }
    }

    return output;
}

#pragma endregion

#pragma region Char Count

inline std::unordered_map<char, int> counter(std::string input)
{
    std::unordered_map<char, int> map;

    for (char c : input)
    {
        map[c] += 1;
    }

    return map;
}

inline void printresult(std::unordered_map<char, int> map)
{
    for (const auto &pair : map)
    {
        std::cout << pair.first << " -> " << pair.second << "\n";
    }
}

#pragma endregion

#pragma region Bitpacking

#define BIT_SET(v, b) ((v) |= (uint8_t(1u) << (b)))
#define BIT_CLEAR(v, b) ((v) &= ~(uint8_t(1u) << (b)))
#define BIT_READ(v, b) (((v) >> (b)) & 1u)
#define BIT_SWAP(v, b) ((v) ^= (uint8_t(1u) << (b)))

#pragma endregion

#pragma region Terminal Markdown

// Terminal Markdown is Libary to make a syntax style similiar to markdown
// but with the ansi colors codes too.
//
// for example the text will be printed in the terminal default color,
// except red, which will be printed in red!

/*
hello, this text is [red]red[/red]
*/

inline std::string parseTerminalMarkdown(std::string input) {}

inline std::string parseTerminalMarkdowntoMarkdown() {}

#pragma endregion

struct LicenseEntry
{
    std::string content;
};

inline std::unordered_map<std::string, std::string> parseHead() {}

#define LICENCE_ENTRY_SEPERATOR "#------#"

inline std::unordered_map<std::string, std::string>
parseLicenseFile(std::string content)
{
    std::unordered_map<std::string, std::string> sections = {};

    // Parse the file
    std::stringstream ss(content);
    std::string to;
    std::string nextline;
    std::string result;

    bool collect;
    bool next;

    while (std::getline(ss, to, '\n'))
    {
        if (next)
        {
            nextline = to;
            next = false;
            continue;
        }

        // Collect the string
        if (collect)
        {
            result += to;
        }

        if (to == LICENCE_ENTRY_SEPERATOR)
        {
            if (to != "" && nextline != "")
            {
                sections[nextline] = to;
                nextline = "";
                to = "";
            }

            next = true;

            collect = !collect;
        }
    }
}

inline std::string getHomeDir()
{
    // -------------------------
    // 1. HOME-Verzeichnis finden
    // -------------------------
    std::string home;

#if defined(_WIN32)
    const char *env = std::getenv("USERPROFILE");
    if (!env)
        env = std::getenv("HOMEPATH");
    if (!env)
        env = std::getenv("HOME"); // falls unter MSYS/MinGW
    if (!env)
        return {};
    home = env;

    // Backslashes ? Slashes normalisieren
    for (char &c : home)
        if (c == '\\')
            c = '/';

#elif defined(__APPLE__) || defined(__linux__)
    const char *env = std::getenv("HOME");
    if (!env)
        return {};
    home = env;

#else

    #error Unsupported platform

#endif

    // -------------------------
    // 2. ".config"-Ordner anhängen
    // -------------------------
    // trailing slash entfernen
    while (!home.empty() && (home.back() == '/' || home.back() == '\\'))
        home.pop_back();

    return home;
}

#pragma region Data

inline std::string escapeNewline(std::string input)
{
    std::string result;
    result.reserve(input.size());

    for (char c : input)
    {
        if (c == '\n')
        {
            result += "\\n";
        }
        else
        {
            result += c;
        }
    }

    return result;
}

inline std::string unescapeNewline(std::string input)
{
    std::string result;
    result.reserve(input.size());

    for (size_t i = 0; i < input.size(); ++i)
    {
        if (input[i] == '\\' && i + 1 < input.size() && input[i + 1] == 'n')
        {
            result += '\n';
            ++i; // 'n' überspringen
        }
        else
        {
            result += input[i];
        }
    }

    return result;
}

#pragma endregion

inline std::int64_t getEditTime(const std::filesystem::path &path)
{
    std::error_code ec;

    auto fileTime = std::filesystem::last_write_time(path, ec);

    if (ec)
        return 0;

    auto systemTime =
        std::chrono::time_point_cast<std::chrono::system_clock::duration>(
            fileTime - std::filesystem::file_time_type::clock::now() +
            std::chrono::system_clock::now());

    return std::chrono::duration_cast<std::chrono::seconds>(
               systemTime.time_since_epoch())
        .count();
}

inline void openTerminal(const std::string &path)
{
#if defined(_WIN32)
    // Windows: CMD im angegebenen Ordner öffnen
    std::string cmd = "start \"\" cmd /K \"cd /d \"" + path + "\"\"";

#elif defined(__APPLE__)
    // macOS: Terminal.app im angegebenen Ordner öffnen
    std::string cmd = "open -a Terminal \"" + path + "\"";

#elif defined(__linux__)
    // Linux: Standard-Terminal im angegebenen Ordner öffnen
    std::string cmd = "cd \"" + path + "\" && x-terminal-emulator";

#else
    #error Unsupported platform
#endif

    std::system(cmd.c_str());
}

// END of shadowdaras LIB