diff --git a/.clang-format b/.clang-format index 98218d0..e338790 100644 --- a/.clang-format +++ b/.clang-format @@ -1,47 +1,25 @@ -BasedOnStyle: Chromium AlignAfterOpenBracket: BlockIndent AlignArrayOfStructures: Right -AlignConsecutiveAssignments: - Enabled: true -AlignConsecutiveDeclarations: - Enabled: true - PadOperators: true -AlignConsecutiveMacros: - Enabled: true -AlignConsecutiveShortCaseStatements: - Enabled: true -AlignOperands: DontAlign +AlignConsecutiveAssignments: true +AlignConsecutiveDeclarations: true AllowShortBlocksOnASingleLine: Always -AllowShortCaseLabelsOnASingleLine: true -AllowShortFunctionsOnASingleLine: Empty +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: WithoutElse AllowShortLoopsOnASingleLine: true +BasedOnStyle: Chromium BinPackArguments: false -BreakBeforeBraces: Attach -ColumnLimit: 0 -ConstructorInitializerIndentWidth: 2 -ContinuationIndentWidth: 2 -Cpp11BracedListStyle: false -IncludeBlocks: Regroup -IncludeCategories: - - Regex: '^<.*>$' - Priority: 1 - - Regex: '^"Config/.*"' - Priority: 2 - - Regex: '^"Core/.*"' - Priority: 3 - - Regex: '^"Services/.*"' - Priority: 4 - - Regex: '^"UI/.*"' - Priority: 5 - - Regex: '^"Util/.*"' - Priority: 6 - - Regex: '^"Wrappers/.*"' - Priority: 7 - - Regex: '^".*"$' - Priority: 8 +BinPackParameters: false +IndentAccessModifiers: false IndentExternBlock: Indent -IndentPPDirectives: BeforeHash +LineEnding: LF NamespaceIndentation: All -QualifierAlignment: Left SpaceBeforeCpp11BracedList: true SpacesBeforeTrailingComments: 1 + +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '".*"' + Priority: 1 + - Regex: '<.*>' + Priority: -1 diff --git a/.clang-tidy b/.clang-tidy index 87e4d6b..e17b1ae 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,52 +1,42 @@ Checks: > *, - -ctad-maybe-unsupported, -abseil-*, -altera-*, - -boost-*, -bugprone-easily-swappable-parameters, -bugprone-implicit-widening-of-multiplication-result, - -cert-env33-c, -concurrency-mt-unsafe, -cppcoreguidelines-avoid-magic-numbers, - -cppcoreguidelines-macro-usage, -cppcoreguidelines-owning-memory, - -cppcoreguidelines-pro-bounds-array-to-pointer-decay, -cppcoreguidelines-pro-type-member-init, - -cppcoreguidelines-pro-type-vararg, -fuchsia-*, -google-*, -hicpp-*, -llvm-include-order, -llvm-include-order, - -llvm-namespace-comment, -llvmlibc-*, -misc-non-private-member-variables-in-classes, - -readability-avoid-nested-conditional-operator, + -modernize-use-trailing-return-type, -readability-braces-around-statements, - -readability-function-cognitive-complexity, -readability-implicit-bool-conversion, -readability-isolate-declaration, -readability-magic-numbers CheckOptions: - cppcoreguidelines-avoid-do-while.IgnoreMacros: true - readability-else-after-return.WarnOnUnfixable: false readability-identifier-naming.ClassCase: CamelCase readability-identifier-naming.EnumCase: CamelCase readability-identifier-naming.LocalConstantCase: camelBack readability-identifier-naming.LocalVariableCase: camelBack readability-identifier-naming.GlobalFunctionCase: CamelCase - readability-identifier-naming.MemberCase: camelBack + readability-identifier-naming.MemberCase: lower_case readability-identifier-naming.MethodCase: camelBack readability-identifier-naming.MethodIgnoredRegexp: ((to|from)_class) readability-identifier-naming.ParameterPackCase: lower_case - readability-identifier-naming.PrivateMemberCase: camelBack + readability-identifier-naming.PrivateMemberCase: CamelCase readability-identifier-naming.PrivateMemberPrefix: 'm_' - readability-identifier-naming.PrivateMethodCase: camelBack + readability-identifier-naming.PrivateMethodCase: CamelCase readability-identifier-naming.PrivateMethodPrefix: '' readability-identifier-naming.ProtectedMemberPrefix: 'm_' readability-identifier-naming.ProtectedMethodPrefix: '' - readability-identifier-naming.PublicMemberCase: camelBack + readability-identifier-naming.PublicMemberCase: lower_case readability-identifier-naming.StaticConstantCase: UPPER_CASE readability-identifier-naming.StaticVariableCase: CamelCase readability-identifier-naming.StructCase: CamelCase diff --git a/.envrc b/.envrc deleted file mode 100644 index c4b17d7..0000000 --- a/.envrc +++ /dev/null @@ -1 +0,0 @@ -use_flake diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index fcadb2c..0000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -* text eol=lf diff --git a/.gitignore b/.gitignore index 26e109b..60d4bfa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,28 +1,2 @@ -.cache/ -.cmake/ -.direnv/ .idea/ -.vs/ -.vscode/ -.xmake/ -.DS_Store - -bin/ -build/ -cmake-build-debug/ -CMakeFiles/ -out/ -Testing/ - -build.ninja -CMakeCache.txt -cmake_install.cmake -CMakeSettings.json -compile_commands.json -config.toml -draconis++ -Makefile -result -subprojects/* -!subprojects/*.wrap -subprojects/sqlite3.wrap +cmake-build-*/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 91223d5..0000000 --- a/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "subprojects/glaze"] - path = subprojects/glaze - url = https://github.com/stephenberry/glaze -[submodule "subprojects/dbus_cxx"] - path = subprojects/dbus_cxx - url = https://github.com/dbus-cxx/dbus-cxx diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2efb0ce --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.28) + +project(draconis__) + +set(CMAKE_CXX_STANDARD 26) +set(CMAKE_MAKE_PROGRAM "Ninja") + +add_executable(draconis__ main.cpp + util.h) + +find_package(fmt CONFIG REQUIRED) +find_package(cppwinrt CONFIG REQUIRED) + +target_link_libraries(draconis__ PRIVATE fmt::fmt-header-only) +target_link_libraries(draconis__ PRIVATE WindowsApp) diff --git a/flake.lock b/flake.lock deleted file mode 100644 index cb22d13..0000000 --- a/flake.lock +++ /dev/null @@ -1,96 +0,0 @@ -{ - "nodes": { - "nixpkgs": { - "locked": { - "lastModified": 1746576598, - "narHash": "sha256-FshoQvr6Aor5SnORVvh/ZdJ1Sa2U4ZrIMwKBX5k2wu0=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "b3582c75c7f21ce0b429898980eddbbf05c68e55", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_2": { - "locked": { - "lastModified": 1745377448, - "narHash": "sha256-jhZDfXVKdD7TSEGgzFJQvEEZ2K65UMiqW5YJ2aIqxMA=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "507b63021ada5fee621b6ca371c4fca9ca46f52c", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "nixpkgs": "nixpkgs", - "treefmt-nix": "treefmt-nix", - "utils": "utils" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "treefmt-nix": { - "inputs": { - "nixpkgs": "nixpkgs_2" - }, - "locked": { - "lastModified": 1746216483, - "narHash": "sha256-4h3s1L/kKqt3gMDcVfN8/4v2jqHrgLIe4qok4ApH5x4=", - "owner": "numtide", - "repo": "treefmt-nix", - "rev": "29ec5026372e0dec56f890e50dbe4f45930320fd", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "treefmt-nix", - "type": "github" - } - }, - "utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/flake.nix b/flake.nix deleted file mode 100644 index abdb9ff..0000000 --- a/flake.nix +++ /dev/null @@ -1,288 +0,0 @@ -{ - description = "C/C++ environment"; - - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; - treefmt-nix.url = "github:numtide/treefmt-nix"; - utils.url = "github:numtide/flake-utils"; - }; - - outputs = { - self, - nixpkgs, - treefmt-nix, - utils, - ... - }: - utils.lib.eachDefaultSystem ( - system: - if system == "x86_64-linux" - then let - pkgs = import nixpkgs {inherit system;}; - muslPkgs = import nixpkgs { - system = "x86_64-linux-musl"; - overlays = [ - (self: super: { - mimalloc = super.mimalloc.overrideAttrs (oldAttrs: { - cmakeFlags = - (oldAttrs.cmakeFlags or []) - ++ [(self.lib.cmakeBool "MI_LIBC_MUSL" true)]; - - postPatch = '' - sed -i '\||s|^|// |' src/prim/unix/prim.c - ''; - }); - }) - ]; - }; - - llvmPackages = muslPkgs.llvmPackages_20; - - stdenv = - muslPkgs.stdenvAdapters.useMoldLinker - llvmPackages.libcxxStdenv; - - glaze = (muslPkgs.glaze.override {inherit stdenv;}).overrideAttrs (oldAttrs: { - cmakeFlags = - (oldAttrs.cmakeFlags or []) - ++ [ - "-Dglaze_DEVELOPER_MODE=OFF" - "-Dglaze_BUILD_EXAMPLES=OFF" - ]; - - doCheck = false; - - enableAvx2 = stdenv.hostPlatform.isx86; - }); - - mkOverridden = buildSystem: pkg: ((pkg.override {inherit stdenv;}).overrideAttrs (oldAttrs: { - "${buildSystem}Flags" = - (oldAttrs."${buildSystem}Flags" or []) - ++ ( - if buildSystem == "meson" - then ["-Ddefault_library=static"] - else if buildSystem == "cmake" - then [ - "-D${pkgs.lib.toUpper pkg.pname}_BUILD_EXAMPLES=OFF" - "-D${pkgs.lib.toUpper pkg.pname}_BUILD_TESTS=OFF" - "-DBUILD_SHARED_LIBS=OFF" - ] - else throw "Invalid build system: ${buildSystem}" - ); - })); - - deps = with pkgs.pkgsStatic; [ - curlMinimal - dbus - glaze - llvmPackages.libcxx - openssl - sqlite - wayland - xorg.libXau - xorg.libXdmcp - xorg.libxcb - - (mkOverridden "cmake" ftxui) - (mkOverridden "cmake" pugixml) - (mkOverridden "cmake" sqlitecpp) - (mkOverridden "meson" tomlplusplus) - ]; - in { - packages = rec { - draconisplusplus = stdenv.mkDerivation { - name = "draconis++"; - version = "0.1.0"; - src = self; - - nativeBuildInputs = with muslPkgs; [ - cmake - meson - ninja - pkg-config - ]; - - buildInputs = deps; - - configurePhase = '' - meson setup build --buildtype release - ''; - - buildPhase = '' - meson compile -C build - ''; - - installPhase = '' - mkdir -p $out/bin - mv build/draconis++ $out/bin/draconis++ - ''; - - NIX_ENFORCE_NO_NATIVE = 0; - meta.staticExecutable = true; - }; - - draconisplusplus-generic = draconisplusplus.overrideAttrs {NIX_ENFORCE_NO_NATIVE = 1;}; - - default = draconisplusplus; - }; - - devShell = muslPkgs.mkShell.override {inherit stdenv;} { - packages = - (with pkgs; [ - bear - cachix - cmake - ]) - ++ (with muslPkgs; [ - llvmPackages_20.clang-tools - meson - ninja - pkg-config - (pkgs.writeScriptBin "build" "meson compile -C build") - (pkgs.writeScriptBin "clean" "meson setup build --wipe") - (pkgs.writeScriptBin "run" "meson compile -C build && build/draconis++") - ]) - ++ deps; - - NIX_ENFORCE_NO_NATIVE = 0; - }; - - formatter = treefmt-nix.lib.mkWrapper pkgs { - projectRootFile = "flake.nix"; - programs = { - alejandra.enable = true; - deadnix.enable = true; - clang-format = { - enable = true; - package = pkgs.llvmPackages.clang-tools; - }; - }; - }; - } - else let - pkgs = import nixpkgs {inherit system;}; - - llvmPackages = pkgs.llvmPackages_20; - - stdenv = with pkgs; - ( - if hostPlatform.isLinux - then stdenvAdapters.useMoldLinker - else lib.id - ) - llvmPackages.libcxxStdenv; - - deps = with pkgs; - [ - (glaze.override {enableAvx2 = hostPlatform.isx86;}) - ] - ++ (with pkgsStatic; [ - curl - ftxui - sqlitecpp - (tomlplusplus.overrideAttrs { - doCheck = false; - }) - ]) - ++ darwinPkgs - ++ linuxPkgs; - - darwinPkgs = nixpkgs.lib.optionals stdenv.isDarwin (with pkgs.pkgsStatic; [ - libiconv - apple-sdk_15 - ]); - - linuxPkgs = nixpkgs.lib.optionals stdenv.isLinux (with pkgs; - [ - valgrind - ] - ++ (with pkgsStatic; [ - dbus - pugixml - xorg.libxcb - wayland - ])); - in - with pkgs; { - packages = rec { - draconisplusplus = stdenv.mkDerivation { - name = "draconis++"; - version = "0.1.0"; - src = self; - - nativeBuildInputs = [ - cmake - meson - ninja - pkg-config - ]; - - buildInputs = deps; - - configurePhase = '' - meson setup build --buildtype release - ''; - - buildPhase = '' - meson compile -C build - ''; - - installPhase = '' - mkdir -p $out/bin - mv build/draconis++ $out/bin/draconis++ - ''; - }; - - default = draconisplusplus; - }; - - formatter = treefmt-nix.lib.mkWrapper pkgs { - projectRootFile = "flake.nix"; - programs = { - alejandra.enable = true; - deadnix.enable = true; - - clang-format = { - enable = true; - package = pkgs.llvmPackages.clang-tools; - }; - }; - }; - - devShell = mkShell.override {inherit stdenv;} { - packages = - [ - alejandra - bear - llvmPackages.clang-tools - cmake - include-what-you-use - lldb - hyperfine - meson - ninja - nvfetcher - pkg-config - unzip - - (writeScriptBin "build" "meson compile -C build") - (writeScriptBin "clean" "meson setup build --wipe") - (writeScriptBin "run" "meson compile -C build && build/draconis++") - ] - ++ deps; - - LD_LIBRARY_PATH = "${lib.makeLibraryPath deps}"; - NIX_ENFORCE_NO_NATIVE = 0; - - shellHook = lib.optionalString pkgs.stdenv.hostPlatform.isDarwin '' - export SDKROOT=${pkgs.pkgsStatic.apple-sdk_15}/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk - export DEVELOPER_DIR=${pkgs.pkgsStatic.apple-sdk_15} - export NIX_CFLAGS_COMPILE="-isysroot $SDKROOT" - export NIX_CXXFLAGS_COMPILE="-isysroot $SDKROOT" - export NIX_OBJCFLAGS_COMPILE="-isysroot $SDKROOT" - export NIX_OBJCXXFLAGS_COMPILE="-isysroot $SDKROOT" - ''; - }; - } - ); -} diff --git a/include/argparse.hpp b/include/argparse.hpp deleted file mode 100644 index 475de03..0000000 --- a/include/argparse.hpp +++ /dev/null @@ -1,3496 +0,0 @@ -/** - * @file argparse.hpp - * @brief Argument Parser for Modern C++ - * @author Pranav Srinivas Kumar - * @copyright Copyright (c) 2019-2022 Pranav Srinivas Kumar and other contributors - * @license MIT License - * - * A powerful, flexible, and easy-to-use command-line argument parser for modern C++. - * Provides a simple interface for defining, parsing, and validating command-line arguments. - * Supports both positional and optional arguments, subcommands, and more. - */ - -#pragma once - -/* - * __ _ _ __ __ _ _ __ __ _ _ __ ___ ___ - * / _` | '__/ _` | '_ \ / _` | '__/ __|/ _ \ Argument Parser for Modern C++ - * | (_| | | | (_| | |_) | (_| | | \__ \ __/ http://github.com/p-ranav/argparse - * \__,_|_| \__, | .__/ \__,_|_| |___/\___| - * |___/|_| - * * Licensed under the MIT License . - * SPDX-License-Identifier: MIT - * Copyright (c) 2019-2022 Pranav Srinivas Kumar - * and other contributors. - * - * 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. - */ - -#include -#include - -#ifndef ARGPARSE_MODULE_USE_STD_MODULE - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include -#endif - -#include "Util/Definitions.hpp" -#include "Util/Error.hpp" -#include "Util/Types.hpp" - -#ifndef ARGPARSE_CUSTOM_STRTOF - #define ARGPARSE_CUSTOM_STRTOF strtof -#endif - -#ifndef ARGPARSE_CUSTOM_STRTOD - #define ARGPARSE_CUSTOM_STRTOD strtod -#endif - -#ifndef ARGPARSE_CUSTOM_STRTOLD - #define ARGPARSE_CUSTOM_STRTOLD strtold -#endif - -// ReSharper disable CppTemplateParameterNeverUsed, CppDFATimeOver -// NOLINTBEGIN(readability-identifier-naming, readability-identifier-length, modernize-use-nullptr) -namespace argparse { - using namespace util::types; - using util::error::DracError, util::error::DracErrorCode; - - using ArgValue = std::variant< - bool, - int, - double, - String, - std::filesystem::path, - Vec, - Vec, - std::set, - std::set>; - - namespace details { - /** - * @brief Trait to check if a type has container-like properties - * @tparam T The type to check - * @tparam void SFINAE parameter - */ - template - struct HasContainerTraits : std::false_type {}; - - /** - * @brief Specialization for std::string - not considered a container - */ - template <> - struct HasContainerTraits : std::false_type {}; - - /** - * @brief Specialization for std::string_view - not considered a container - */ - template <> - struct HasContainerTraits : std::false_type {}; - - /** - * @brief Specialization for types that have container-like properties - * @tparam T The type to check - */ - template - struct HasContainerTraits().begin()), decltype(std::declval().end()), decltype(std::declval().size())>> : std::true_type {}; - - /** - * @brief Convenience variable template for checking if a type is a container - * @tparam T The type to check - */ - template - inline constexpr bool IsContainer = HasContainerTraits::value; - - /** - * @brief Trait to check if a type can be streamed to std::ostream - * @tparam T The type to check - * @tparam void SFINAE parameter - */ - template - struct HasStreamableTraits : std::false_type {}; - - /** - * @brief Specialization for types that can be streamed to std::ostream - * @tparam T The type to check - */ - template - struct HasStreamableTraits() << std::declval())>> : std::true_type {}; - - /** - * @brief Convenience variable template for checking if a type is streamable - * @tparam T The type to check - */ - template - inline constexpr bool IsStreamable = HasStreamableTraits::value; - - /** - * @brief Maximum number of elements to show when representing a container - */ - constexpr usize repr_max_container_size = 5; - - /** - * @brief Concept to check if a type can be formatted using std::format - * @tparam T The type to check - * @tparam CharT The character type for formatting - */ - template - concept Formattable = requires(const T& t, std::basic_format_context ctx) { - std::formatter, CharT>().format(t, ctx); - }; - - /** - * @brief Convert a value to its string representation - * @tparam T The type of the value to convert - * @param val The value to convert - * @return String representation of the value - */ - template - static auto repr(const T& val) -> String { - if constexpr (std::is_same_v) - return val ? "true" : "false"; - else if constexpr (std::is_convertible_v) - return std::format("\"{}\"", String { StringView { val } }); - else if constexpr (IsContainer) { - String result = "{"; - - const auto size = val.size(); - - if (size > 0) { - bool first = true; - - auto transformed_view = val | std::views::transform([](const auto& elem) { return details::repr(elem); }); - - if (size <= repr_max_container_size) { - for (const String& elem_repr : transformed_view) { - if (!first) - result += " "; - - result += elem_repr; - first = false; - } - } else { - for (const String& elem_repr : transformed_view | std::views::take(repr_max_container_size - 1)) { - if (!first) - result += " "; - - result += elem_repr; - first = false; - } - - result += "... "; - - result += details::repr(*std::prev(val.end())); - } - } - - result += "}"; - return result; - } else if constexpr (Formattable) - return std::format("{}", val); - else if constexpr (IsStreamable) { - std::stringstream out; - out << val; - return out.str(); - } else - return ""; - } - - /** - * @brief Radix constants for number parsing - */ - constexpr i32 radix_2 = 2; - constexpr i32 radix_8 = 8; - constexpr i32 radix_10 = 10; - constexpr i32 radix_16 = 16; - - /** - * @brief Helper function to apply a function with an additional argument - * @tparam F Function type - * @tparam Tuple Tuple type containing the base arguments - * @tparam Extra Type of the additional argument - * @tparam I... Index sequence - * @param f Function to apply - * @param t Tuple of base arguments - * @param x Additional argument - * @return Result of applying the function - */ - template - constexpr fn apply_plus_one_impl(F&& f, Tuple&& t, Extra&& x, std::index_sequence /*unused*/) -> decltype(auto) { - return std::invoke(std::forward(f), std::get(std::forward(t))..., std::forward(x)); - } - - /** - * @brief Wrapper for apply_plus_one_impl that creates the index sequence - * @tparam F Function type - * @tparam Tuple Tuple type containing the base arguments - * @tparam Extra Type of the additional argument - * @param f Function to apply - * @param t Tuple of base arguments - * @param x Additional argument - * @return Result of applying the function - */ - template - constexpr fn apply_plus_one(F&& f, Tuple&& t, Extra&& x) -> decltype(auto) { - return details::apply_plus_one_impl( - std::forward(f), std::forward(t), std::forward(x), std::make_index_sequence>> {} - ); - } - - /** - * @brief Get a tuple of pointers to the start and end of a string view - * @param s The string view to get pointers for - * @return Tuple of (start pointer, end pointer) - */ - constexpr fn pointer_range(const StringView s) noexcept -> std::tuple { - return { s.data(), s.data() + s.size() }; - } - - /** - * @brief Check if a string view starts with a given prefix - * @tparam CharT Character type - * @tparam Traits Character traits type - * @param prefix The prefix to check for - * @param s The string to check - * @return true if s starts with prefix, false otherwise - */ - template - constexpr fn starts_with(std::basic_string_view prefix, std::basic_string_view s) noexcept -> bool { - return s.substr(0, prefix.size()) == prefix; - } - - /** - * @brief Format flags for number parsing - */ - enum class chars_format : u8 { - scientific = 0xf1, ///< Scientific notation (e.g., 1.23e4) - fixed = 0xf2, ///< Fixed point notation (e.g., 123.45) - hex = 0xf4, ///< Hexadecimal notation (e.g., 0x1a) - binary = 0xf8, ///< Binary notation (e.g., 0b1010) - general = fixed | scientific ///< General format (either fixed or scientific) - }; - - /** - * @brief Result of checking for binary prefix - */ - struct ConsumeBinaryPrefixResult { - bool is_binary; ///< Whether the string had a binary prefix - StringView rest; ///< The string after removing the prefix - }; - - /** - * @brief Check if a string starts with a binary prefix and remove it - * @param s The string to check - * @return Result containing whether a binary prefix was found and the remaining string - */ - constexpr fn consume_binary_prefix(StringView s) -> ConsumeBinaryPrefixResult { - if (starts_with(StringView { "0b" }, s) || - starts_with(StringView { "0B" }, s)) { - s.remove_prefix(2); - return { .is_binary = true, .rest = s }; - } - - return { .is_binary = false, .rest = s }; - } - - /** - * @brief Result of checking for hexadecimal prefix - */ - struct ConsumeHexPrefixResult { - bool is_hexadecimal; ///< Whether the string had a hex prefix - StringView rest; ///< The string after removing the prefix - }; - - using namespace std::literals; - - /** - * @brief Check if a string starts with a hexadecimal prefix and remove it - * @param s The string to check - * @return Result containing whether a hex prefix was found and the remaining string - */ - constexpr fn consume_hex_prefix(StringView s) -> ConsumeHexPrefixResult { - if (starts_with("0x"sv, s) || starts_with("0X"sv, s)) { - s.remove_prefix(2); - return { .is_hexadecimal = true, .rest = s }; - } - - return { .is_hexadecimal = false, .rest = s }; - } - - /** - * @brief Parse a string into a number using std::from_chars - * @tparam T The type to parse into - * @tparam Param The radix or format to use - * @param s The string to parse - * @return Result containing the parsed number or an error - */ - template - fn do_from_chars(const StringView s) -> Result { - T x { 0 }; - auto [first, last] = pointer_range(s); - auto [ptr, ec] = std::from_chars(first, last, x, Param); - - if (ec == std::errc()) { - if (ptr == last) - return x; - - return Err(DracError(DracErrorCode::ParseError, std::format("pattern '{}' does not match to the end", String(s)))); - } - - if (ec == std::errc::invalid_argument) - return Err(DracError(DracErrorCode::InvalidArgument, std::format("pattern '{}' not found", String(s)))); - - if (ec == std::errc::result_out_of_range) - return Err(DracError(DracErrorCode::ParseError, std::format("'{}' not representable", String(s)))); - - return Err(DracError(DracErrorCode::InternalError, std::format("Unknown parsing error for '{}'", String(s)))); - } - - /** - * @brief Functor for parsing numbers with a specific radix - * @tparam T The type to parse into - * @tparam Param The radix to use (defaults to 0 for automatic detection) - */ - template - struct parse_number { - /** - * @brief Parse a string into a number - * @param s The string to parse - * @return Result containing the parsed number or an error - */ - static fn operator()(const StringView s)->Result { - return do_from_chars(s); - } - }; - - /** - * @brief Specialization for binary number parsing - * @tparam T The type to parse into - */ - template - struct parse_number { - /** - * @brief Parse a binary string into a number - * @param s The string to parse (must start with 0b or 0B) - * @return Result containing the parsed number or an error - */ - static fn operator()(const StringView s)->Result { - if (auto [ok, rest] = consume_binary_prefix(s); ok) - return do_from_chars(rest); - - return Err(DracError(DracErrorCode::InvalidArgument, "pattern not found")); - } - }; - - /** - * @brief Specialization for hexadecimal number parsing - * @tparam T The type to parse into - */ - template - struct parse_number { - /** - * @brief Parse a hexadecimal string into a number - * @param s The string to parse (may start with 0x or 0X) - * @return Result containing the parsed number or an error - */ - static fn operator()(const StringView s)->Result { - Result result; - - if (starts_with("0x"sv, s) || starts_with("0X"sv, s)) { - if (auto [ok, rest] = consume_hex_prefix(s); ok) - result = do_from_chars(rest); - else - return Err(DracError(DracErrorCode::InternalError, std::format("Inconsistent hex prefix detection for '{}'", String(s)))); - } else - result = do_from_chars(s); - - if (!result) - return Err(DracError(result.error().code, std::format("Failed to parse '{}' as hexadecimal: {}", String(s), result.error().message))); - - return result; - } - }; - - /** - * @brief Specialization for automatic number format detection - * @tparam T The type to parse into - */ - template - struct parse_number { - /** - * @brief Parse a string into a number, automatically detecting the format - * @param s The string to parse - * @return Result containing the parsed number or an error - * - * Supports: - * - Hexadecimal (0x/0X prefix) - * - Binary (0b/0B prefix) - * - Octal (0 prefix) - * - Decimal (no prefix) - */ - static fn operator()(const StringView s)->Result { - if (auto [ok, rest] = consume_hex_prefix(s); ok) { - Result result = do_from_chars(rest); - - if (!result) - return Err(DracError(result.error().code, std::format("Failed to parse '{}' as hexadecimal: {}", String(s), result.error().message))); - - return result; - } - - if (auto [ok_binary, rest_binary] = consume_binary_prefix(s); ok_binary) { - Result result = do_from_chars(rest_binary); - - if (!result) - return Err(DracError(result.error().code, std::format("Failed to parse '{}' as binary: {}", String(s), result.error().message))); - - return result; - } - - if (starts_with("0"sv, s)) { - Result result = do_from_chars(s); - - if (!result) - return Err(DracError(result.error().code, std::format("Failed to parse '{}' as octal: {}", String(s), result.error().message))); - - return result; - } - - Result result = do_from_chars(s); - - if (!result) - return Err(DracError(result.error().code, std::format("Failed to parse '{}' as decimal integer: {}", String(s), result.error().message))); - - return result; - } - }; - - /** - * @brief Custom string to number conversion functions - * @tparam T The type to convert to - */ - template - inline constexpr std::nullptr_t generic_strtod = nullptr; - template <> - inline const auto generic_strtod = ARGPARSE_CUSTOM_STRTOF; - template <> - inline const auto generic_strtod = ARGPARSE_CUSTOM_STRTOD; - template <> - inline const auto generic_strtod = ARGPARSE_CUSTOM_STRTOLD; - - /** - * @brief Parse a string into a floating point number - * @tparam T The floating point type to parse into - * @param s The string to parse - * @return Result containing the parsed number or an error - */ - template - fn do_strtod(const String& s) -> Result { - if (isspace(static_cast(s[0])) || s[0] == '+') - return Err(DracError(DracErrorCode::InvalidArgument, std::format("pattern '{}' not found", s))); - - auto [first, last] = pointer_range(s); - - char* ptr = nullptr; - - errno = 0; - - auto x = generic_strtod(first, &ptr); - - if (errno == 0) { - if (ptr == last) - return x; - - return Err(DracError(DracErrorCode::ParseError, std::format("pattern '{}' does not match to the end", s))); - } - - if (errno == ERANGE) - return Err(DracError(DracErrorCode::ParseError, std::format("'{}' not representable", s))); - - return Err(DracError(std::error_code(errno, std::system_category()))); - } - - /** - * @brief Specialization for general floating point format - * @tparam T The floating point type to parse into - */ - template - struct parse_number { - /** - * @brief Parse a string into a floating point number in general format - * @param s The string to parse - * @return Result containing the parsed number or an error - */ - fn operator()(const String& s)->Result { - if (auto [is_hex, rest] = consume_hex_prefix(s); is_hex) - return Err(DracError(DracErrorCode::InvalidArgument, "chars_format::general does not parse hexfloat")); - - if (auto [is_bin, rest] = consume_binary_prefix(s); is_bin) - return Err(DracError(DracErrorCode::InvalidArgument, "chars_format::general does not parse binfloat")); - - Result result = do_strtod(s); - if (!result) - return Err(DracError(result.error().code, std::format("Failed to parse '{}' as number: {}", s, result.error().message))); - return result; - } - }; - - /** - * @brief Specialization for hexadecimal floating point format - * @tparam T The floating point type to parse into - */ - template - struct parse_number { - /** - * @brief Parse a string into a floating point number in hexadecimal format - * @param s The string to parse (must start with 0x or 0X) - * @return Result containing the parsed number or an error - */ - fn operator()(const String& s)->Result { - if (auto [is_hex, rest] = consume_hex_prefix(s); !is_hex) - return Err(DracError(DracErrorCode::InvalidArgument, "chars_format::hex requires hexfloat format (e.g., 0x1.2p3)")); - - if (auto [is_bin, rest] = consume_binary_prefix(s); is_bin) - return Err(DracError(DracErrorCode::InvalidArgument, "chars_format::hex does not parse binfloat")); - - Result result = do_strtod(s); - if (!result) - return Err(DracError(result.error().code, std::format("Failed to parse '{}' as hexadecimal float: {}", s, result.error().message))); - return result; - } - }; - - /** - * @brief Specialization for binary floating point format - * @tparam T The floating point type to parse into - */ - template - struct parse_number { - /** - * @brief Parse a string into a floating point number in binary format - * @param s The string to parse (must start with 0b or 0B) - * @return Result containing the parsed number or an error - */ - fn operator()(const String& s)->Result { - if (auto [is_hex, rest] = consume_hex_prefix(s); is_hex) - return Err(DracError(DracErrorCode::InvalidArgument, "chars_format::binary does not parse hexfloat")); - - if (auto [is_bin, rest] = consume_binary_prefix(s); !is_bin) - return Err(DracError(DracErrorCode::InvalidArgument, "chars_format::binary requires binfloat format (e.g., 0b1.01p2)")); - - Result result = do_strtod(s); - if (!result) - return Err(DracError(result.error().code, std::format("Failed to parse '{}' as binary float: {}", s, result.error().message))); - return result; - } - }; - - /** - * @brief Specialization for scientific floating point format - * @tparam T The floating point type to parse into - */ - template - struct parse_number { - /** - * @brief Parse a string into a floating point number in scientific notation - * @param s The string to parse (must contain e or E) - * @return Result containing the parsed number or an error - */ - fn operator()(const String& s)->Result { - if (const auto [is_hex, rest] = consume_hex_prefix(s); is_hex) - return Err(DracError(DracErrorCode::InvalidArgument, "chars_format::scientific does not parse hexfloat")); - - if (const auto [is_bin, rest] = consume_binary_prefix(s); is_bin) - return Err(DracError(DracErrorCode::InvalidArgument, "chars_format::scientific does not parse binfloat")); - - if (s.find_first_of("eE") == String::npos) - return Err(DracError(DracErrorCode::InvalidArgument, "chars_format::scientific requires exponent part")); - - Result result = do_strtod(s); - - if (!result) - return Err(DracError(result.error().code, std::format("Failed to parse '{}' as scientific notation: {}", s, result.error().message))); - - return result; - } - }; - - /** - * @brief Specialization for fixed point floating point format - * @tparam T The floating point type to parse into - */ - template - struct parse_number { - /** - * @brief Parse a string into a floating point number in fixed point notation - * @param s The string to parse (must not contain e or E) - * @return Result containing the parsed number or an error - */ - fn operator()(const String& s)->Result { - if (const auto [is_hex, rest] = consume_hex_prefix(s); is_hex) - return Err(DracError(DracErrorCode::InvalidArgument, "chars_format::fixed does not parse hexfloat")); - - if (const auto [is_bin, rest] = consume_binary_prefix(s); is_bin) - return Err(DracError(DracErrorCode::InvalidArgument, "chars_format::fixed does not parse binfloat")); - - if (s.find_first_of("eE") != String::npos) - return Err(DracError(DracErrorCode::InvalidArgument, "chars_format::fixed does not parse exponent part")); - - Result result = do_strtod(s); - - if (!result) - return Err(DracError(result.error().code, std::format("Failed to parse '{}' as fixed notation: {}", s, result.error().message))); - - return result; - } - }; - - /** - * @brief Concept to check if a type can be converted to a string - * @tparam T The type to check - */ - template - concept ToStringConvertible = std::convertible_to || - std::convertible_to || - requires(const T& t) { std::format("{}", t); }; - - /** - * @brief Join a range of strings with a separator - * @tparam StrIt Iterator type for the string range - * @param first Iterator to the first string - * @param last Iterator past the last string - * @param separator The separator to use between strings - * @return The joined string - */ - template - fn join(StrIt first, StrIt last, const String& separator) -> String { - if (first == last) - return ""; - - std::stringstream value; - value << *first; - ++first; - - while (first != last) { - value << separator << *first; - ++first; - } - - return value.str(); - } - - /** - * @brief Trait to check if a type can be converted using std::to_string - * @tparam T The type to check - */ - template - struct can_invoke_to_string { - /** - * @brief SFINAE test for std::to_string support - * @tparam U The type to test - */ - template - // ReSharper disable CppFunctionIsNotImplemented - static fn test(int) -> decltype(std::to_string(std::declval()), std::true_type {}); - - /** - * @brief Fallback for types without std::to_string support - * @tparam U The type to test - */ - template - static fn test(...) -> std::false_type; - // ReSharper restore CppFunctionIsNotImplemented - - static constexpr bool value = decltype(test(0))::value; - }; - - /** - * @brief Trait to check if a type is supported for choice arguments - * @tparam T The type to check - */ - template - struct IsChoiceTypeSupported { - using CleanType = std::decay_t; - static const bool value = std::is_integral_v || - std::is_same_v || - std::is_same_v || - std::is_same_v; - }; - - /** - * @brief Calculate the Levenshtein distance between two strings - * @tparam StringType The string type to use - * @param s1 First string - * @param s2 Second string - * @return The Levenshtein distance between s1 and s2 - */ - template - fn get_levenshtein_distance(const StringType& s1, const StringType& s2) -> usize { - Vec> dp( - s1.size() + 1, Vec(s2.size() + 1, 0) - ); - - for (usize i = 0; i <= s1.size(); ++i) { - for (usize j = 0; j <= s2.size(); ++j) { - if (i == 0) { - dp[i][j] = j; - } else if (j == 0) { - dp[i][j] = i; - } else if (s1[i - 1] == s2[j - 1]) { - dp[i][j] = dp[i - 1][j - 1]; - } else { - dp[i][j] = 1 + std::min({ dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1] }); - } - } - } - - return dp[s1.size()][s2.size()]; - } - - /** - * @brief Find the most similar string in a map to a given input - * @tparam MapType The map-like container type - * @tparam ValueType The value type of the map - * @param map The map to search in - * @param input The input string to find matches for - * @return The most similar string from the map - */ - template - fn get_most_similar_string(const MapType& map, const String& input) -> String { - String most_similar {}; - usize min_distance = (std::numeric_limits::max)(); - - for (const auto& entry : map) - if (const usize distance = get_levenshtein_distance(entry.first, input); distance < min_distance) { - min_distance = distance; - most_similar = entry.first; - } - - return most_similar; - } - - /** - * @brief Trait to check if a type is a specialization of a template - * @tparam Test The type to check - * @tparam Ref The template to check against - */ - template class Ref> - struct is_specialization : std::false_type {}; - - /** - * @brief Specialization for when Test is a specialization of Ref - * @tparam Ref The template - * @tparam Args The template arguments - */ - template