From 2477df98fadc281e55fab806ee9c7f6f47f1e661 Mon Sep 17 00:00:00 2001 From: Pavel Belikov Date: Wed, 8 Nov 2017 22:25:08 +0300 Subject: add validation for commands --- args.hxx | 55 +++++++++++++++++++++++++++++++++++++++++++++++-------- test.cxx | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 8 deletions(-) diff --git a/args.hxx b/args.hxx index 4eff869..c290d6b 100644 --- a/args.hxx +++ b/args.hxx @@ -527,7 +527,7 @@ namespace args const std::string help; #ifdef ARGS_NOEXCEPT /// Only for ARGS_NOEXCEPT - Error error; + mutable Error error; #endif public: @@ -544,7 +544,7 @@ namespace args return matched; } - virtual void Validate(const std::string &, const std::string &) + virtual void Validate(const std::string &, const std::string &) const { } @@ -674,7 +674,7 @@ namespace args #ifndef ARGS_NOEXCEPT if (max < min) { - throw std::invalid_argument("Nargs: max > min"); + throw UsageError("Nargs: max > min"); } #endif } @@ -718,7 +718,7 @@ namespace args return nullptr; } - virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) override + virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override { if (!Matched() && (GetOptions() & Options::Required) != Options::None) { @@ -758,6 +758,20 @@ namespace args return true; } +#ifdef ARGS_NOEXCEPT + /// Only for ARGS_NOEXCEPT + virtual Error GetError() const override + { + const auto nargs = NumberOfArguments(); + if (nargs.min > nargs.max) + { + return Error::Usage; + } + + return error; + } +#endif + /** Defines how many values can be consumed by this option. * * \return closed interval [min, max] @@ -847,7 +861,7 @@ namespace args return { "[" + Name() + ']' }; } - virtual void Validate(const std::string &, const std::string &) override + virtual void Validate(const std::string &, const std::string &) const override { if ((GetOptions() & Options::Required) != Options::None && !Matched()) { @@ -962,7 +976,7 @@ namespace args return nullptr; } - virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) override + virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override { for (Base *child: Children()) { @@ -1207,6 +1221,7 @@ namespace args mutable std::vector subparserProgramLine; mutable bool subparserHasFlag = false; mutable bool subparserHasPositional = false; + mutable bool subparserHasCommand = false; mutable Subparser *subparser = nullptr; protected: @@ -1431,7 +1446,7 @@ namespace args auto res = Group::GetProgramLine(params); res.insert(res.end(), subparserProgramLine.begin(), subparserProgramLine.end()); - if (!params.proglineCommand.empty() && Group::HasCommand()) + if (!params.proglineCommand.empty() && (Group::HasCommand() || subparserHasCommand)) { res.insert(res.begin(), commandIsRequired ? params.proglineCommand : "[" + params.proglineCommand + "]"); } @@ -1577,8 +1592,13 @@ namespace args return descriptions; } - virtual void Validate(const std::string &shortprefix, const std::string &longprefix) override + virtual void Validate(const std::string &shortprefix, const std::string &longprefix) const override { + if (!Matched()) + { + return; + } + for (Base *child: Children()) { if (child->IsGroup() && !child->Matched()) @@ -1594,6 +1614,22 @@ namespace args child->Validate(shortprefix, longprefix); } + + if (subparser != nullptr) + { + subparser->Validate(shortprefix, longprefix); + } + + if (selectedCommand == nullptr && commandIsRequired && (Group::HasCommand() || subparserHasCommand)) + { +#ifdef ARGS_NOEXCEPT + error = Error::Validation; +#else + std::ostringstream problem; + problem << "Command is required"; + throw ValidationError(problem.str()); +#endif + } } virtual void Reset() noexcept override @@ -1604,6 +1640,7 @@ namespace args subparserDescription.clear(); subparserHasFlag = false; subparserHasPositional = false; + subparserHasCommand = false; } }; @@ -2207,6 +2244,7 @@ namespace args command.subparserDescription = GetDescription(helpParams, 0); command.subparserHasFlag = HasFlag(); command.subparserHasPositional = HasPositional(); + command.subparserHasCommand = HasCommand(); command.subparserProgramLine = GetProgramLine(helpParams); if (parser == nullptr) { @@ -2219,6 +2257,7 @@ namespace args } auto it = parser->Parse(args.begin(), args.end()); + command.Validate(parser->ShortPrefix(), parser->LongPrefix()); kicked.assign(it, args.end()); } diff --git a/test.cxx b/test.cxx index 135d1ee..7f92f26 100644 --- a/test.cxx +++ b/test.cxx @@ -857,6 +857,46 @@ TEST_CASE("Subparser help works as expected", "[args]") } +TEST_CASE("Subparser validation works as expected", "[args]") +{ + args::ArgumentParser p("parser"); + args::Command a(p, "a", "command a", [](args::Subparser &s) + { + args::ValueFlag f(s, "", "", {'f'}, args::Options::Required); + s.Parse(); + }); + + args::Command b(p, "b", "command b"); + args::ValueFlag f(b, "", "", {'f'}, args::Options::Required); + + REQUIRE_NOTHROW(p.ParseArgs(std::vector{})); + REQUIRE_THROWS_AS(p.ParseArgs(std::vector{"a"}), args::RequiredError); + REQUIRE_NOTHROW(p.ParseArgs(std::vector{"a", "-f", "F"})); + REQUIRE_THROWS_AS(p.ParseArgs(std::vector{"b"}), args::RequiredError); + REQUIRE_NOTHROW(p.ParseArgs(std::vector{"b", "-f", "F"})); + + p.RequireCommand(true); + REQUIRE_THROWS_AS(p.ParseArgs(std::vector{}), args::ValidationError); + + p.RequireCommand(false); + REQUIRE_NOTHROW(p.ParseArgs(std::vector{})); +} + +TEST_CASE("Global options work as expected", "[args]") +{ + args::Group globals; + args::Flag f(globals, "f", "f", {'f'}); + + args::ArgumentParser p("parser"); + args::GlobalOptions g(p, globals); + args::Command a(p, "a", "command a"); + args::Command b(p, "b", "command b"); + + REQUIRE_NOTHROW(p.ParseArgs(std::vector{"-f"})); + REQUIRE_NOTHROW(p.ParseArgs(std::vector{"a", "-f"})); + REQUIRE_NOTHROW(p.ParseArgs(std::vector{"b", "-f"})); +} + #undef ARGS_HXX #define ARGS_TESTNAMESPACE #define ARGS_NOEXCEPT -- cgit v1.2.1