Brett 2025-02-12 19:42:50 -05:00
parent a437935ab0
commit 457dd5203b
3 changed files with 95 additions and 23 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
include(cmake/color.cmake) include(cmake/color.cmake)
set(BLT_VERSION 4.0.0) set(BLT_VERSION 4.0.1)
set(BLT_TARGET BLT) set(BLT_TARGET BLT)

View File

@ -26,7 +26,10 @@
#include <vector> #include <vector>
#include <variant> #include <variant>
#include <optional> #include <optional>
#include <type_traits>
#include <blt/iterator/enumerate.h> #include <blt/iterator/enumerate.h>
#include <blt/std/ranges.h>
#include <blt/std/utility.h>
namespace blt::argparse namespace blt::argparse
{ {
@ -35,8 +38,10 @@ namespace blt::argparse
inline hashset_t<std::string_view> allowed_flag_prefixes = {"-", "--", "+"}; inline hashset_t<std::string_view> allowed_flag_prefixes = {"-", "--", "+"};
std::string flag_prefixes_as_string(); std::string flag_prefixes_as_string();
hashset_t<char> prefix_characters();
inline std::string flag_prefix_list_string = flag_prefixes_as_string(); 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 class bad_flag final : public std::runtime_error
{ {
@ -64,7 +69,42 @@ namespace blt::argparse
using arg_data_t = std::variant<arg_primitive_data_t, arg_list_data_t>; using arg_data_t = std::variant<arg_primitive_data_t, arg_list_data_t>;
template <typename T> template <typename T>
class arg_type_t; struct arg_type_t
{
static T convert(const std::string_view value)
{
const std::string temp{value};
if constexpr (std::is_same_v<T, i8> || std::is_same_v<T, i16> || std::is_same_v<T, i32>)
{
return static_cast<T>(std::stoi(temp));
}
else if constexpr (std::is_same_v<T, i64>)
{
return static_cast<i64>(std::stoll(temp));
}
else if constexpr (std::is_same_v<T, u8> || std::is_same_v<T, u16> || std::is_same_v<T, u32>)
{
return static_cast<T>(std::stoul(temp));
}
else if constexpr (std::is_same_v<T, u64>)
{
return static_cast<u64>(std::stoull(temp));
}
else if constexpr (std::is_same_v<T, float>)
{
return std::stof(temp);
}
else if constexpr (std::is_same_v<T, double>)
{
return std::stod(temp);
} else
{
static_assert(std::is_arithmetic_v<T>, "Unsupported type for this specialization");
}
BLT_UNREACHABLE;
}
};
void test(); void test();
} }
@ -115,7 +155,7 @@ namespace blt::argparse
size_t start = m_argument.size(); size_t start = m_argument.size();
for (auto [i, c] : enumerate(m_argument)) for (auto [i, c] : enumerate(m_argument))
{ {
if (std::isalnum(c)) if (!detail::prefix_characters_set.contains(c))
{ {
start = i; start = i;
break; break;
@ -140,38 +180,37 @@ namespace blt::argparse
class argument_consumer_t class argument_consumer_t
{ {
public: public:
argument_consumer_t(const i32 argc, const char** argv): argv(argv), argc(argc) explicit argument_consumer_t(const span<argument_string_t>& args): args(args)
{ {
} }
[[nodiscard]] std::string_view peek(const i32 offset = 0) const [[nodiscard]] argument_string_t peek(const i32 offset = 0) const
{ {
return argv[forward_index + offset]; return args[forward_index + offset];
} }
std::string_view consume() argument_string_t consume()
{ {
return argv[forward_index++]; return args[forward_index++];
} }
[[nodiscard]] i32 position() const [[nodiscard]] i32 position() const
{ {
return argc; return forward_index;
} }
[[nodiscard]] i32 remaining() const [[nodiscard]] i32 remaining() const
{ {
return argc - forward_index; return static_cast<i32>(args.size()) - forward_index;
} }
[[nodiscard]] bool has_next(const i32 offset = 0) const [[nodiscard]] bool has_next(const i32 offset = 0) const
{ {
return (offset + forward_index) < argc; return (offset + forward_index) < args.size();
} }
private: private:
const char** argv; span<argument_string_t> args;
i32 argc;
i32 forward_index = 0; i32 forward_index = 0;
}; };
} }

View File

@ -41,6 +41,15 @@ namespace blt::argparse
} }
return result; return result;
} }
hashset_t<char> prefix_characters()
{
hashset_t<char> result;
for (auto [i, v] : enumerate(allowed_flag_prefixes))
for (auto c : v)
result.insert(c);
return result;
}
} }
@ -100,16 +109,9 @@ namespace blt::argparse
// Test Case 9: Validate edge case of an argument with spaces // Test Case 9: Validate edge case of an argument with spaces
void test_argument_string_t_with_spaces() void test_argument_string_t_with_spaces()
{ {
try const argument_string_t arg(" ");
{ BLT_ASSERT(!arg.is_flag() && "Arguments with spaces should not be treated as flags.");
const argument_string_t arg(" "); BLT_ASSERT(arg.value() == " " && "Arguments with spaces should match the input string.");
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.");
} catch (bad_flag&)
{
return;
}
BLT_ASSERT(false && "Expected an exception to be thrown for arguments with spaces.");
} }
// Test Case 10: Validate arguments with numeric characters // Test Case 10: Validate arguments with numeric characters
@ -120,6 +122,33 @@ namespace blt::argparse
BLT_ASSERT(arg.value() == "123" && "Numeric flag value should match the input string."); 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 argument_string_t arg("+f");
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 argument_string_t arg("+");
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 argument_string_t arg("++");
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() void run_all_tests_argument_string_t()
{ {
test_argument_string_t_flag_basic(); test_argument_string_t_flag_basic();
@ -130,7 +159,11 @@ namespace blt::argparse
test_argument_string_t_double_hyphen(); test_argument_string_t_double_hyphen();
test_argument_string_t_with_spaces(); test_argument_string_t_with_spaces();
test_argument_string_t_numeric_flag(); test_argument_string_t_numeric_flag();
test_argument_string_t_plus_flag_basic();
test_argument_string_t_single_plus();
test_argument_string_t_double_plus();
} }
void test() void test()
{ {
run_all_tests_argument_string_t(); run_all_tests_argument_string_t();