Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ The below lists features of the `argparse` module that this implementation suppo

* The `add_argument()` method
* [x] name or flags
* [x] `action` (only `store`, `store_true`, `store_false`, `store_const`, and `help`)
* [x] `action` (only `store`, `store_true`, `store_false`, `store_const`, `help`, and `version`)
* [x] `nargs` (except for `REMAINDER`)
* [x] `const` (renamed to `const_` due to keyword clash)
* [x] `default` (renamed to `default_` due to keyword clash; only for optional arguments and with no string parsing)
Expand Down
53 changes: 52 additions & 1 deletion include/argparse.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ namespace argparse
store_true,
store_false,
store_const,
help
help,
version
};

enum Nargs
Expand Down Expand Up @@ -147,6 +148,7 @@ namespace argparse
using optstring = std::optional<std::string>;

class HelpRequested {};
class VersionRequested {};

public:
template<typename ...Args>
Expand Down Expand Up @@ -176,6 +178,16 @@ namespace argparse

return get_parameters();
}
catch (VersionRequested const &)
{
if (m_handle == Handle::errors_and_help || m_handle == Handle::help)
{
std::cout << format_version() << std::endl;
std::exit(EXIT_SUCCESS);
}

return get_parameters();
}
catch (parsing_error const & e)
{
if (m_handle == Handle::errors_and_help || m_handle == Handle::errors)
Expand Down Expand Up @@ -251,6 +263,16 @@ namespace argparse
return formatter.format_help();
}

auto format_version() const -> std::string
{
if (auto it = std::ranges::find_if(m_arguments, [](auto && arg) { return arg->has_version_action(); }); it != m_arguments.end())
{
return (*it)->get_version();
}

return "";
}

ArgumentParser()
: m_arguments()
, m_prog()
Expand Down Expand Up @@ -475,6 +497,7 @@ namespace argparse
{
std::vector<std::string> names;
std::string help;
std::string version;
std::string metavar;
std::string dest;
Action action = store;
Expand Down Expand Up @@ -550,6 +573,16 @@ namespace argparse
return m_options.action == store;
}

auto has_version_action() const -> bool
{
return m_options.action == version;
}

auto get_version() const -> std::string const &
{
return m_options.version;
}

auto get_help_message() const -> std::string const &
{
return m_options.help;
Expand Down Expand Up @@ -788,6 +821,9 @@ namespace argparse
case help:
m_value = true;
throw HelpRequested();
case version:
m_value = true;
throw VersionRequested();
}
m_present = true;
}
Expand All @@ -797,6 +833,7 @@ namespace argparse
{
case store_true:
case help:
case version:
m_value = false;
break;
case store_false:
Expand Down Expand Up @@ -1327,6 +1364,14 @@ namespace argparse

~ArgumentBuilder()
{
if (m_options.action == argparse::version)
{
if (m_options.help.empty())
{
m_options.help = "show program's version number and exit";
}
}

if (is_positional())
{
m_arguments.push_back(std::make_unique<PositionalArgument>(std::move(m_options)));
Expand All @@ -1343,6 +1388,12 @@ namespace argparse
return *this;
}

auto version(std::string const & version) -> ArgumentBuilder &
{
m_options.version = version;
return *this;
}

auto metavar(std::string const & metavar) -> ArgumentBuilder &
{
m_options.metavar = metavar;
Expand Down
1 change: 1 addition & 0 deletions test/unittest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ target_sources(unittest
test_usage_message.cpp
test_help_message.cpp
test_error_message.cpp
test_version.cpp
cstring_array.h
custom_a.h
custom_b.h)
Expand Down
21 changes: 21 additions & 0 deletions test/unittest/test_help_message.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,27 @@ TEST_CASE("Help message contains...")
CHECK(parser.format_help() == "usage: prog [-h]\n\noptional arguments:\n -h, --help show this help message and exit"s);
}

SUBCASE("...name and automatically added help for argument with version action")
{
parser.add_argument("-v").action(argparse::version);

CHECK(parser.format_help() == "usage: prog [-v]\n\noptional arguments:\n -v show program's version number and exit"s);
}

SUBCASE("...name, long name, and automatically added help for argument with version action")
{
parser.add_argument("-v", "--version").action(argparse::version);

CHECK(parser.format_help() == "usage: prog [-v]\n\noptional arguments:\n -v, --version show program's version number and exit"s);
}

SUBCASE("...name and help for argument with version action and help string")
{
parser.add_argument("-v").action(argparse::version).help("version1");

CHECK(parser.format_help() == "usage: prog [-v]\n\noptional arguments:\n -v version1"s);
}

SUBCASE("...name and automatic metavar")
{
parser.add_argument("-o");
Expand Down
20 changes: 20 additions & 0 deletions test/unittest/test_parsing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,26 @@ TEST_CASE("Parsing an optional argument with help action...")
}
}

TEST_CASE("Parsing an optional argument with version action yields false when it's missing")
{
auto parser = argparse::ArgumentParser().handle(argparse::Handle::none);
parser.add_argument("-v").action(argparse::version);

auto const parsed = parser.parse_args(1, cstr_arr{"prog"});

CHECK(parsed.get_value<bool>("v") == false);
}

TEST_CASE("Parsing an optional argument with version action yields true when it's present")
{
auto parser = argparse::ArgumentParser().handle(argparse::Handle::none);
parser.add_argument("-v").action(argparse::version);

auto const parsed = parser.parse_args(2, cstr_arr{"prog", "-v"});

CHECK(parsed.get_value<bool>("v") == true);
}

TEST_CASE("Optional argument can be used with either...")
{
auto parser = argparse::ArgumentParser();
Expand Down
17 changes: 17 additions & 0 deletions test/unittest/test_version.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include "argparse.h"

#include "doctest.h"

#include <string>


using namespace std::string_literals;

TEST_CASE("ArgumentParser provides program version")
{
auto parser = argparse::ArgumentParser();

parser.add_argument("-v").action(argparse::version).version("0.0.1");

CHECK(parser.format_version() == "0.0.1"s);
}