#pragma once /* * Copyright (C) 2024 Brett Terpstra * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BLT_PARSE_ARGPARSE_V2_H #define BLT_PARSE_ARGPARSE_V2_H #include #include #include #include #include #include #include #include #include #include #include namespace blt::argparse { namespace detail { inline hashset_t allowed_flag_prefixes = {"-", "--", "+"}; std::string flag_prefixes_as_string(); hashset_t prefix_characters(); inline std::string flag_prefix_list_string = flag_prefixes_as_string(); inline auto prefix_characters_set = prefix_characters(); class bad_flag final : public std::runtime_error { public: explicit bad_flag(const std::string& message): std::runtime_error(message) { } explicit bad_flag(const char* message): std::runtime_error(message) { } }; template struct arg_data_helper_t { using arg_primitive_data_t = std::variant; using arg_list_data_t = std::variant...>; }; using data_helper_t = arg_data_helper_t; using arg_primitive_data_t = data_helper_t::arg_primitive_data_t; using arg_list_data_t = data_helper_t::arg_list_data_t; using arg_data_t = std::variant; template struct arg_type_t { static T convert(const std::string_view value) { const std::string temp{value}; if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) { return static_cast(std::stoi(temp)); } else if constexpr (std::is_same_v) { return static_cast(std::stoll(temp)); } else if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) { return static_cast(std::stoul(temp)); } else if constexpr (std::is_same_v) { return static_cast(std::stoull(temp)); } else if constexpr (std::is_same_v) { return std::stof(temp); } else if constexpr (std::is_same_v) { return std::stod(temp); } else { static_assert(std::is_arithmetic_v, "Unsupported type for this specialization"); } BLT_UNREACHABLE; } }; void test(); } class argument_string_t { public: explicit argument_string_t(const char* input): m_argument(input) { if (input == nullptr) throw detail::bad_flag("Argument cannot be null!"); } [[nodiscard]] std::string_view get_flag() const { if (!flag_section) process_argument(); return *flag_section; } [[nodiscard]] std::string_view get_name() const { if (!name_section) process_argument(); return *name_section; } [[nodiscard]] std::string_view value() const { return get_name(); } [[nodiscard]] bool is_flag() const { if (!m_is_flag) process_argument(); return *m_is_flag; } [[nodiscard]] std::string_view get_argument() const { return m_argument; } private: void process_argument() const { size_t start = m_argument.size(); for (auto [i, c] : enumerate(m_argument)) { if (!detail::prefix_characters_set.contains(c)) { start = i; break; } } m_is_flag = (start != 0); flag_section = {m_argument.data(), start}; name_section = {m_argument.data() + start, m_argument.size() - start}; if (!flag_section->empty() && !detail::allowed_flag_prefixes.contains(*flag_section)) throw detail::bad_flag( "Invalid flag " + std::string(*flag_section) + " detected, flag is not in allowed list of flags! Must be one of " + detail::flag_prefix_list_string); } std::string_view m_argument; mutable std::optional m_is_flag; mutable std::optional flag_section; mutable std::optional name_section; }; class argument_consumer_t { public: explicit argument_consumer_t(const span& args): args(args) { } [[nodiscard]] argument_string_t peek(const i32 offset = 0) const { return args[forward_index + offset]; } argument_string_t consume() { return args[forward_index++]; } [[nodiscard]] i32 position() const { return forward_index; } [[nodiscard]] i32 remaining() const { return static_cast(args.size()) - forward_index; } [[nodiscard]] bool has_next(const i32 offset = 0) const { return (offset + forward_index) < args.size(); } private: span args; i32 forward_index = 0; }; } #endif //BLT_PARSE_ARGPARSE_V2_H