Mercurial > code
changeset 662:c13e190b2a74
options: allow '!' in format string to stop at first argument
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 16 Jul 2019 21:07:25 +0200 |
parents | a7ef5d868275 |
children | c3d758f1d640 |
files | cpp/options/options.hpp cpp/options/test/main.cpp |
diffstat | 2 files changed, 43 insertions(+), 5 deletions(-) [+] |
line wrap: on
line diff
--- a/cpp/options/options.hpp Tue Jul 16 20:31:36 2019 +0200 +++ b/cpp/options/options.hpp Tue Jul 16 21:07:25 2019 +0200 @@ -55,6 +55,12 @@ * If a -- option appears in the argument list, it stops option parsing and all * next tokens are considered arguments even if they start with an hyphen. * + * If the exlamation mark appears in the fmt argument, the function will stop + * parsing tokens immediately when one argument is not an option. + * + * This function explicitly takes references to it and end parameters to allow + * the user to determine the number of tokens actually parsed. + * * Example of format strings: * * - "abc": are all three boolean options, @@ -74,20 +80,29 @@ * \return the result */ template <typename InputIt> -inline auto parse(InputIt it, InputIt end, std::string_view fmt) -> pack +inline auto parse(InputIt&& it, InputIt&& end, std::string_view fmt) -> pack { pack result; for (; it != end; ++it) { const std::string_view token(*it); + /* + * Special token that stops parsing options, all next tokens + * will be considered as positional arguments. + */ if (token == "--") { for (++it; it != end; ++it) std::get<0>(result).push_back(std::string(*it)); break; } - if (token.compare(0, 1, "-") != 0) { + // Is this a positional argument? + if (token.compare(0U, 1U, "-") != 0) { + // Stop parsing in case of '!' in format string. + if (fmt.find('!') != std::string_view::npos) + break; + std::get<0>(result).push_back(std::string(token)); continue; } @@ -100,16 +115,22 @@ if (idx == std::string_view::npos) throw std::runtime_error("invalid option"); - if (fmt.compare(idx + 1U, 1, ":") != 0) { + // This is a boolean value. + if (fmt.compare(idx + 1U, 1U, ":") != 0) { std::get<1>(result).emplace(sub[i], ""); continue; } + /* + * The value is adjacent to the option (e.g. + * -csuper.conf). + */ if (idx + 1U < sub.size()) { std::get<1>(result).emplace(sub[i], std::string(sub.substr(i + 1))); break; } + // Option is the next token (e.g. -c super.conf). if (++it == end || std::string_view(*it).compare(0U, 1U, "-") == 0) throw std::runtime_error("option require a value"); @@ -131,7 +152,10 @@ template <typename String> inline auto parse(std::initializer_list<String> args, std::string_view fmt) -> pack { - return parse(args.begin(), args.end(), fmt); + auto begin = args.begin(); + auto end = args.end(); + + return parse(begin, end, fmt); } /** @@ -149,7 +173,10 @@ for (int i = 0; i < argc; ++i) args[i] = argv[i]; - return parse(args.begin(), args.end(), fmt); + auto begin = args.begin(); + auto end = args.end(); + + return parse(begin, end, fmt); }
--- a/cpp/options/test/main.cpp Tue Jul 16 20:31:36 2019 +0200 +++ b/cpp/options/test/main.cpp Tue Jul 16 21:07:25 2019 +0200 @@ -130,6 +130,17 @@ BOOST_TEST(options.find('f')->second.empty()); } +BOOST_AUTO_TEST_CASE(exclam) +{ + const auto [ args, options ] = options::parse({"-v", "install", "-p"}, "pv!"); + + BOOST_TEST(args.size() == 0U); + BOOST_TEST(options.size() == 1U); + BOOST_TEST(options.count('v') == 1U); + BOOST_TEST(options.find('v')->second.empty()); + BOOST_TEST(options.count('p') == 0U); +} + BOOST_AUTO_TEST_SUITE(errors) BOOST_AUTO_TEST_CASE(invalid_option)