diff --git a/CMakeLists.txt b/CMakeLists.txt index a2318a7..3ae9eac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.20) include(cmake/color.cmake) -set(BLT_VERSION 4.0.10) +set(BLT_VERSION 4.0.11) set(BLT_TARGET BLT) diff --git a/include/blt/parse/argparse_v2.h b/include/blt/parse/argparse_v2.h index 184cd7f..c5f53cf 100644 --- a/include/blt/parse/argparse_v2.h +++ b/include/blt/parse/argparse_v2.h @@ -110,7 +110,7 @@ namespace blt::argparse [[nodiscard]] std::string error_string() const; - [[nodiscard]] const char* what() const override + [[nodiscard]] const char* what() const noexcept override { return "Please use error_string() method instead of what(). This exception should *always* be caught!"; } @@ -134,21 +134,40 @@ namespace blt::argparse using arg_t = meta::arg_helper; using arg_vec_t = meta::arg_helper...>; - template typename... Defaults> - static auto make_lists_only_visitor(Defaults>&&... d) + template + static auto make_lists_only_visitor_per_object_in_vec(const PerObjectAction& action) { return lambda_visitor{ invalid_option_lambda, - std::forward(d)... + ([&action](std::vector arg_vec) + { + for (const auto& arg : arg_vec) + action(arg_vec, arg); + })... }; } - template typename... Defaults> - static auto make_reject_lists_visitor_t(Defaults&&... d) + template + static auto make_lists_only_visitor_on_vec(const DefaultAction& action) + { + return lambda_visitor{ + invalid_option_lambda..., + ([&action](std::vector arg_vec) + { + action(arg_vec); + })... + }; + } + + template + static auto make_reject_lists_visitor_t(const DefaultAction& action) { return lambda_visitor{ invalid_option_lambda...>, - std::forward(d)... + ([&action](Args arg) + { + action(arg); + })... }; } }; @@ -346,7 +365,7 @@ namespace blt::argparse private: void add(const argument_storage_t& values) { - for (const auto value : values) + for (const auto& value : values.m_data) m_data.insert(value); } diff --git a/src/blt/parse/argparse_v2 (conflicted copy 2025-02-13 150255).cpp b/src/blt/parse/argparse_v2 (conflicted copy 2025-02-13 150255).cpp deleted file mode 100644 index 1a32833..0000000 --- a/src/blt/parse/argparse_v2 (conflicted copy 2025-02-13 150255).cpp +++ /dev/null @@ -1,165 +0,0 @@ -/* - * - * Copyright (C) 2025 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 . - */ -#include -#include - -namespace blt::argparse -{ - namespace detail - { - // Unit Tests for class argument_string_t - // Test Case 1: Ensure the constructor handles flags correctly - void test_argument_string_t_flag_basic(const hashset_t& prefixes) - { - const argument_string_t arg("-f", prefixes); - BLT_ASSERT(arg.is_flag() && "Expected argument to be identified as a flag."); - BLT_ASSERT(arg.value() == "f" && "Flag value should match the input string."); - } - - // Test Case 2: Ensure the constructor handles long flags correctly - void test_argument_string_t_long_flag(const hashset_t& prefixes) - { - const argument_string_t arg("--file", prefixes); - BLT_ASSERT(arg.is_flag() && "Expected argument to be identified as a flag."); - BLT_ASSERT(arg.value() == "file" && "Long flag value should match the input string."); - } - - // Test Case 3: Ensure positional arguments are correctly identified - void test_argument_string_t_positional_argument(const hashset_t& prefixes) - { - const argument_string_t arg("filename.txt", prefixes); - BLT_ASSERT(!arg.is_flag() && "Expected argument to be identified as positional."); - BLT_ASSERT(arg.value() == "filename.txt" && "Positional argument value should match the input string."); - } - - // Test Case 5: Handle an empty string - void test_argument_string_t_empty_input(const hashset_t& prefixes) - { - const argument_string_t arg("", prefixes); - BLT_ASSERT(!arg.is_flag() && "Expected an empty input to be treated as positional, not a flag."); - BLT_ASSERT(arg.value().empty() && "Empty input should have an empty value."); - } - - // Test Case 6: Handle edge case of a single hyphen (`-`) which might be ambiguous - void test_argument_string_t_single_hyphen(const hashset_t& prefixes) - { - const argument_string_t arg("-", prefixes); - BLT_ASSERT(arg.is_flag() && "Expected single hyphen (`-`) to be treated as a flag."); - BLT_ASSERT(arg.value().empty() && "Single hyphen flag should have empty value."); - BLT_ASSERT(arg.get_flag() == "-" && "Single hyphen flag should match the input string."); - } - - // Test Case 8: Handle arguments with prefix only (like "--") - void test_argument_string_t_double_hyphen(const hashset_t& prefixes) - { - const argument_string_t arg("--", prefixes); - BLT_ASSERT(arg.is_flag() && "Double hyphen ('--') should be treated as a flag."); - BLT_ASSERT(arg.value().empty() && "Double hyphen flag should have empty value."); - BLT_ASSERT(arg.get_flag() == "--" && "Double hyphen value should match the input string."); - } - - // Test Case 9: Validate edge case of an argument with spaces - void test_argument_string_t_with_spaces(const hashset_t& prefixes) - { - const argument_string_t arg(" ", prefixes); - BLT_ASSERT(!arg.is_flag() && "Arguments with spaces should not be treated as flags."); - BLT_ASSERT(arg.value() == " " && "Arguments with spaces should match the input string."); - } - - // Test Case 10: Validate arguments with numeric characters - void test_argument_string_t_numeric_flag(const hashset_t& prefixes) - { - const argument_string_t arg("-123", prefixes); - BLT_ASSERT(arg.is_flag() && "Numeric flags should still be treated as flags."); - BLT_ASSERT(arg.value() == "123" && "Numeric flag value should match the input string."); - } - - // Test Case 11: Ensure the constructor handles '+' flag correctly - void test_argument_string_t_plus_flag_basic(const hashset_t& prefixes) - { - const argument_string_t arg("+f", prefixes); - BLT_ASSERT(arg.is_flag() && "Expected argument to be identified as a flag."); - BLT_ASSERT(arg.value() == "f" && "Plus flag value should match the input string."); - } - - // Test Case 13: Handle edge case of a single plus (`+`) which might be ambiguous - void test_argument_string_t_single_plus(const hashset_t& prefixes) - { - const argument_string_t arg("+", prefixes); - BLT_ASSERT(arg.is_flag() && "Expected single plus (`+`) to be treated as a flag."); - BLT_ASSERT(arg.value().empty() && "Single plus flag should have empty value."); - BLT_ASSERT(arg.get_flag() == "+" && "Single plus flag should match the input string."); - } - - // Test Case 14: Handle arguments with prefix only (like '++') - void test_argument_string_t_double_plus(const hashset_t& prefixes) - { - const argument_string_t arg("++", prefixes); - BLT_ASSERT(arg.is_flag() && "Double plus ('++') should be treated as a flag."); - BLT_ASSERT(arg.value().empty() && "Double plus flag should have empty value."); - BLT_ASSERT(arg.get_flag() == "++" && "Double plus value should match the input string."); - } - - void run_all_tests_argument_string_t() - { - const hashset_t prefixes = {'-', '+'}; - test_argument_string_t_flag_basic(prefixes); - test_argument_string_t_long_flag(prefixes); - test_argument_string_t_positional_argument(prefixes); - test_argument_string_t_empty_input(prefixes); - test_argument_string_t_single_hyphen(prefixes); - test_argument_string_t_double_hyphen(prefixes); - test_argument_string_t_with_spaces(prefixes); - test_argument_string_t_numeric_flag(prefixes); - test_argument_string_t_plus_flag_basic(prefixes); - test_argument_string_t_single_plus(prefixes); - test_argument_string_t_double_plus(prefixes); - } - - std::string subparse_error::error_string() const - { - std::string message = "Subparser Error: "; - message += m_found_string; - message += " is not a valid command. Allowed commands are: {"; - for (const auto [i, allowed_string] : enumerate(m_allowed_strings)) - { - message += allowed_string; - if (i != m_allowed_strings.size() - 1) - message += ' '; - } - message += "}"; - return message; - } - - void test() - { - run_all_tests_argument_string_t(); - } - } - - void argument_string_t::process_argument() - { - size_t start = 0; - for (; start < m_argument.size() && m_allowed_flag_prefix->contains(m_argument[start]); start++) - { - } - - m_flag_section = {m_argument.data(), start}; - m_name_section = {m_argument.data() + start, m_argument.size() - start}; - } -} diff --git a/src/blt/parse/argparse_v2.cpp b/src/blt/parse/argparse_v2.cpp index f095652..5ea7735 100644 --- a/src/blt/parse/argparse_v2.cpp +++ b/src/blt/parse/argparse_v2.cpp @@ -255,7 +255,7 @@ namespace blt::argparse void argument_parser_t::parse_flag(argument_storage_t& parsed_args, argument_consumer_t& consumer, const std::string_view arg) { auto& flag = m_flag_arguments[arg]; - auto dest = flag->m_dest.value_or(arg); + auto dest = flag->m_dest.value_or(std::string{arg}); std::visit(lambda_visitor{ [&parsed_args, &consumer, &dest, &flag, arg](const nargs_t arg_enum) { @@ -270,9 +270,10 @@ namespace blt::argparse parsed_args.m_data.insert({dest, *flag->m_const_value}); } break; - [[fallthrough]] case nargs_t::ALL_AT_LEAST_ONE: + case nargs_t::ALL_AT_LEAST_ONE: if (!consumer.can_consume()) std::cerr << "Error expected at least one argument to be consumed by '" << arg << '\'' << std::endl; + [[fallthrough]]; case nargs_t::ALL: std::vector args; while (consumer.can_consume() && !consumer.peek().is_flag()) @@ -300,7 +301,7 @@ namespace blt::argparse } args.push_back(consumer.consume().get_argument()); } - if (args.size() != argc) + if (args.size() != static_cast(argc)) { std::cerr << "This error condition should not be possible. " @@ -338,12 +339,12 @@ namespace blt::argparse std::cerr << "Constant value for flag '" << arg << "' type doesn't values already present!" << std::endl; std::exit(1); } - + auto visitor = detail::arg_meta_type_helper_t::make_lists_only_visitor_on_vec([&flag](auto& vec) + { + vec.push_back(std::get>::value_type>(*flag->m_const_value)); + }); + std::visit(visitor, data); } - // if (parsed_args.contains(dest)) - // { - // std::visit(detail::arg_meta_type_helper_t::make_lists_only_visitor(handle_insert), parsed_args.m_data[dest]); - // } case action_t::STORE_CONST: std::cerr << "Store const flag called with an argument. This condition doesn't make sense." << std::endl; print_usage(); @@ -391,7 +392,7 @@ namespace blt::argparse std::cerr << "Error: " << type << " argument '" << key << "' was not found but is required by the program" << std::endl; exit(1); } - auto dest = value->m_dest.value_or(key); + auto dest = value->m_dest.value_or(std::string{key}); if (value->m_default_value && !parsed_args.contains(dest)) parsed_args.m_data.insert({dest, *value->m_default_value}); }