Skip to content

Commit

Permalink
wip: name_match_predicate
Browse files Browse the repository at this point in the history
  • Loading branch information
SpectraL519 committed Apr 1, 2024
1 parent 0d1755c commit c181619
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 46 deletions.
63 changes: 46 additions & 17 deletions include/ap/argument_parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,37 @@ struct argument_name {
* @return Equality of argument names.
*/
bool operator==(const argument_name& other) const noexcept {
return this->primary == other.primary;
if (not (this->secondary and other.secondary) and (this->secondary or other.secondary))
return false;

if (this->primary != other.primary)
return false;

return this->secondary ? this->secondary.value() == other.secondary.value() : true;
}

/**
* @brief Matches the given string to the argument_name instance.
* @param arg_name The name string to match.
* @return True if name is equal to either the primary or the secondary name of the argument_name instance.
*/
[[nodiscard]] bool match(std::string_view arg_name) const noexcept {
return arg_name == this->primary or (this->secondary and arg_name == this->secondary.value());
}

/**
* @brief Matches the given argument name to the argument_name instance.
* @param arg_name The name string to match.
* @return True if arg_name's primary or secondary value matches the argument_name instance.
*/
[[nodiscard]] bool match(const argument_name arg_name) const noexcept {
if (not this->match(arg_name.primary))
return false;

if (arg_name.secondary)
return this->match(arg_name.secondary.value());

return true;
}

/**
Expand Down Expand Up @@ -1162,7 +1192,6 @@ class argument_parser {
*/
argument_parser& default_positional_arguments(const std::vector<default_argument::positional>& arg_discriminator_list
) noexcept {
std::cout << ">>> default_positional_arguments" << std::endl;
for (const auto arg_discriminator : arg_discriminator_list)
this->_add_default_positional_argument(arg_discriminator);
return *this;
Expand All @@ -1189,14 +1218,10 @@ class argument_parser {
argument::positional_argument<T>& add_positional_argument(std::string_view primary_name) {
// TODO: check forbidden characters

std::cout << ">>> add_positional_argument(" << primary_name << ")" << std::endl;

const argument::detail::argument_name arg_name = {primary_name};
if (this->_is_arg_name_used(arg_name))
throw error::argument_name_used_error(arg_name);

std::cout << ">>> arg name not used" << std::endl;

this->_positional_args.push_back(std::make_unique<argument::positional_argument<T>>(arg_name));
return static_cast<argument::positional_argument<T>&>(*this->_positional_args.back());
}
Expand Down Expand Up @@ -1408,17 +1433,14 @@ class argument_parser {
* @param arg_discriminator The default positional argument discriminator.
*/
void _add_default_positional_argument(const default_argument::positional arg_discriminator) noexcept {
std::cout << ">>> _add_default_positional_argument" << std::endl;
switch (arg_discriminator) {
case default_argument::positional::input:
std::cout << "\tinput" << std::endl;
this->add_positional_argument("input")
.action<ap::void_action>(ap::action::check_file_exists_action())
.help("Input file path");
break;

case default_argument::positional::output:
std::cout << "\toutput" << std::endl;
this->add_positional_argument("output").help("Output file path");
break;
}
Expand Down Expand Up @@ -1503,12 +1525,21 @@ class argument_parser {
using argument_predicate_type = std::function<bool(const argument_ptr_type&)>;

/**
* @brief Function to create a predicate for finding arguments by name.
* @brief Returns an unary predicate function which checks if the given name matches the argument's name
* @param arg_name The name of the argument.
* @return Argument predicate based on the provided name.
*/
[[nodiscard]] argument_predicate_type _name_match_predicate(std::string_view arg_name) const noexcept {
return [&arg_name](const argument_ptr_type& arg) { return arg->name().match(arg_name); };
}

/**
* @brief Returns an unary predicate function which checks if the given name matches the argument's name
* @param arg_name The name of the argument.
* @return Argument predicate based on the provided name.
*/
[[nodiscard]] argument_predicate_type _name_eq_predicate(const argument::detail::argument_name& arg_name) const noexcept {
return [&arg_name](const argument_ptr_type& arg) { return arg_name == arg->name(); };
[[nodiscard]] argument_predicate_type _name_match_predicate(const argument::detail::argument_name& arg_name) const noexcept {
return [&arg_name](const argument_ptr_type& arg) { return arg->name().match(arg_name); };
}

/**
Expand All @@ -1517,7 +1548,7 @@ class argument_parser {
* @return True if the argument name is already used, false otherwise.
*/
[[nodiscard]] bool _is_arg_name_used(const argument::detail::argument_name& arg_name) const noexcept {
const auto predicate = this->_name_eq_predicate(arg_name);
const auto predicate = this->_name_match_predicate(arg_name);

if (std::ranges::find_if(this->_positional_args, predicate) != this->_positional_args.end())
return true;
Expand Down Expand Up @@ -1621,7 +1652,7 @@ class argument_parser {
while (cmd_it != cmd_args.end()) {
if (cmd_it->discriminator == cmd_argument::type_discriminator::flag) {
auto opt_arg_it =
std::ranges::find_if(this->_optional_args, this->_name_eq_predicate({cmd_it->value}));
std::ranges::find_if(this->_optional_args, this->_name_match_predicate(cmd_it->value));

if (opt_arg_it == this->_optional_args.end())
throw error::argument_not_found_error(cmd_it->value);
Expand Down Expand Up @@ -1682,7 +1713,7 @@ class argument_parser {
* @return The argument with the specified name, if found; otherwise, std::nullopt.
*/
argument_opt_type _get_argument(const std::string_view& arg_name) const noexcept {
const auto predicate = this->_name_eq_predicate({arg_name});
const auto predicate = this->_name_match_predicate(arg_name);

if (auto pos_arg_it = std::ranges::find_if(this->_positional_args, predicate);
pos_arg_it != this->_positional_args.end()) {
Expand All @@ -1694,8 +1725,6 @@ class argument_parser {
return std::ref(**opt_arg_it);
}

std::cout << ">>> _get_argument(" << arg_name << ") - nullopt" << std::endl;

return std::nullopt;
}

Expand Down
34 changes: 17 additions & 17 deletions test/source/test_argument_name.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,55 +47,55 @@ TEST_CASE("argument_name members should be correctly "
REQUIRE_EQ(arg_name.secondary.value(), secondary_name);
}

TEST_CASE("argument_name::operator==(argument_name) should "
"return true if primary names are equal") {
TEST_CASE("argument_name::operator==(argument_name) should return false if primary names are not equal") {
const auto arg_name_a = default_argument_name_primary();
const auto arg_name_b = default_argument_name_primary_and_secondary();
const auto arg_name_b = argument_name{other_primary_name};

REQUIRE_EQ(arg_name_a, arg_name_b);
REQUIRE_NE(arg_name_a, arg_name_b);
}

TEST_CASE("argument_name::operator==(argument_name) should "
"return false if primary names are not equal") {
TEST_CASE("argument_name::operator==(argument_name) should return false if only one argument has both primary and secondary values") {
const auto arg_name_a = default_argument_name_primary();
const auto arg_name_b = argument_name{other_primary_name, other_secondary_name};
const auto arg_name_b = default_argument_name_primary_and_secondary();

REQUIRE_NE(arg_name_a, arg_name_b);
REQUIRE_NE(arg_name_b, arg_name_a);
}

TEST_CASE("argument_name::operator==(string_view) should "
"return true if at least one primary name matches") {
TEST_CASE("argument_name::match(string_view) should return true if the given string matches at least one name") {
SUBCASE("argument_name with primary name only") {
const auto arg_name = default_argument_name_primary();

REQUIRE_EQ(arg_name, primary_name);
REQUIRE(arg_name.match(primary_name));
}

SUBCASE("argument_name with both names") {
const auto arg_name = default_argument_name_primary_and_secondary();

REQUIRE_EQ(arg_name, primary_name);
REQUIRE_EQ(arg_name, secondary_name);
REQUIRE(arg_name.match(primary_name));
REQUIRE(arg_name.match(secondary_name));
}
}

TEST_CASE("argument_name::operator==(string_view) should "
TEST_CASE("argument_name::match(string_view) should "
"return false if no primary name matches") {
SUBCASE("argument_name with primary name only") {
const auto arg_name = default_argument_name_primary();

REQUIRE_NE(arg_name, other_primary_name);
REQUIRE_NE(arg_name, other_secondary_name);
REQUIRE_FALSE(arg_name.match(other_primary_name));
REQUIRE_FALSE(arg_name.match(other_secondary_name));
}

SUBCASE("argument_name with both names") {
const auto arg_name = default_argument_name_primary_and_secondary();

REQUIRE_NE(arg_name, other_primary_name);
REQUIRE_NE(arg_name, other_secondary_name);
REQUIRE_FALSE(arg_name.match(other_primary_name));
REQUIRE_FALSE(arg_name.match(other_secondary_name));
}
}

// TODO: match for argument_name

TEST_CASE("operator<< should push correct data to the output stream") {
std::stringstream ss, expected_ss;

Expand Down
30 changes: 18 additions & 12 deletions test/source/test_argument_parser_add_argument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ TEST_CASE_FIXTURE(argument_parser_test_fixture, "default_positional_arguments sh
{ ap::default_argument::positional::input, ap::default_argument::positional::output }
);

std::cout << ">>> add - ok" << std::endl;
const auto input_arg = sut_get_argument("input");
REQUIRE(input_arg);
REQUIRE_FALSE(input_arg->get().is_optional());

REQUIRE_FALSE(sut_get_argument("input")->get().is_optional());
std::cout << ">>> input - ok" << std::endl;

REQUIRE_FALSE(sut_get_argument("output")->get().is_optional());
std::cout << ">>> output - ok" << std::endl;
const auto output_arg = sut_get_argument("output");
REQUIRE(output_arg);
REQUIRE_FALSE(output_arg->get().is_optional());
}

TEST_CASE_FIXTURE(argument_parser_test_fixture, "default_optional_arguments should add the specified arguments") {
Expand All @@ -44,14 +44,20 @@ TEST_CASE_FIXTURE(argument_parser_test_fixture, "default_optional_arguments shou
ap::default_argument::optional::output }
);

REQUIRE(sut_get_argument("help")->get().is_optional());
REQUIRE(sut_get_argument("h")->get().is_optional());
const auto help_arg = sut_get_argument("help");
REQUIRE(help_arg);
REQUIRE(help_arg->get().is_optional());
// TODO: secondary flag

REQUIRE(sut_get_argument("input")->get().is_optional());
REQUIRE(sut_get_argument("i")->get().is_optional());
const auto input_arg = sut_get_argument("help");
REQUIRE(input_arg);
REQUIRE(input_arg->get().is_optional());
// TODO: secondary flag

REQUIRE(sut_get_argument("output")->get().is_optional());
REQUIRE(sut_get_argument("o")->get().is_optional());
const auto output_arg = sut_get_argument("output");
REQUIRE(output_arg);
REQUIRE_FALSE(output_arg->get().is_optional());
// TODO: secondary flag
}

TEST_CASE_FIXTURE(argument_parser_test_fixture, "add_positional_argument should return a positional argument reference") {
Expand Down

0 comments on commit c181619

Please sign in to comment.