From 8c1154cbcddf5dfcb278e3a821cb1d0a3a6c984e Mon Sep 17 00:00:00 2001 From: "Taylor C. Richberger" Date: Tue, 14 Jun 2016 19:35:48 -0600 Subject: add new feature --- args.hxx | 34 +++++++++++++++++++++++++++++----- test.cxx | 30 ++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/args.hxx b/args.hxx index ac9ccfc..26cb4f6 100644 --- a/args.hxx +++ b/args.hxx @@ -175,6 +175,15 @@ namespace args virtual ~MapError() {}; }; + /** Error that occurs when a singular flag is specified multiple times + */ + class ExtraError : public ParseError + { + public: + ExtraError(const std::string &problem) : ParseError(problem) {} + virtual ~ExtraError() {}; + }; + /** An exception that indicates that the user has requested help */ class Help : public Error @@ -393,11 +402,14 @@ namespace args */ class FlagBase : public NamedBase { + private: + const bool extraError; + protected: const Matcher matcher; public: - FlagBase(const std::string &name, const std::string &help, Matcher &&matcher) : NamedBase(name, help), matcher(std::move(matcher)) {} + FlagBase(const std::string &name, const std::string &help, Matcher &&matcher, const bool extraError = false) : NamedBase(name, help), extraError(extraError), matcher(std::move(matcher)) {} virtual ~FlagBase() {} @@ -405,6 +417,12 @@ namespace args { if (matcher.Match(flag)) { + if (extraError && matched) + { + std::ostringstream problem; + problem << "Flag '" << flag << "' was passed multiple times, but should only be allowed to be passed once"; + throw ExtraError(problem.str()); + } matched = true; return this; } @@ -415,6 +433,12 @@ namespace args { if (matcher.Match(flag)) { + if (extraError && matched) + { + std::ostringstream problem; + problem << "Flag '" << flag << "' was passed multiple times, but should only be allowed to be passed once"; + throw ExtraError(problem.str()); + } matched = true; return this; } @@ -445,7 +469,7 @@ namespace args class ValueFlagBase : public FlagBase { public: - ValueFlagBase(const std::string &name, const std::string &help, Matcher &&matcher) : FlagBase(name, help, std::move(matcher)) {} + ValueFlagBase(const std::string &name, const std::string &help, Matcher &&matcher, const bool extraError = false) : FlagBase(name, help, std::move(matcher), extraError) {} virtual ~ValueFlagBase() {} virtual void ParseValue(const std::string &value) = 0; @@ -1254,7 +1278,7 @@ namespace args class Flag : public FlagBase { public: - Flag(Group &group, const std::string &name, const std::string &help, Matcher &&matcher): FlagBase(name, help, std::move(matcher)) + Flag(Group &group, const std::string &name, const std::string &help, Matcher &&matcher, const bool extraError = false): FlagBase(name, help, std::move(matcher), extraError) { group.Add(*this); } @@ -1389,7 +1413,7 @@ namespace args public: - ValueFlag(Group &group, const std::string &name, const std::string &help, Matcher &&matcher, const T &defaultValue = T()): ValueFlagBase(name, help, std::move(matcher)), value(defaultValue) + ValueFlag(Group &group, const std::string &name, const std::string &help, Matcher &&matcher, const T &defaultValue = T(), const bool extraError = false): ValueFlagBase(name, help, std::move(matcher), extraError), value(defaultValue) { group.Add(*this); } @@ -1469,7 +1493,7 @@ namespace args public: - MapFlag(Group &group, const std::string &name, const std::string &help, Matcher &&matcher, const Map &map, const T &defaultValue = T()): ValueFlagBase(name, help, std::move(matcher)), map(map), value(defaultValue) + MapFlag(Group &group, const std::string &name, const std::string &help, Matcher &&matcher, const Map &map, const T &defaultValue = T(), const bool extraError = false): ValueFlagBase(name, help, std::move(matcher), extraError), map(map), value(defaultValue) { group.Add(*this); } diff --git a/test.cxx b/test.cxx index b4c451b..c368bf9 100644 --- a/test.cxx +++ b/test.cxx @@ -412,3 +412,33 @@ TEST_CASE("Mapping types work as needed", "[args]") REQUIRE((args::get(mpl) == std::vector{MappingEnum::red, MappingEnum::def})); REQUIRE_THROWS_AS(parser.ParseArgs(std::vector{"--mf=YeLLoW"}), args::MapError); } + +TEST_CASE("An exception should be thrown when a single-argument flag is matched multiple times and the constructor option is specified", "[args]") +{ + std::unordered_map map{ + {"default", MappingEnum::def}, + {"foo", MappingEnum::foo}, + {"bar", MappingEnum::bar}, + {"red", MappingEnum::red}, + {"yellow", MappingEnum::yellow}, + {"green", MappingEnum::green}}; + + std::ostream null(nullptr); + args::ArgumentParser parser("Test command"); + args::Flag foo(parser, "Foo", "Foo", {'f', "foo"}, true); + args::ValueFlag bar(parser, "Bar", "Bar", {'b', "bar"}, "", true); + args::Flag bix(parser, "Bix", "Bix", {'x', "bix"}); + args::MapFlag baz(parser, "Baz", "Baz", {'B', "baz"}, map, MappingEnum::def, true); + REQUIRE_THROWS_AS(parser.ParseArgs(std::vector{"--foo", "-f", "-bblah"}), args::ExtraError); + REQUIRE_NOTHROW(parser.ParseArgs(std::vector{"--foo", "-xxx", "--bix", "-bblah", "--bix"})); + REQUIRE_THROWS_AS(parser.ParseArgs(std::vector{"--foo", "-bblah", "-blah"}), args::ExtraError); + REQUIRE_THROWS_AS(parser.ParseArgs(std::vector{"--foo", "-bblah", "--bar", "blah"}), args::ExtraError); + REQUIRE_THROWS_AS(parser.ParseArgs(std::vector{"--baz=red", "-B", "yellow"}), args::ExtraError); + REQUIRE_THROWS_AS(parser.ParseArgs(std::vector{"--baz", "red", "-Byellow"}), args::ExtraError); + REQUIRE_NOTHROW(parser.ParseArgs(std::vector{"--foo", "-Bgreen"})); + REQUIRE(foo); + REQUIRE_FALSE(bar); + REQUIRE_FALSE(bix); + REQUIRE(baz); + REQUIRE(args::get(baz) == MappingEnum::green); +} -- cgit v1.2.1 From 520c27cd0db9b0ec00e4df30a2898a045687b776 Mon Sep 17 00:00:00 2001 From: "Taylor C. Richberger" Date: Tue, 14 Jun 2016 19:37:46 -0600 Subject: bump version number --- Doxyfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doxyfile b/Doxyfile index 51becfa..49b4c39 100644 --- a/Doxyfile +++ b/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = "args" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 4.1.5 +PROJECT_NUMBER = 4.2.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a -- cgit v1.2.1