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)
include(cmake/color.cmake)
set(BLT_VERSION 4.0.0)
set(BLT_VERSION 4.0.1)
set(BLT_TARGET BLT)

View File

@ -26,7 +26,10 @@
#include <vector>
#include <variant>
#include <optional>
#include <type_traits>
#include <blt/iterator/enumerate.h>
#include <blt/std/ranges.h>
#include <blt/std/utility.h>
namespace blt::argparse
{
@ -35,8 +38,10 @@ namespace blt::argparse
inline hashset_t<std::string_view> allowed_flag_prefixes = {"-", "--", "+"};
std::string flag_prefixes_as_string();
hashset_t<char> 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
{
@ -64,7 +69,42 @@ namespace blt::argparse
using arg_data_t = std::variant<arg_primitive_data_t, arg_list_data_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();
}
@ -115,7 +155,7 @@ namespace blt::argparse
size_t start = m_argument.size();
for (auto [i, c] : enumerate(m_argument))
{
if (std::isalnum(c))
if (!detail::prefix_characters_set.contains(c))
{
start = i;
break;
@ -140,38 +180,37 @@ namespace blt::argparse
class argument_consumer_t
{
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
{
return argc;
return forward_index;
}
[[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
{
return (offset + forward_index) < argc;
return (offset + forward_index) < args.size();
}
private:
const char** argv;
i32 argc;
span<argument_string_t> args;
i32 forward_index = 0;
};
}

View File

@ -41,6 +41,15 @@ namespace blt::argparse
}
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
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.");
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.");
const argument_string_t arg(" ");
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
@ -120,6 +122,33 @@ namespace blt::argparse
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()
{
test_argument_string_t_flag_basic();
@ -130,7 +159,11 @@ namespace blt::argparse
test_argument_string_t_double_hyphen();
test_argument_string_t_with_spaces();
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()
{
run_all_tests_argument_string_t();