diff --git a/include/argparse/argparse.hpp b/include/argparse/argparse.hpp index cdbcd19..cb427cd 100644 --- a/include/argparse/argparse.hpp +++ b/include/argparse/argparse.hpp @@ -472,7 +472,8 @@ public: } else if (mDefaultValue.has_value()) { return start; } else { - throw std::runtime_error("Too few arguments"); + throw std::runtime_error("Too few arguments for '" + + std::string(mUsedName) + "'."); } } @@ -759,7 +760,7 @@ private: if (mDefaultValue.has_value()) { return std::any_cast(mDefaultValue); } - throw std::logic_error("No value provided"); + throw std::logic_error("No value provided for '" + mNames.back() + "'."); } /* @@ -910,12 +911,16 @@ public: } /* Getter for options with default values. + * @throws std::logic_error if parse_args() has not been previously called * @throws std::logic_error if there is no such option * @throws std::logic_error if the option has no value * @throws std::bad_any_cast if the option is not of type T */ template T get(std::string_view aArgumentName) const { + if (!mIsParsed) { + throw std::logic_error("Nothing parsed, no arguments are available."); + } return (*this)[aArgumentName].get(); } @@ -1075,6 +1080,7 @@ private: throw std::runtime_error("Unknown argument"); } } + mIsParsed = true; } /* @@ -1114,6 +1120,7 @@ private: std::string mVersion; std::string mDescription; std::string mEpilog; + bool mIsParsed = false; std::list mPositionalArguments; std::list mOptionalArguments; std::map> mArgumentMap; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e601965..e8f7d89 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -30,6 +30,7 @@ file(GLOB ARGPARSE_TEST_SOURCES test_compound_arguments.cpp test_container_arguments.cpp test_const_correct.cpp + test_get.cpp test_help.cpp test_invalid_arguments.cpp test_is_used.cpp diff --git a/test/test_get.cpp b/test/test_get.cpp new file mode 100644 index 0000000..db316ba --- /dev/null +++ b/test/test_get.cpp @@ -0,0 +1,37 @@ +#include +#include + +using doctest::test_suite; + +TEST_CASE("Getting a simple argument" * test_suite("ArgumentParser::get")) { + argparse::ArgumentParser program("test"); + program.add_argument("-s", "--stuff"); + REQUIRE_NOTHROW(program.parse_args({ "test", "-s", "./src" })); + REQUIRE(program.get("--stuff") == "./src"); +} + +TEST_CASE("Skipped call to parse_args" * test_suite("ArgumentParser::get")) { + argparse::ArgumentParser program("test"); + program.add_argument("stuff"); + REQUIRE_THROWS_WITH_AS(program.get("stuff"), + "Nothing parsed, no arguments are available.", + std::logic_error); +} + +TEST_CASE("Missing argument" * test_suite("ArgumentParser::get")) { + argparse::ArgumentParser program("test"); + program.add_argument("-s", "--stuff"); + REQUIRE_NOTHROW(program.parse_args({ "test" })); + REQUIRE_THROWS_WITH_AS(program.get("--stuff"), + "No value provided for '--stuff'.", + std::logic_error); +} + +TEST_CASE("Implicit argument" * test_suite("ArgumentParser::get")) { + argparse::ArgumentParser program("test"); + program.add_argument("-s", "--stuff").nargs(1); + REQUIRE_NOTHROW(program.parse_args({ "test" })); + REQUIRE_THROWS_WITH_AS(program.get("--stuff"), + "No value provided for '--stuff'.", + std::logic_error); +} diff --git a/test/test_parse_args.cpp b/test/test_parse_args.cpp index a3a0452..146dfff 100644 --- a/test/test_parse_args.cpp +++ b/test/test_parse_args.cpp @@ -3,6 +3,14 @@ using doctest::test_suite; +TEST_CASE("Missing argument" * test_suite("parse_args")) { + argparse::ArgumentParser program("test"); + program.add_argument("--config").nargs(1); + REQUIRE_THROWS_WITH_AS(program.parse_args({ "test", "--config" }), + "Too few arguments for '--config'.", + std::runtime_error); +} + TEST_CASE("Parse a string argument with value" * test_suite("parse_args")) { argparse::ArgumentParser program("test"); program.add_argument("--config"); diff --git a/test/test_positional_arguments.cpp b/test/test_positional_arguments.cpp index f0d936c..4e2489b 100644 --- a/test/test_positional_arguments.cpp +++ b/test/test_positional_arguments.cpp @@ -13,6 +13,15 @@ TEST_CASE("Parse positional arguments" * test_suite("positional_arguments")) { REQUIRE(program.get("output") == "thrust_profile.csv"); } +TEST_CASE("Missing expected positional argument" * + test_suite("positional_arguments")) { + argparse::ArgumentParser program("test"); + program.add_argument("input"); + REQUIRE_THROWS_WITH_AS(program.parse_args({ "test" }), + "1 argument(s) expected. 0 provided.", + std::runtime_error); +} + TEST_CASE("Parse positional arguments with fixed nargs" * test_suite("positional_arguments")) { argparse::ArgumentParser program("test");