diff options
author | Brad King <brad.king@kitware.com> | 2022-06-28 19:49:48 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2022-07-28 12:24:47 (GMT) |
commit | 7ca8d9f0f854acd71f2a2134d86a1e182496c4cc (patch) | |
tree | d8d483043a1c825b2f560d723c8f1bb9274ca688 | |
parent | 110baa254bc2e5589e073affb8ea445e825d20eb (diff) | |
download | CMake-7ca8d9f0f854acd71f2a2134d86a1e182496c4cc.zip CMake-7ca8d9f0f854acd71f2a2134d86a1e182496c4cc.tar.gz CMake-7ca8d9f0f854acd71f2a2134d86a1e182496c4cc.tar.bz2 |
cmArgumentParser: Model non-empty strings with wrapper type
Some clients have been explicitly checking whether the string specified
after a keyword is empty. Offer them a way to specify that the string
must be non-empty as part of the binding type.
-rw-r--r-- | Source/cmArgumentParser.cxx | 14 | ||||
-rw-r--r-- | Source/cmArgumentParser.h | 1 | ||||
-rw-r--r-- | Source/cmArgumentParserTypes.h | 5 | ||||
-rw-r--r-- | Tests/CMakeLib/testArgumentParser.cxx | 16 |
4 files changed, 36 insertions, 0 deletions
diff --git a/Source/cmArgumentParser.cxx b/Source/cmArgumentParser.cxx index 05f8ce8..614d00d 100644 --- a/Source/cmArgumentParser.cxx +++ b/Source/cmArgumentParser.cxx @@ -76,6 +76,20 @@ void Instance::Bind(std::string& val) ExpectAtLeast{ 1 }); } +void Instance::Bind(NonEmpty<std::string>& val) +{ + this->Bind( + [this, &val](cm::string_view arg) -> Continue { + if (arg.empty() && this->ParseResults) { + this->ParseResults->AddKeywordError(this->Keyword, + " empty string not allowed\n"); + } + val.assign(std::string(arg)); + return Continue::No; + }, + ExpectAtLeast{ 1 }); +} + void Instance::Bind(Maybe<std::string>& val) { this->Bind( diff --git a/Source/cmArgumentParser.h b/Source/cmArgumentParser.h index c25e2f6..ac78872 100644 --- a/Source/cmArgumentParser.h +++ b/Source/cmArgumentParser.h @@ -171,6 +171,7 @@ public: void Bind(std::function<Continue(cm::string_view)> f, ExpectAtLeast expect); void Bind(bool& val); void Bind(std::string& val); + void Bind(NonEmpty<std::string>& val); void Bind(Maybe<std::string>& val); void Bind(MaybeEmpty<std::vector<std::string>>& val); void Bind(NonEmpty<std::vector<std::string>>& val); diff --git a/Source/cmArgumentParserTypes.h b/Source/cmArgumentParserTypes.h index fe8e4ca..7daae09 100644 --- a/Source/cmArgumentParserTypes.h +++ b/Source/cmArgumentParserTypes.h @@ -34,6 +34,11 @@ struct NonEmpty<std::vector<T>> : public std::vector<T> { using std::vector<T>::vector; }; +template <> +struct NonEmpty<std::string> : public std::string +{ + using std::string::basic_string; +}; } // namespace ArgumentParser diff --git a/Tests/CMakeLib/testArgumentParser.cxx b/Tests/CMakeLib/testArgumentParser.cxx index 496b873..58c0418 100644 --- a/Tests/CMakeLib/testArgumentParser.cxx +++ b/Tests/CMakeLib/testArgumentParser.cxx @@ -27,6 +27,8 @@ struct Result : public ArgumentParser::ParseResult cm::optional<std::string> String2; cm::optional<std::string> String3; ArgumentParser::Maybe<std::string> String4; + ArgumentParser::NonEmpty<std::string> String5; + ArgumentParser::NonEmpty<std::string> String6; ArgumentParser::NonEmpty<std::vector<std::string>> List1; ArgumentParser::NonEmpty<std::vector<std::string>> List2; @@ -87,6 +89,8 @@ struct Result : public ArgumentParser::ParseResult ArgumentParser::NonEmpty<std::vector<std::string>> UnboundNonEmpty{ 1, "unbound" }; + ArgumentParser::NonEmpty<std::string> UnboundNonEmptyStr{ 'u', 'n', 'b', 'o', + 'u', 'n', 'd' }; std::vector<cm::string_view> ParsedKeywords; }; @@ -100,6 +104,8 @@ std::initializer_list<cm::string_view> const args = { "STRING_2", "foo", "bar", // string arg + unparsed value, presence captured // "STRING_3", // string arg that is not present "STRING_4", // string arg allowed to be missing value + "STRING_5", "foo", // string arg that is not empty + "STRING_6", "", // string arg that is empty "LIST_1", // list arg missing values "LIST_2", "foo", "bar", // list arg with 2 elems "LIST_3", "bar", // list arg ... @@ -133,6 +139,8 @@ bool verifyResult(Result const& result, "STRING_1", "STRING_2", "STRING_4", + "STRING_5", + "STRING_6", "LIST_1", "LIST_2", "LIST_3", @@ -159,6 +167,7 @@ bool verifyResult(Result const& result, }; static std::map<cm::string_view, std::string> const keywordErrors = { { "STRING_1"_s, " missing required value\n" }, + { "STRING_6"_s, " empty string not allowed\n" }, { "LIST_1"_s, " missing required value\n" }, { "LIST_4"_s, " missing required value\n" }, { "FUNC_0"_s, " missing required value\n" } @@ -184,6 +193,8 @@ bool verifyResult(Result const& result, ASSERT_TRUE(*result.String2 == "foo"); ASSERT_TRUE(!result.String3); ASSERT_TRUE(result.String4.empty()); + ASSERT_TRUE(result.String5 == "foo"); + ASSERT_TRUE(result.String6.empty()); ASSERT_TRUE(result.List1.empty()); ASSERT_TRUE(result.List2 == foobar); @@ -217,6 +228,7 @@ bool verifyResult(Result const& result, ASSERT_TRUE(result.UnboundMaybe == "unbound"); ASSERT_TRUE(result.UnboundMaybeEmpty == unbound); ASSERT_TRUE(result.UnboundNonEmpty == unbound); + ASSERT_TRUE(result.UnboundNonEmptyStr == "unbound"); ASSERT_TRUE(result.ParsedKeywords == parsedKeywords); @@ -249,6 +261,8 @@ bool testArgumentParserDynamic() .Bind("STRING_2"_s, result.String2) .Bind("STRING_3"_s, result.String3) .Bind("STRING_4"_s, result.String4) + .Bind("STRING_5"_s, result.String5) + .Bind("STRING_6"_s, result.String6) .Bind("LIST_1"_s, result.List1) .Bind("LIST_2"_s, result.List2) .Bind("LIST_3"_s, result.List3) @@ -299,6 +313,8 @@ static auto const parserStatic = // .Bind("STRING_2"_s, &Result::String2) .Bind("STRING_3"_s, &Result::String3) .Bind("STRING_4"_s, &Result::String4) + .Bind("STRING_5"_s, &Result::String5) + .Bind("STRING_6"_s, &Result::String6) .Bind("LIST_1"_s, &Result::List1) .Bind("LIST_2"_s, &Result::List2) .Bind("LIST_3"_s, &Result::List3) |