| /* Copyright (c) 2016-2017 Taylor C. Richberger <taywee@gmx.com> and Pavel | |
| * Belikov | |
| * | |
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| * of this software and associated documentation files (the "Software"), to | |
| * deal in the Software without restriction, including without limitation the | |
| * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |
| * sell copies of the Software, and to permit persons to whom the Software is | |
| * furnished to do so, subject to the following conditions: | |
| * | |
| * The above copyright notice and this permission notice shall be included in | |
| * all copies or substantial portions of the Software. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
| * IN THE SOFTWARE. | |
| */ | |
| /** \file args.hxx | |
| * \brief this single-header lets you use all of the args functionality | |
| * | |
| * The important stuff is done inside the args namespace | |
| */ | |
| #ifndef ARGS_HXX | |
| #define ARGS_HXX | |
| #include <algorithm> | |
| #include <iterator> | |
| #include <exception> | |
| #include <functional> | |
| #include <sstream> | |
| #include <string> | |
| #include <tuple> | |
| #include <vector> | |
| #include <unordered_map> | |
| #include <unordered_set> | |
| #include <type_traits> | |
| #ifdef ARGS_TESTNAMESPACE | |
| namespace argstest | |
| { | |
| #else | |
| /** \namespace args | |
| * \brief contains all the functionality of the args library | |
| */ | |
| namespace args | |
| { | |
| #endif | |
| /** Getter to grab the value from the argument type. | |
| * | |
| * If the Get() function of the type returns a reference, so does this, and | |
| * the value will be modifiable. | |
| */ | |
| template <typename Option> | |
| auto get(Option &option_) -> decltype(option_.Get()) | |
| { | |
| return option_.Get(); | |
| } | |
| /** (INTERNAL) Count UTF-8 glyphs | |
| * | |
| * This is not reliable, and will fail for combinatory glyphs, but it's | |
| * good enough here for now. | |
| * | |
| * \param string The string to count glyphs from | |
| * \return The UTF-8 glyphs in the string | |
| */ | |
| inline std::string::size_type Glyphs(const std::string &string_) | |
| { | |
| std::string::size_type length = 0; | |
| for (const char c: string_) | |
| { | |
| if ((c & 0xc0) != 0x80) | |
| { | |
| ++length; | |
| } | |
| } | |
| return length; | |
| } | |
| /** (INTERNAL) Wrap a vector of words into a vector of lines | |
| * | |
| * Empty words are skipped. Word "\n" forces wrapping. | |
| * | |
| * \param begin The begin iterator | |
| * \param end The end iterator | |
| * \param width The width of the body | |
| * \param firstlinewidth the width of the first line, defaults to the width of the body | |
| * \param firstlineindent the indent of the first line, defaults to 0 | |
| * \return the vector of lines | |
| */ | |
| template <typename It> | |
| inline std::vector<std::string> Wrap(It begin, | |
| It end, | |
| const std::string::size_type width, | |
| std::string::size_type firstlinewidth = 0, | |
| std::string::size_type firstlineindent = 0) | |
| { | |
| std::vector<std::string> output; | |
| std::string line(firstlineindent, ' '); | |
| bool empty = true; | |
| if (firstlinewidth == 0) | |
| { | |
| firstlinewidth = width; | |
| } | |
| auto currentwidth = firstlinewidth; | |
| for (auto it = begin; it != end; ++it) | |
| { | |
| if (it->empty()) | |
| { | |
| continue; | |
| } | |
| if (*it == "\n") | |
| { | |
| if (!empty) | |
| { | |
| output.push_back(line); | |
| line.clear(); | |
| empty = true; | |
| currentwidth = width; | |
| } | |
| continue; | |
| } | |
| auto itemsize = Glyphs(*it); | |
| if ((line.length() + 1 + itemsize) > currentwidth) | |
| { | |
| if (!empty) | |
| { | |
| output.push_back(line); | |
| line.clear(); | |
| empty = true; | |
| currentwidth = width; | |
| } | |
| } | |
| if (itemsize > 0) | |
| { | |
| if (!empty) | |
| { | |
| line += ' '; | |
| } | |
| line += *it; | |
| empty = false; | |
| } | |
| } | |
| if (!empty) | |
| { | |
| output.push_back(line); | |
| } | |
| return output; | |
| } | |
| namespace detail | |
| { | |
| template <typename T> | |
| std::string Join(const T& array, const std::string &delimiter) | |
| { | |
| std::string res; | |
| for (auto &element : array) | |
| { | |
| if (!res.empty()) | |
| { | |
| res += delimiter; | |
| } | |
| res += element; | |
| } | |
| return res; | |
| } | |
| } | |
| /** (INTERNAL) Wrap a string into a vector of lines | |
| * | |
| * This is quick and hacky, but works well enough. You can specify a | |
| * different width for the first line | |
| * | |
| * \param width The width of the body | |
| * \param firstlinewid the width of the first line, defaults to the width of the body | |
| * \return the vector of lines | |
| */ | |
| inline std::vector<std::string> Wrap(const std::string &in, const std::string::size_type width, std::string::size_type firstlinewidth = 0) | |
| { | |
| // Preserve existing line breaks | |
| const auto newlineloc = in.find('\n'); | |
| if (newlineloc != in.npos) | |
| { | |
| auto first = Wrap(std::string(in, 0, newlineloc), width); | |
| auto second = Wrap(std::string(in, newlineloc + 1), width); | |
| first.insert( | |
| std::end(first), | |
| std::make_move_iterator(std::begin(second)), | |
| std::make_move_iterator(std::end(second))); | |
| return first; | |
| } | |
| std::istringstream stream(in); | |
| std::string::size_type indent = 0; | |
| for (char c : in) | |
| { | |
| if (!isspace(c)) | |
| { | |
| break; | |
| } | |
| ++indent; | |
| } | |
| return Wrap(std::istream_iterator<std::string>(stream), std::istream_iterator<std::string>(), | |
| width, firstlinewidth, indent); | |
| } | |
| #ifdef ARGS_NOEXCEPT | |
| /// Error class, for when ARGS_NOEXCEPT is defined | |
| enum class Error | |
| { | |
| None, | |
| Usage, | |
| Parse, | |
| Validation, | |
| Required, | |
| Map, | |
| Extra, | |
| Help, | |
| Subparser, | |
| Completion, | |
| }; | |
| #else | |
| /** Base error class | |
| */ | |
| class Error : public std::runtime_error | |
| { | |
| public: | |
| Error(const std::string &problem) : std::runtime_error(problem) {} | |
| virtual ~Error() {} | |
| }; | |
| /** Errors that occur during usage | |
| */ | |
| class UsageError : public Error | |
| { | |
| public: | |
| UsageError(const std::string &problem) : Error(problem) {} | |
| virtual ~UsageError() {} | |
| }; | |
| /** Errors that occur during regular parsing | |
| */ | |
| class ParseError : public Error | |
| { | |
| public: | |
| ParseError(const std::string &problem) : Error(problem) {} | |
| virtual ~ParseError() {} | |
| }; | |
| /** Errors that are detected from group validation after parsing finishes | |
| */ | |
| class ValidationError : public Error | |
| { | |
| public: | |
| ValidationError(const std::string &problem) : Error(problem) {} | |
| virtual ~ValidationError() {} | |
| }; | |
| /** Errors that when a required flag is omitted | |
| */ | |
| class RequiredError : public ValidationError | |
| { | |
| public: | |
| RequiredError(const std::string &problem) : ValidationError(problem) {} | |
| virtual ~RequiredError() {} | |
| }; | |
| /** Errors in map lookups | |
| */ | |
| class MapError : public ParseError | |
| { | |
| public: | |
| MapError(const std::string &problem) : ParseError(problem) {} | |
| 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 | |
| { | |
| public: | |
| Help(const std::string &flag) : Error(flag) {} | |
| virtual ~Help() {} | |
| }; | |
| /** (INTERNAL) An exception that emulates coroutine-like control flow for subparsers. | |
| */ | |
| class SubparserError : public Error | |
| { | |
| public: | |
| SubparserError() : Error("") {} | |
| virtual ~SubparserError() {} | |
| }; | |
| /** An exception that contains autocompletion reply | |
| */ | |
| class Completion : public Error | |
| { | |
| public: | |
| Completion(const std::string &flag) : Error(flag) {} | |
| virtual ~Completion() {} | |
| }; | |
| #endif | |
| /** A simple unified option type for unified initializer lists for the Matcher class. | |
| */ | |
| struct EitherFlag | |
| { | |
| const bool isShort; | |
| const char shortFlag; | |
| const std::string longFlag; | |
| EitherFlag(const std::string &flag) : isShort(false), shortFlag(), longFlag(flag) {} | |
| EitherFlag(const char *flag) : isShort(false), shortFlag(), longFlag(flag) {} | |
| EitherFlag(const char flag) : isShort(true), shortFlag(flag), longFlag() {} | |
| /** Get just the long flags from an initializer list of EitherFlags | |
| */ | |
| static std::unordered_set<std::string> GetLong(std::initializer_list<EitherFlag> flags) | |
| { | |
| std::unordered_set<std::string> longFlags; | |
| for (const EitherFlag &flag: flags) | |
| { | |
| if (!flag.isShort) | |
| { | |
| longFlags.insert(flag.longFlag); | |
| } | |
| } | |
| return longFlags; | |
| } | |
| /** Get just the short flags from an initializer list of EitherFlags | |
| */ | |
| static std::unordered_set<char> GetShort(std::initializer_list<EitherFlag> flags) | |
| { | |
| std::unordered_set<char> shortFlags; | |
| for (const EitherFlag &flag: flags) | |
| { | |
| if (flag.isShort) | |
| { | |
| shortFlags.insert(flag.shortFlag); | |
| } | |
| } | |
| return shortFlags; | |
| } | |
| std::string str() const | |
| { | |
| return isShort ? std::string(1, shortFlag) : longFlag; | |
| } | |
| std::string str(const std::string &shortPrefix, const std::string &longPrefix) const | |
| { | |
| return isShort ? shortPrefix + std::string(1, shortFlag) : longPrefix + longFlag; | |
| } | |
| }; | |
| /** A class of "matchers", specifying short and flags that can possibly be | |
| * matched. | |
| * | |
| * This is supposed to be constructed and then passed in, not used directly | |
| * from user code. | |
| */ | |
| class Matcher | |
| { | |
| private: | |
| const std::unordered_set<char> shortFlags; | |
| const std::unordered_set<std::string> longFlags; | |
| public: | |
| /** Specify short and long flags separately as iterators | |
| * | |
| * ex: `args::Matcher(shortFlags.begin(), shortFlags.end(), longFlags.begin(), longFlags.end())` | |
| */ | |
| template <typename ShortIt, typename LongIt> | |
| Matcher(ShortIt shortFlagsStart, ShortIt shortFlagsEnd, LongIt longFlagsStart, LongIt longFlagsEnd) : | |
| shortFlags(shortFlagsStart, shortFlagsEnd), | |
| longFlags(longFlagsStart, longFlagsEnd) | |
| { | |
| if (shortFlags.empty() && longFlags.empty()) | |
| { | |
| #ifndef ARGS_NOEXCEPT | |
| throw UsageError("empty Matcher"); | |
| #endif | |
| } | |
| } | |
| #ifdef ARGS_NOEXCEPT | |
| /// Only for ARGS_NOEXCEPT | |
| Error GetError() const noexcept | |
| { | |
| return shortFlags.empty() && longFlags.empty() ? Error::Usage : Error::None; | |
| } | |
| #endif | |
| /** Specify short and long flags separately as iterables | |
| * | |
| * ex: `args::Matcher(shortFlags, longFlags)` | |
| */ | |
| template <typename Short, typename Long> | |
| Matcher(Short &&shortIn, Long &&longIn) : | |
| Matcher(std::begin(shortIn), std::end(shortIn), std::begin(longIn), std::end(longIn)) | |
| {} | |
| /** Specify a mixed single initializer-list of both short and long flags | |
| * | |
| * This is the fancy one. It takes a single initializer list of | |
| * any number of any mixed kinds of flags. Chars are | |
| * automatically interpreted as short flags, and strings are | |
| * automatically interpreted as long flags: | |
| * | |
| * args::Matcher{'a'} | |
| * args::Matcher{"foo"} | |
| * args::Matcher{'h', "help"} | |
| * args::Matcher{"foo", 'f', 'F', "FoO"} | |
| */ | |
| Matcher(std::initializer_list<EitherFlag> in) : | |
| Matcher(EitherFlag::GetShort(in), EitherFlag::GetLong(in)) {} | |
| Matcher(Matcher &&other) : shortFlags(std::move(other.shortFlags)), longFlags(std::move(other.longFlags)) | |
| {} | |
| ~Matcher() {} | |
| /** (INTERNAL) Check if there is a match of a short flag | |
| */ | |
| bool Match(const char flag) const | |
| { | |
| return shortFlags.find(flag) != shortFlags.end(); | |
| } | |
| /** (INTERNAL) Check if there is a match of a long flag | |
| */ | |
| bool Match(const std::string &flag) const | |
| { | |
| return longFlags.find(flag) != longFlags.end(); | |
| } | |
| /** (INTERNAL) Check if there is a match of a flag | |
| */ | |
| bool Match(const EitherFlag &flag) const | |
| { | |
| return flag.isShort ? Match(flag.shortFlag) : Match(flag.longFlag); | |
| } | |
| /** (INTERNAL) Get all flag strings as a vector, with the prefixes embedded | |
| */ | |
| std::vector<EitherFlag> GetFlagStrings() const | |
| { | |
| std::vector<EitherFlag> flagStrings; | |
| flagStrings.reserve(shortFlags.size() + longFlags.size()); | |
| for (const char flag: shortFlags) | |
| { | |
| flagStrings.emplace_back(flag); | |
| } | |
| for (const std::string &flag: longFlags) | |
| { | |
| flagStrings.emplace_back(flag); | |
| } | |
| return flagStrings; | |
| } | |
| /** (INTERNAL) Get long flag if it exists or any short flag | |
| */ | |
| EitherFlag GetLongOrAny() const | |
| { | |
| if (!longFlags.empty()) | |
| { | |
| return *longFlags.begin(); | |
| } | |
| if (!shortFlags.empty()) | |
| { | |
| return *shortFlags.begin(); | |
| } | |
| // should be unreachable | |
| return ' '; | |
| } | |
| /** (INTERNAL) Get short flag if it exists or any long flag | |
| */ | |
| EitherFlag GetShortOrAny() const | |
| { | |
| if (!shortFlags.empty()) | |
| { | |
| return *shortFlags.begin(); | |
| } | |
| if (!longFlags.empty()) | |
| { | |
| return *longFlags.begin(); | |
| } | |
| // should be unreachable | |
| return ' '; | |
| } | |
| }; | |
| /** Attributes for flags. | |
| */ | |
| enum class Options | |
| { | |
| /** Default options. | |
| */ | |
| None = 0x0, | |
| /** Flag can't be passed multiple times. | |
| */ | |
| Single = 0x01, | |
| /** Flag can't be omitted. | |
| */ | |
| Required = 0x02, | |
| /** Flag is excluded from usage line. | |
| */ | |
| HiddenFromUsage = 0x04, | |
| /** Flag is excluded from options help. | |
| */ | |
| HiddenFromDescription = 0x08, | |
| /** Flag is global and can be used in any subcommand. | |
| */ | |
| Global = 0x10, | |
| /** Flag stops a parser. | |
| */ | |
| KickOut = 0x20, | |
| /** Flag is excluded from auto completion. | |
| */ | |
| HiddenFromCompletion = 0x40, | |
| /** Flag is excluded from options help and usage line | |
| */ | |
| Hidden = HiddenFromUsage | HiddenFromDescription | HiddenFromCompletion, | |
| }; | |
| inline Options operator | (Options lhs, Options rhs) | |
| { | |
| return static_cast<Options>(static_cast<int>(lhs) | static_cast<int>(rhs)); | |
| } | |
| inline Options operator & (Options lhs, Options rhs) | |
| { | |
| return static_cast<Options>(static_cast<int>(lhs) & static_cast<int>(rhs)); | |
| } | |
| class FlagBase; | |
| class PositionalBase; | |
| class Command; | |
| class ArgumentParser; | |
| /** A simple structure of parameters for easy user-modifyable help menus | |
| */ | |
| struct HelpParams | |
| { | |
| /** The width of the help menu | |
| */ | |
| unsigned int width = 80; | |
| /** The indent of the program line | |
| */ | |
| unsigned int progindent = 2; | |
| /** The indent of the program trailing lines for long parameters | |
| */ | |
| unsigned int progtailindent = 4; | |
| /** The indent of the description and epilogs | |
| */ | |
| unsigned int descriptionindent = 4; | |
| /** The indent of the flags | |
| */ | |
| unsigned int flagindent = 6; | |
| /** The indent of the flag descriptions | |
| */ | |
| unsigned int helpindent = 40; | |
| /** The additional indent each group adds | |
| */ | |
| unsigned int eachgroupindent = 2; | |
| /** The minimum gutter between each flag and its help | |
| */ | |
| unsigned int gutter = 1; | |
| /** Show the terminator when both options and positional parameters are present | |
| */ | |
| bool showTerminator = true; | |
| /** Show the {OPTIONS} on the prog line when this is true | |
| */ | |
| bool showProglineOptions = true; | |
| /** Show the positionals on the prog line when this is true | |
| */ | |
| bool showProglinePositionals = true; | |
| /** The prefix for short flags | |
| */ | |
| std::string shortPrefix; | |
| /** The prefix for long flags | |
| */ | |
| std::string longPrefix; | |
| /** The separator for short flags | |
| */ | |
| std::string shortSeparator; | |
| /** The separator for long flags | |
| */ | |
| std::string longSeparator; | |
| /** The program name for help generation | |
| */ | |
| std::string programName; | |
| /** Show command's flags | |
| */ | |
| bool showCommandChildren = false; | |
| /** Show command's descriptions and epilog | |
| */ | |
| bool showCommandFullHelp = false; | |
| /** The postfix for progline when showProglineOptions is true and command has any flags | |
| */ | |
| std::string proglineOptions = "{OPTIONS}"; | |
| /** The prefix for progline when command has any subcommands | |
| */ | |
| std::string proglineCommand = "COMMAND"; | |
| /** The prefix for progline value | |
| */ | |
| std::string proglineValueOpen = " <"; | |
| /** The postfix for progline value | |
| */ | |
| std::string proglineValueClose = ">"; | |
| /** The prefix for progline required argument | |
| */ | |
| std::string proglineRequiredOpen = ""; | |
| /** The postfix for progline required argument | |
| */ | |
| std::string proglineRequiredClose = ""; | |
| /** The prefix for progline non-required argument | |
| */ | |
| std::string proglineNonrequiredOpen = "["; | |
| /** The postfix for progline non-required argument | |
| */ | |
| std::string proglineNonrequiredClose = "]"; | |
| /** Show flags in program line | |
| */ | |
| bool proglineShowFlags = false; | |
| /** Use short flags in program lines when possible | |
| */ | |
| bool proglinePreferShortFlags = false; | |
| /** Program line prefix | |
| */ | |
| std::string usageString; | |
| /** String shown in help before flags descriptions | |
| */ | |
| std::string optionsString = "OPTIONS:"; | |
| /** Display value name after all the long and short flags | |
| */ | |
| bool useValueNameOnce = false; | |
| /** Show value name | |
| */ | |
| bool showValueName = true; | |
| /** Add newline before flag description | |
| */ | |
| bool addNewlineBeforeDescription = false; | |
| /** The prefix for option value | |
| */ | |
| std::string valueOpen = "["; | |
| /** The postfix for option value | |
| */ | |
| std::string valueClose = "]"; | |
| /** Add choices to argument description | |
| */ | |
| bool addChoices = false; | |
| /** The prefix for choices | |
| */ | |
| std::string choiceString = "\nOne of: "; | |
| /** Add default values to argument description | |
| */ | |
| bool addDefault = false; | |
| /** The prefix for default values | |
| */ | |
| std::string defaultString = "\nDefault: "; | |
| }; | |
| /** A number of arguments which can be consumed by an option. | |
| * | |
| * Represents a closed interval [min, max]. | |
| */ | |
| struct Nargs | |
| { | |
| const size_t min; | |
| const size_t max; | |
| Nargs(size_t min_, size_t max_) : min{min_}, max{max_} | |
| { | |
| #ifndef ARGS_NOEXCEPT | |
| if (max < min) | |
| { | |
| throw UsageError("Nargs: max > min"); | |
| } | |
| #endif | |
| } | |
| Nargs(size_t num_) : min{num_}, max{num_} | |
| { | |
| } | |
| friend bool operator == (const Nargs &lhs, const Nargs &rhs) | |
| { | |
| return lhs.min == rhs.min && lhs.max == rhs.max; | |
| } | |
| friend bool operator != (const Nargs &lhs, const Nargs &rhs) | |
| { | |
| return !(lhs == rhs); | |
| } | |
| }; | |
| /** Base class for all match types | |
| */ | |
| class Base | |
| { | |
| private: | |
| Options options = {}; | |
| protected: | |
| bool matched = false; | |
| const std::string help; | |
| #ifdef ARGS_NOEXCEPT | |
| /// Only for ARGS_NOEXCEPT | |
| mutable Error error = Error::None; | |
| mutable std::string errorMsg; | |
| #endif | |
| public: | |
| Base(const std::string &help_, Options options_ = {}) : options(options_), help(help_) {} | |
| virtual ~Base() {} | |
| Options GetOptions() const noexcept | |
| { | |
| return options; | |
| } | |
| bool IsRequired() const noexcept | |
| { | |
| return (GetOptions() & Options::Required) != Options::None; | |
| } | |
| virtual bool Matched() const noexcept | |
| { | |
| return matched; | |
| } | |
| virtual void Validate(const std::string &, const std::string &) const | |
| { | |
| } | |
| operator bool() const noexcept | |
| { | |
| return Matched(); | |
| } | |
| virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &, const unsigned indentLevel) const | |
| { | |
| std::tuple<std::string, std::string, unsigned> description; | |
| std::get<1>(description) = help; | |
| std::get<2>(description) = indentLevel; | |
| return { std::move(description) }; | |
| } | |
| virtual std::vector<Command*> GetCommands() | |
| { | |
| return {}; | |
| } | |
| virtual bool IsGroup() const | |
| { | |
| return false; | |
| } | |
| virtual FlagBase *Match(const EitherFlag &) | |
| { | |
| return nullptr; | |
| } | |
| virtual PositionalBase *GetNextPositional() | |
| { | |
| return nullptr; | |
| } | |
| virtual std::vector<FlagBase*> GetAllFlags() | |
| { | |
| return {}; | |
| } | |
| virtual bool HasFlag() const | |
| { | |
| return false; | |
| } | |
| virtual bool HasPositional() const | |
| { | |
| return false; | |
| } | |
| virtual bool HasCommand() const | |
| { | |
| return false; | |
| } | |
| virtual std::vector<std::string> GetProgramLine(const HelpParams &) const | |
| { | |
| return {}; | |
| } | |
| /// Sets a kick-out value for building subparsers | |
| void KickOut(bool kickout_) noexcept | |
| { | |
| if (kickout_) | |
| { | |
| options = options | Options::KickOut; | |
| } | |
| else | |
| { | |
| options = static_cast<Options>(static_cast<int>(options) & ~static_cast<int>(Options::KickOut)); | |
| } | |
| } | |
| /// Gets the kick-out value for building subparsers | |
| bool KickOut() const noexcept | |
| { | |
| return (options & Options::KickOut) != Options::None; | |
| } | |
| virtual void Reset() noexcept | |
| { | |
| matched = false; | |
| #ifdef ARGS_NOEXCEPT | |
| error = Error::None; | |
| errorMsg.clear(); | |
| #endif | |
| } | |
| #ifdef ARGS_NOEXCEPT | |
| /// Only for ARGS_NOEXCEPT | |
| virtual Error GetError() const | |
| { | |
| return error; | |
| } | |
| /// Only for ARGS_NOEXCEPT | |
| std::string GetErrorMsg() const | |
| { | |
| return errorMsg; | |
| } | |
| #endif | |
| }; | |
| /** Base class for all match types that have a name | |
| */ | |
| class NamedBase : public Base | |
| { | |
| protected: | |
| const std::string name; | |
| bool kickout = false; | |
| std::string defaultString; | |
| bool defaultStringManual = false; | |
| std::vector<std::string> choicesStrings; | |
| bool choicesStringManual = false; | |
| virtual std::string GetDefaultString(const HelpParams&) const { return {}; } | |
| virtual std::vector<std::string> GetChoicesStrings(const HelpParams&) const { return {}; } | |
| virtual std::string GetNameString(const HelpParams&) const { return Name(); } | |
| void AddDescriptionPostfix(std::string &dest, const bool isManual, const std::string &manual, bool isGenerated, const std::string &generated, const std::string &str) const | |
| { | |
| if (isManual && !manual.empty()) | |
| { | |
| dest += str; | |
| dest += manual; | |
| } | |
| else if (!isManual && isGenerated && !generated.empty()) | |
| { | |
| dest += str; | |
| dest += generated; | |
| } | |
| } | |
| public: | |
| NamedBase(const std::string &name_, const std::string &help_, Options options_ = {}) : Base(help_, options_), name(name_) {} | |
| virtual ~NamedBase() {} | |
| /** Sets default value string that will be added to argument description. | |
| * Use empty string to disable it for this argument. | |
| */ | |
| void HelpDefault(const std::string &str) | |
| { | |
| defaultStringManual = true; | |
| defaultString = str; | |
| } | |
| /** Gets default value string that will be added to argument description. | |
| */ | |
| std::string HelpDefault(const HelpParams ¶ms) const | |
| { | |
| return defaultStringManual ? defaultString : GetDefaultString(params); | |
| } | |
| /** Sets choices strings that will be added to argument description. | |
| * Use empty vector to disable it for this argument. | |
| */ | |
| void HelpChoices(const std::vector<std::string> &array) | |
| { | |
| choicesStringManual = true; | |
| choicesStrings = array; | |
| } | |
| /** Gets choices strings that will be added to argument description. | |
| */ | |
| std::vector<std::string> HelpChoices(const HelpParams ¶ms) const | |
| { | |
| return choicesStringManual ? choicesStrings : GetChoicesStrings(params); | |
| } | |
| virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams ¶ms, const unsigned indentLevel) const override | |
| { | |
| std::tuple<std::string, std::string, unsigned> description; | |
| std::get<0>(description) = GetNameString(params); | |
| std::get<1>(description) = help; | |
| std::get<2>(description) = indentLevel; | |
| AddDescriptionPostfix(std::get<1>(description), choicesStringManual, detail::Join(choicesStrings, ", "), params.addChoices, detail::Join(GetChoicesStrings(params), ", "), params.choiceString); | |
| AddDescriptionPostfix(std::get<1>(description), defaultStringManual, defaultString, params.addDefault, GetDefaultString(params), params.defaultString); | |
| return { std::move(description) }; | |
| } | |
| virtual std::string Name() const | |
| { | |
| return name; | |
| } | |
| }; | |
| namespace detail | |
| { | |
| template <typename T, typename = int> | |
| struct IsConvertableToString : std::false_type {}; | |
| template <typename T> | |
| struct IsConvertableToString<T, decltype(std::declval<std::ostringstream&>() << std::declval<T>(), int())> : std::true_type {}; | |
| template <typename T> | |
| typename std::enable_if<IsConvertableToString<T>::value, std::string>::type | |
| ToString(const T &value) | |
| { | |
| std::ostringstream s; | |
| s << value; | |
| return s.str(); | |
| } | |
| template <typename T> | |
| typename std::enable_if<!IsConvertableToString<T>::value, std::string>::type | |
| ToString(const T &) | |
| { | |
| return {}; | |
| } | |
| template <typename T> | |
| std::vector<std::string> MapKeysToStrings(const T &map) | |
| { | |
| std::vector<std::string> res; | |
| using K = typename std::decay<decltype(std::begin(map)->first)>::type; | |
| if (IsConvertableToString<K>::value) | |
| { | |
| for (const auto &p : map) | |
| { | |
| res.push_back(detail::ToString(p.first)); | |
| } | |
| std::sort(res.begin(), res.end()); | |
| } | |
| return res; | |
| } | |
| } | |
| /** Base class for all flag options | |
| */ | |
| class FlagBase : public NamedBase | |
| { | |
| protected: | |
| const Matcher matcher; | |
| virtual std::string GetNameString(const HelpParams ¶ms) const override | |
| { | |
| const std::string postfix = !params.showValueName || NumberOfArguments() == 0 ? std::string() : Name(); | |
| std::string flags; | |
| const auto flagStrings = matcher.GetFlagStrings(); | |
| const bool useValueNameOnce = flagStrings.size() == 1 ? false : params.useValueNameOnce; | |
| for (auto it = flagStrings.begin(); it != flagStrings.end(); ++it) | |
| { | |
| auto &flag = *it; | |
| if (it != flagStrings.begin()) | |
| { | |
| flags += ", "; | |
| } | |
| flags += flag.isShort ? params.shortPrefix : params.longPrefix; | |
| flags += flag.str(); | |
| if (!postfix.empty() && (!useValueNameOnce || it + 1 == flagStrings.end())) | |
| { | |
| flags += flag.isShort ? params.shortSeparator : params.longSeparator; | |
| flags += params.valueOpen + postfix + params.valueClose; | |
| } | |
| } | |
| return flags; | |
| } | |
| public: | |
| FlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : NamedBase(name_, help_, extraError_ ? Options::Single : Options()), matcher(std::move(matcher_)) {} | |
| FlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : NamedBase(name_, help_, options_), matcher(std::move(matcher_)) {} | |
| virtual ~FlagBase() {} | |
| virtual FlagBase *Match(const EitherFlag &flag) override | |
| { | |
| if (matcher.Match(flag)) | |
| { | |
| if ((GetOptions() & Options::Single) != Options::None && matched) | |
| { | |
| std::ostringstream problem; | |
| problem << "Flag '" << flag.str() << "' was passed multiple times, but is only allowed to be passed once"; | |
| #ifdef ARGS_NOEXCEPT | |
| error = Error::Extra; | |
| errorMsg = problem.str(); | |
| #else | |
| throw ExtraError(problem.str()); | |
| #endif | |
| } | |
| matched = true; | |
| return this; | |
| } | |
| return nullptr; | |
| } | |
| virtual std::vector<FlagBase*> GetAllFlags() override | |
| { | |
| return { this }; | |
| } | |
| const Matcher &GetMatcher() const | |
| { | |
| return matcher; | |
| } | |
| virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override | |
| { | |
| if (!Matched() && IsRequired()) | |
| { | |
| std::ostringstream problem; | |
| problem << "Flag '" << matcher.GetLongOrAny().str(shortPrefix, longPrefix) << "' is required"; | |
| #ifdef ARGS_NOEXCEPT | |
| error = Error::Required; | |
| errorMsg = problem.str(); | |
| #else | |
| throw RequiredError(problem.str()); | |
| #endif | |
| } | |
| } | |
| virtual std::vector<std::string> GetProgramLine(const HelpParams ¶ms) const override | |
| { | |
| if (!params.proglineShowFlags) | |
| { | |
| return {}; | |
| } | |
| const std::string postfix = NumberOfArguments() == 0 ? std::string() : Name(); | |
| const EitherFlag flag = params.proglinePreferShortFlags ? matcher.GetShortOrAny() : matcher.GetLongOrAny(); | |
| std::string res = flag.str(params.shortPrefix, params.longPrefix); | |
| if (!postfix.empty()) | |
| { | |
| res += params.proglineValueOpen + postfix + params.proglineValueClose; | |
| } | |
| return { IsRequired() ? params.proglineRequiredOpen + res + params.proglineRequiredClose | |
| : params.proglineNonrequiredOpen + res + params.proglineNonrequiredClose }; | |
| } | |
| virtual bool HasFlag() const override | |
| { | |
| 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; | |
| } | |
| const auto matcherError = matcher.GetError(); | |
| if (matcherError != Error::None) | |
| { | |
| return matcherError; | |
| } | |
| return error; | |
| } | |
| #endif | |
| /** Defines how many values can be consumed by this option. | |
| * | |
| * \return closed interval [min, max] | |
| */ | |
| virtual Nargs NumberOfArguments() const noexcept = 0; | |
| /** Parse values of this option. | |
| * | |
| * \param value Vector of values. It's size must be in NumberOfArguments() interval. | |
| */ | |
| virtual void ParseValue(const std::vector<std::string> &value) = 0; | |
| }; | |
| /** Base class for value-accepting flag options | |
| */ | |
| class ValueFlagBase : public FlagBase | |
| { | |
| public: | |
| ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : FlagBase(name_, help_, std::move(matcher_), extraError_) {} | |
| ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : FlagBase(name_, help_, std::move(matcher_), options_) {} | |
| virtual ~ValueFlagBase() {} | |
| virtual Nargs NumberOfArguments() const noexcept override | |
| { | |
| return 1; | |
| } | |
| }; | |
| class CompletionFlag : public ValueFlagBase | |
| { | |
| public: | |
| std::vector<std::string> reply; | |
| size_t cword = 0; | |
| std::string syntax; | |
| template <typename GroupClass> | |
| CompletionFlag(GroupClass &group_, Matcher &&matcher_): ValueFlagBase("completion", "completion flag", std::move(matcher_), Options::Hidden) | |
| { | |
| group_.AddCompletion(*this); | |
| } | |
| virtual ~CompletionFlag() {} | |
| virtual Nargs NumberOfArguments() const noexcept override | |
| { | |
| return 2; | |
| } | |
| virtual void ParseValue(const std::vector<std::string> &value_) override | |
| { | |
| syntax = value_.at(0); | |
| std::istringstream(value_.at(1)) >> cword; | |
| } | |
| /** Get the completion reply | |
| */ | |
| std::string Get() noexcept | |
| { | |
| return detail::Join(reply, "\n"); | |
| } | |
| virtual void Reset() noexcept override | |
| { | |
| ValueFlagBase::Reset(); | |
| cword = 0; | |
| syntax.clear(); | |
| reply.clear(); | |
| } | |
| }; | |
| /** Base class for positional options | |
| */ | |
| class PositionalBase : public NamedBase | |
| { | |
| protected: | |
| bool ready; | |
| public: | |
| PositionalBase(const std::string &name_, const std::string &help_, Options options_ = {}) : NamedBase(name_, help_, options_), ready(true) {} | |
| virtual ~PositionalBase() {} | |
| bool Ready() | |
| { | |
| return ready; | |
| } | |
| virtual void ParseValue(const std::string &value_) = 0; | |
| virtual void Reset() noexcept override | |
| { | |
| matched = false; | |
| ready = true; | |
| #ifdef ARGS_NOEXCEPT | |
| error = Error::None; | |
| errorMsg.clear(); | |
| #endif | |
| } | |
| virtual PositionalBase *GetNextPositional() override | |
| { | |
| return Ready() ? this : nullptr; | |
| } | |
| virtual bool HasPositional() const override | |
| { | |
| return true; | |
| } | |
| virtual std::vector<std::string> GetProgramLine(const HelpParams ¶ms) const override | |
| { | |
| return { IsRequired() ? params.proglineRequiredOpen + Name() + params.proglineRequiredClose | |
| : params.proglineNonrequiredOpen + Name() + params.proglineNonrequiredClose }; | |
| } | |
| virtual void Validate(const std::string &, const std::string &) const override | |
| { | |
| if (IsRequired() && !Matched()) | |
| { | |
| std::ostringstream problem; | |
| problem << "Option '" << Name() << "' is required"; | |
| #ifdef ARGS_NOEXCEPT | |
| error = Error::Required; | |
| errorMsg = problem.str(); | |
| #else | |
| throw RequiredError(problem.str()); | |
| #endif | |
| } | |
| } | |
| }; | |
| /** Class for all kinds of validating groups, including ArgumentParser | |
| */ | |
| class Group : public Base | |
| { | |
| private: | |
| std::vector<Base*> children; | |
| std::function<bool(const Group &)> validator; | |
| public: | |
| /** Default validators | |
| */ | |
| struct Validators | |
| { | |
| static bool Xor(const Group &group) | |
| { | |
| return group.MatchedChildren() == 1; | |
| } | |
| static bool AtLeastOne(const Group &group) | |
| { | |
| return group.MatchedChildren() >= 1; | |
| } | |
| static bool AtMostOne(const Group &group) | |
| { | |
| return group.MatchedChildren() <= 1; | |
| } | |
| static bool All(const Group &group) | |
| { | |
| return group.Children().size() == group.MatchedChildren(); | |
| } | |
| static bool AllOrNone(const Group &group) | |
| { | |
| return (All(group) || None(group)); | |
| } | |
| static bool AllChildGroups(const Group &group) | |
| { | |
| return std::none_of(std::begin(group.Children()), std::end(group.Children()), [](const Base* child) -> bool { | |
| return child->IsGroup() && !child->Matched(); | |
| }); | |
| } | |
| static bool DontCare(const Group &) | |
| { | |
| return true; | |
| } | |
| static bool CareTooMuch(const Group &) | |
| { | |
| return false; | |
| } | |
| static bool None(const Group &group) | |
| { | |
| return group.MatchedChildren() == 0; | |
| } | |
| }; | |
| /// If help is empty, this group will not be printed in help output | |
| Group(const std::string &help_ = std::string(), const std::function<bool(const Group &)> &validator_ = Validators::DontCare, Options options_ = {}) : Base(help_, options_), validator(validator_) {} | |
| /// If help is empty, this group will not be printed in help output | |
| Group(Group &group_, const std::string &help_ = std::string(), const std::function<bool(const Group &)> &validator_ = Validators::DontCare, Options options_ = {}) : Base(help_, options_), validator(validator_) | |
| { | |
| group_.Add(*this); | |
| } | |
| virtual ~Group() {} | |
| /** Append a child to this Group. | |
| */ | |
| void Add(Base &child) | |
| { | |
| children.emplace_back(&child); | |
| } | |
| /** Get all this group's children | |
| */ | |
| const std::vector<Base *> &Children() const | |
| { | |
| return children; | |
| } | |
| /** Return the first FlagBase that matches flag, or nullptr | |
| * | |
| * \param flag The flag with prefixes stripped | |
| * \return the first matching FlagBase pointer, or nullptr if there is no match | |
| */ | |
| virtual FlagBase *Match(const EitherFlag &flag) override | |
| { | |
| for (Base *child: Children()) | |
| { | |
| if (FlagBase *match = child->Match(flag)) | |
| { | |
| return match; | |
| } | |
| } | |
| return nullptr; | |
| } | |
| virtual std::vector<FlagBase*> GetAllFlags() override | |
| { | |
| std::vector<FlagBase*> res; | |
| for (Base *child: Children()) | |
| { | |
| auto childRes = child->GetAllFlags(); | |
| res.insert(res.end(), childRes.begin(), childRes.end()); | |
| } | |
| return res; | |
| } | |
| virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override | |
| { | |
| for (Base *child: Children()) | |
| { | |
| child->Validate(shortPrefix, longPrefix); | |
| } | |
| } | |
| /** Get the next ready positional, or nullptr if there is none | |
| * | |
| * \return the first ready PositionalBase pointer, or nullptr if there is no match | |
| */ | |
| virtual PositionalBase *GetNextPositional() override | |
| { | |
| for (Base *child: Children()) | |
| { | |
| if (auto next = child->GetNextPositional()) | |
| { | |
| return next; | |
| } | |
| } | |
| return nullptr; | |
| } | |
| /** Get whether this has any FlagBase children | |
| * | |
| * \return Whether or not there are any FlagBase children | |
| */ | |
| virtual bool HasFlag() const override | |
| { | |
| return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasFlag(); }); | |
| } | |
| /** Get whether this has any PositionalBase children | |
| * | |
| * \return Whether or not there are any PositionalBase children | |
| */ | |
| virtual bool HasPositional() const override | |
| { | |
| return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasPositional(); }); | |
| } | |
| /** Get whether this has any Command children | |
| * | |
| * \return Whether or not there are any Command children | |
| */ | |
| virtual bool HasCommand() const override | |
| { | |
| return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasCommand(); }); | |
| } | |
| /** Count the number of matched children this group has | |
| */ | |
| std::vector<Base *>::size_type MatchedChildren() const | |
| { | |
| return std::count_if(std::begin(Children()), std::end(Children()), [](const Base *child){return child->Matched();}); | |
| } | |
| /** Whether or not this group matches validation | |
| */ | |
| virtual bool Matched() const noexcept override | |
| { | |
| return validator(*this); | |
| } | |
| /** Get validation | |
| */ | |
| bool Get() const | |
| { | |
| return Matched(); | |
| } | |
| /** Get all the child descriptions for help generation | |
| */ | |
| virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams ¶ms, const unsigned int indent) const override | |
| { | |
| std::vector<std::tuple<std::string, std::string, unsigned int>> descriptions; | |
| // Push that group description on the back if not empty | |
| unsigned addindent = 0; | |
| if (!help.empty()) | |
| { | |
| descriptions.emplace_back(help, "", indent); | |
| addindent = 1; | |
| } | |
| for (Base *child: Children()) | |
| { | |
| if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None) | |
| { | |
| continue; | |
| } | |
| auto groupDescriptions = child->GetDescription(params, indent + addindent); | |
| descriptions.insert( | |
| std::end(descriptions), | |
| std::make_move_iterator(std::begin(groupDescriptions)), | |
| std::make_move_iterator(std::end(groupDescriptions))); | |
| } | |
| return descriptions; | |
| } | |
| /** Get the names of positional parameters | |
| */ | |
| virtual std::vector<std::string> GetProgramLine(const HelpParams ¶ms) const override | |
| { | |
| std::vector <std::string> names; | |
| for (Base *child: Children()) | |
| { | |
| if ((child->GetOptions() & Options::HiddenFromUsage) != Options::None) | |
| { | |
| continue; | |
| } | |
| auto groupNames = child->GetProgramLine(params); | |
| names.insert( | |
| std::end(names), | |
| std::make_move_iterator(std::begin(groupNames)), | |
| std::make_move_iterator(std::end(groupNames))); | |
| } | |
| return names; | |
| } | |
| virtual std::vector<Command*> GetCommands() override | |
| { | |
| std::vector<Command*> res; | |
| for (const auto &child : Children()) | |
| { | |
| auto subparsers = child->GetCommands(); | |
| res.insert(std::end(res), std::begin(subparsers), std::end(subparsers)); | |
| } | |
| return res; | |
| } | |
| virtual bool IsGroup() const override | |
| { | |
| return true; | |
| } | |
| virtual void Reset() noexcept override | |
| { | |
| Base::Reset(); | |
| for (auto &child: Children()) | |
| { | |
| child->Reset(); | |
| } | |
| #ifdef ARGS_NOEXCEPT | |
| error = Error::None; | |
| errorMsg.clear(); | |
| #endif | |
| } | |
| #ifdef ARGS_NOEXCEPT | |
| /// Only for ARGS_NOEXCEPT | |
| virtual Error GetError() const override | |
| { | |
| if (error != Error::None) | |
| { | |
| return error; | |
| } | |
| auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;}); | |
| if (it == Children().end()) | |
| { | |
| return Error::None; | |
| } else | |
| { | |
| return (*it)->GetError(); | |
| } | |
| } | |
| #endif | |
| }; | |
| /** Class for using global options in ArgumentParser. | |
| */ | |
| class GlobalOptions : public Group | |
| { | |
| public: | |
| GlobalOptions(Group &base, Base &options_) : Group(base, {}, Group::Validators::DontCare, Options::Global) | |
| { | |
| Add(options_); | |
| } | |
| }; | |
| /** Utility class for building subparsers with coroutines/callbacks. | |
| * | |
| * Brief example: | |
| * \code | |
| * Command command(argumentParser, "command", "my command", [](args::Subparser &s) | |
| * { | |
| * // your command flags/positionals | |
| * s.Parse(); //required | |
| * //your command code | |
| * }); | |
| * \endcode | |
| * | |
| * For ARGS_NOEXCEPT mode don't forget to check `s.GetError()` after `s.Parse()` | |
| * and return if it isn't equals to args::Error::None. | |
| * | |
| * \sa Command | |
| */ | |
| class Subparser : public Group | |
| { | |
| private: | |
| std::vector<std::string> args; | |
| std::vector<std::string> kicked; | |
| ArgumentParser *parser = nullptr; | |
| const HelpParams &helpParams; | |
| const Command &command; | |
| bool isParsed = false; | |
| public: | |
| Subparser(std::vector<std::string> args_, ArgumentParser &parser_, const Command &command_, const HelpParams &helpParams_) | |
| : args(std::move(args_)), parser(&parser_), helpParams(helpParams_), command(command_) | |
| { | |
| } | |
| Subparser(const Command &command_, const HelpParams &helpParams_) : helpParams(helpParams_), command(command_) | |
| { | |
| } | |
| Subparser(const Subparser&) = delete; | |
| Subparser(Subparser&&) = delete; | |
| Subparser &operator = (const Subparser&) = delete; | |
| Subparser &operator = (Subparser&&) = delete; | |
| const Command &GetCommand() | |
| { | |
| return command; | |
| } | |
| /** (INTERNAL) Determines whether Parse was called or not. | |
| */ | |
| bool IsParsed() const | |
| { | |
| return isParsed; | |
| } | |
| /** Continue parsing arguments for new command. | |
| */ | |
| void Parse(); | |
| /** Returns a vector of kicked out arguments. | |
| * | |
| * \sa Base::KickOut | |
| */ | |
| const std::vector<std::string> &KickedOut() const noexcept | |
| { | |
| return kicked; | |
| } | |
| }; | |
| /** Main class for building subparsers. | |
| * | |
| * /sa Subparser | |
| */ | |
| class Command : public Group | |
| { | |
| private: | |
| friend class Subparser; | |
| std::string name; | |
| std::string help; | |
| std::string description; | |
| std::string epilog; | |
| std::string proglinePostfix; | |
| std::function<void(Subparser&)> parserCoroutine; | |
| bool commandIsRequired = true; | |
| Command *selectedCommand = nullptr; | |
| mutable std::vector<std::tuple<std::string, std::string, unsigned>> subparserDescription; | |
| mutable std::vector<std::string> subparserProgramLine; | |
| mutable bool subparserHasFlag = false; | |
| mutable bool subparserHasPositional = false; | |
| mutable bool subparserHasCommand = false; | |
| #ifdef ARGS_NOEXCEPT | |
| mutable Error subparserError = Error::None; | |
| #endif | |
| mutable Subparser *subparser = nullptr; | |
| protected: | |
| class RaiiSubparser | |
| { | |
| public: | |
| RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_); | |
| RaiiSubparser(const Command &command_, const HelpParams ¶ms_); | |
| ~RaiiSubparser() | |
| { | |
| command.subparser = oldSubparser; | |
| } | |
| Subparser &Parser() | |
| { | |
| return parser; | |
| } | |
| private: | |
| const Command &command; | |
| Subparser parser; | |
| Subparser *oldSubparser; | |
| }; | |
| Command() = default; | |
| std::function<void(Subparser&)> &GetCoroutine() | |
| { | |
| return selectedCommand != nullptr ? selectedCommand->GetCoroutine() : parserCoroutine; | |
| } | |
| Command &SelectedCommand() | |
| { | |
| Command *res = this; | |
| while (res->selectedCommand != nullptr) | |
| { | |
| res = res->selectedCommand; | |
| } | |
| return *res; | |
| } | |
| const Command &SelectedCommand() const | |
| { | |
| const Command *res = this; | |
| while (res->selectedCommand != nullptr) | |
| { | |
| res = res->selectedCommand; | |
| } | |
| return *res; | |
| } | |
| void UpdateSubparserHelp(const HelpParams ¶ms) const | |
| { | |
| if (parserCoroutine) | |
| { | |
| RaiiSubparser coro(*this, params); | |
| #ifndef ARGS_NOEXCEPT | |
| try | |
| { | |
| parserCoroutine(coro.Parser()); | |
| } | |
| catch (args::SubparserError&) | |
| { | |
| } | |
| #else | |
| parserCoroutine(coro.Parser()); | |
| #endif | |
| } | |
| } | |
| public: | |
| Command(Group &base_, std::string name_, std::string help_, std::function<void(Subparser&)> coroutine_ = {}) | |
| : name(std::move(name_)), help(std::move(help_)), parserCoroutine(std::move(coroutine_)) | |
| { | |
| base_.Add(*this); | |
| } | |
| /** The description that appears on the prog line after options | |
| */ | |
| const std::string &ProglinePostfix() const | |
| { return proglinePostfix; } | |
| /** The description that appears on the prog line after options | |
| */ | |
| void ProglinePostfix(const std::string &proglinePostfix_) | |
| { this->proglinePostfix = proglinePostfix_; } | |
| /** The description that appears above options | |
| */ | |
| const std::string &Description() const | |
| { return description; } | |
| /** The description that appears above options | |
| */ | |
| void Description(const std::string &description_) | |
| { this->description = description_; } | |
| /** The description that appears below options | |
| */ | |
| const std::string &Epilog() const | |
| { return epilog; } | |
| /** The description that appears below options | |
| */ | |
| void Epilog(const std::string &epilog_) | |
| { this->epilog = epilog_; } | |
| /** The name of command | |
| */ | |
| const std::string &Name() const | |
| { return name; } | |
| /** The description of command | |
| */ | |
| const std::string &Help() const | |
| { return help; } | |
| /** If value is true, parser will fail if no command was parsed. | |
| * | |
| * Default: true. | |
| */ | |
| void RequireCommand(bool value) | |
| { commandIsRequired = value; } | |
| virtual bool IsGroup() const override | |
| { return false; } | |
| virtual bool Matched() const noexcept override | |
| { return Base::Matched(); } | |
| operator bool() const noexcept | |
| { return Matched(); } | |
| void Match() noexcept | |
| { matched = true; } | |
| void SelectCommand(Command *c) noexcept | |
| { | |
| selectedCommand = c; | |
| if (c != nullptr) | |
| { | |
| c->Match(); | |
| } | |
| } | |
| virtual FlagBase *Match(const EitherFlag &flag) override | |
| { | |
| if (selectedCommand != nullptr) | |
| { | |
| if (auto *res = selectedCommand->Match(flag)) | |
| { | |
| return res; | |
| } | |
| for (auto *child: Children()) | |
| { | |
| if ((child->GetOptions() & Options::Global) != Options::None) | |
| { | |
| if (auto *res = child->Match(flag)) | |
| { | |
| return res; | |
| } | |
| } | |
| } | |
| return nullptr; | |
| } | |
| if (subparser != nullptr) | |
| { | |
| return subparser->Match(flag); | |
| } | |
| return Matched() ? Group::Match(flag) : nullptr; | |
| } | |
| virtual std::vector<FlagBase*> GetAllFlags() override | |
| { | |
| std::vector<FlagBase*> res; | |
| if (!Matched()) | |
| { | |
| return res; | |
| } | |
| for (auto *child: Children()) | |
| { | |
| if (selectedCommand == nullptr || (child->GetOptions() & Options::Global) != Options::None) | |
| { | |
| auto childFlags = child->GetAllFlags(); | |
| res.insert(res.end(), childFlags.begin(), childFlags.end()); | |
| } | |
| } | |
| if (selectedCommand != nullptr) | |
| { | |
| auto childFlags = selectedCommand->GetAllFlags(); | |
| res.insert(res.end(), childFlags.begin(), childFlags.end()); | |
| } | |
| if (subparser != nullptr) | |
| { | |
| auto childFlags = subparser->GetAllFlags(); | |
| res.insert(res.end(), childFlags.begin(), childFlags.end()); | |
| } | |
| return res; | |
| } | |
| virtual PositionalBase *GetNextPositional() override | |
| { | |
| if (selectedCommand != nullptr) | |
| { | |
| if (auto *res = selectedCommand->GetNextPositional()) | |
| { | |
| return res; | |
| } | |
| for (auto *child: Children()) | |
| { | |
| if ((child->GetOptions() & Options::Global) != Options::None) | |
| { | |
| if (auto *res = child->GetNextPositional()) | |
| { | |
| return res; | |
| } | |
| } | |
| } | |
| return nullptr; | |
| } | |
| if (subparser != nullptr) | |
| { | |
| return subparser->GetNextPositional(); | |
| } | |
| return Matched() ? Group::GetNextPositional() : nullptr; | |
| } | |
| virtual bool HasFlag() const override | |
| { | |
| return subparserHasFlag || Group::HasFlag(); | |
| } | |
| virtual bool HasPositional() const override | |
| { | |
| return subparserHasPositional || Group::HasPositional(); | |
| } | |
| virtual bool HasCommand() const override | |
| { | |
| return true; | |
| } | |
| std::vector<std::string> GetCommandProgramLine(const HelpParams ¶ms) const | |
| { | |
| UpdateSubparserHelp(params); | |
| auto res = Group::GetProgramLine(params); | |
| res.insert(res.end(), subparserProgramLine.begin(), subparserProgramLine.end()); | |
| if (!params.proglineCommand.empty() && (Group::HasCommand() || subparserHasCommand)) | |
| { | |
| res.insert(res.begin(), commandIsRequired ? params.proglineCommand : "[" + params.proglineCommand + "]"); | |
| } | |
| if (!Name().empty()) | |
| { | |
| res.insert(res.begin(), Name()); | |
| } | |
| if ((subparserHasFlag || Group::HasFlag()) && params.showProglineOptions && !params.proglineShowFlags) | |
| { | |
| res.push_back(params.proglineOptions); | |
| } | |
| if (!ProglinePostfix().empty()) | |
| { | |
| std::string line; | |
| for (char c : ProglinePostfix()) | |
| { | |
| if (isspace(c)) | |
| { | |
| if (!line.empty()) | |
| { | |
| res.push_back(line); | |
| line.clear(); | |
| } | |
| if (c == '\n') | |
| { | |
| res.push_back("\n"); | |
| } | |
| } | |
| else | |
| { | |
| line += c; | |
| } | |
| } | |
| if (!line.empty()) | |
| { | |
| res.push_back(line); | |
| } | |
| } | |
| return res; | |
| } | |
| virtual std::vector<std::string> GetProgramLine(const HelpParams ¶ms) const override | |
| { | |
| if (!Matched()) | |
| { | |
| return {}; | |
| } | |
| return GetCommandProgramLine(params); | |
| } | |
| virtual std::vector<Command*> GetCommands() override | |
| { | |
| if (selectedCommand != nullptr) | |
| { | |
| return selectedCommand->GetCommands(); | |
| } | |
| if (Matched()) | |
| { | |
| return Group::GetCommands(); | |
| } | |
| return { this }; | |
| } | |
| virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams ¶ms, const unsigned int indent) const override | |
| { | |
| std::vector<std::tuple<std::string, std::string, unsigned>> descriptions; | |
| unsigned addindent = 0; | |
| UpdateSubparserHelp(params); | |
| if (!Matched()) | |
| { | |
| if (params.showCommandFullHelp) | |
| { | |
| std::ostringstream s; | |
| bool empty = true; | |
| for (const auto &progline: GetCommandProgramLine(params)) | |
| { | |
| if (!empty) | |
| { | |
| s << ' '; | |
| } | |
| else | |
| { | |
| empty = false; | |
| } | |
| s << progline; | |
| } | |
| descriptions.emplace_back(s.str(), "", indent); | |
| } | |
| else | |
| { | |
| descriptions.emplace_back(Name(), help, indent); | |
| } | |
| if (!params.showCommandChildren && !params.showCommandFullHelp) | |
| { | |
| return descriptions; | |
| } | |
| addindent = 1; | |
| } | |
| if (params.showCommandFullHelp && !Matched()) | |
| { | |
| descriptions.emplace_back("", "", indent + addindent); | |
| descriptions.emplace_back(Description().empty() ? Help() : Description(), "", indent + addindent); | |
| descriptions.emplace_back("", "", indent + addindent); | |
| } | |
| for (Base *child: Children()) | |
| { | |
| if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None) | |
| { | |
| continue; | |
| } | |
| auto groupDescriptions = child->GetDescription(params, indent + addindent); | |
| descriptions.insert( | |
| std::end(descriptions), | |
| std::make_move_iterator(std::begin(groupDescriptions)), | |
| std::make_move_iterator(std::end(groupDescriptions))); | |
| } | |
| for (auto childDescription: subparserDescription) | |
| { | |
| std::get<2>(childDescription) += indent + addindent; | |
| descriptions.push_back(std::move(childDescription)); | |
| } | |
| if (params.showCommandFullHelp && !Matched()) | |
| { | |
| descriptions.emplace_back("", "", indent + addindent); | |
| if (!Epilog().empty()) | |
| { | |
| descriptions.emplace_back(Epilog(), "", indent + addindent); | |
| descriptions.emplace_back("", "", indent + addindent); | |
| } | |
| } | |
| return descriptions; | |
| } | |
| 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()) | |
| { | |
| std::ostringstream problem; | |
| problem << "Group validation failed somewhere!"; | |
| #ifdef ARGS_NOEXCEPT | |
| error = Error::Validation; | |
| errorMsg = problem.str(); | |
| #else | |
| throw ValidationError(problem.str()); | |
| #endif | |
| } | |
| child->Validate(shortprefix, longprefix); | |
| } | |
| if (subparser != nullptr) | |
| { | |
| subparser->Validate(shortprefix, longprefix); | |
| } | |
| if (selectedCommand == nullptr && commandIsRequired && (Group::HasCommand() || subparserHasCommand)) | |
| { | |
| std::ostringstream problem; | |
| problem << "Command is required"; | |
| #ifdef ARGS_NOEXCEPT | |
| error = Error::Validation; | |
| errorMsg = problem.str(); | |
| #else | |
| throw ValidationError(problem.str()); | |
| #endif | |
| } | |
| } | |
| virtual void Reset() noexcept override | |
| { | |
| Group::Reset(); | |
| selectedCommand = nullptr; | |
| subparserProgramLine.clear(); | |
| subparserDescription.clear(); | |
| subparserHasFlag = false; | |
| subparserHasPositional = false; | |
| subparserHasCommand = false; | |
| #ifdef ARGS_NOEXCEPT | |
| subparserError = Error::None; | |
| #endif | |
| } | |
| #ifdef ARGS_NOEXCEPT | |
| /// Only for ARGS_NOEXCEPT | |
| virtual Error GetError() const override | |
| { | |
| if (!Matched()) | |
| { | |
| return Error::None; | |
| } | |
| if (error != Error::None) | |
| { | |
| return error; | |
| } | |
| if (subparserError != Error::None) | |
| { | |
| return subparserError; | |
| } | |
| return Group::GetError(); | |
| } | |
| #endif | |
| }; | |
| /** The main user facing command line argument parser class | |
| */ | |
| class ArgumentParser : public Command | |
| { | |
| friend class Subparser; | |
| private: | |
| std::string longprefix; | |
| std::string shortprefix; | |
| std::string longseparator; | |
| std::string terminator; | |
| bool allowJoinedShortValue = true; | |
| bool allowJoinedLongValue = true; | |
| bool allowSeparateShortValue = true; | |
| bool allowSeparateLongValue = true; | |
| CompletionFlag *completion = nullptr; | |
| bool readCompletion = false; | |
| protected: | |
| enum class OptionType | |
| { | |
| LongFlag, | |
| ShortFlag, | |
| Positional | |
| }; | |
| OptionType ParseOption(const std::string &s, bool allowEmpty = false) | |
| { | |
| if (s.find(longprefix) == 0 && (allowEmpty || s.length() > longprefix.length())) | |
| { | |
| return OptionType::LongFlag; | |
| } | |
| if (s.find(shortprefix) == 0 && (allowEmpty || s.length() > shortprefix.length())) | |
| { | |
| return OptionType::ShortFlag; | |
| } | |
| return OptionType::Positional; | |
| } | |
| template <typename It> | |
| bool Complete(FlagBase &flag, It it, It end) | |
| { | |
| auto nextIt = it; | |
| if (!readCompletion || (++nextIt != end)) | |
| { | |
| return false; | |
| } | |
| const auto &chunk = *it; | |
| for (auto &choice : flag.HelpChoices(helpParams)) | |
| { | |
| AddCompletionReply(chunk, choice); | |
| } | |
| #ifndef ARGS_NOEXCEPT | |
| throw Completion(completion->Get()); | |
| #else | |
| return true; | |
| #endif | |
| } | |
| /** (INTERNAL) Parse flag's values | |
| * | |
| * \param arg The string to display in error message as a flag name | |
| * \param[in, out] it The iterator to first value. It will point to the last value | |
| * \param end The end iterator | |
| * \param joinedArg Joined value (e.g. bar in --foo=bar) | |
| * \param canDiscardJoined If true joined value can be parsed as flag not as a value (as in -abcd) | |
| * \param[out] values The vector to store parsed arg's values | |
| */ | |
| template <typename It> | |
| std::string ParseArgsValues(FlagBase &flag, const std::string &arg, It &it, It end, | |
| const bool allowSeparate, const bool allowJoined, | |
| const bool hasJoined, const std::string &joinedArg, | |
| const bool canDiscardJoined, std::vector<std::string> &values) | |
| { | |
| values.clear(); | |
| Nargs nargs = flag.NumberOfArguments(); | |
| if (hasJoined && !allowJoined && nargs.min != 0) | |
| { | |
| return "Flag '" + arg + "' was passed a joined argument, but these are disallowed"; | |
| } | |
| if (hasJoined) | |
| { | |
| if (!canDiscardJoined || nargs.max != 0) | |
| { | |
| values.push_back(joinedArg); | |
| } | |
| } else if (!allowSeparate) | |
| { | |
| if (nargs.min != 0) | |
| { | |
| return "Flag '" + arg + "' was passed a separate argument, but these are disallowed"; | |
| } | |
| } else | |
| { | |
| auto valueIt = it; | |
| ++valueIt; | |
| while (valueIt != end && | |
| values.size() < nargs.max && | |
| (nargs.min == nargs.max || ParseOption(*valueIt) == OptionType::Positional)) | |
| { | |
| if (Complete(flag, valueIt, end)) | |
| { | |
| it = end; | |
| return ""; | |
| } | |
| values.push_back(*valueIt); | |
| ++it; | |
| ++valueIt; | |
| } | |
| } | |
| if (values.size() > nargs.max) | |
| { | |
| return "Passed an argument into a non-argument flag: " + arg; | |
| } else if (values.size() < nargs.min) | |
| { | |
| if (nargs.min == 1 && nargs.max == 1) | |
| { | |
| return "Flag '" + arg + "' requires an argument but received none"; | |
| } else if (nargs.min == 1) | |
| { | |
| return "Flag '" + arg + "' requires at least one argument but received none"; | |
| } else if (nargs.min != nargs.max) | |
| { | |
| return "Flag '" + arg + "' requires at least " + std::to_string(nargs.min) + | |
| " arguments but received " + std::to_string(values.size()); | |
| } else | |
| { | |
| return "Flag '" + arg + "' requires " + std::to_string(nargs.min) + | |
| " arguments but received " + std::to_string(values.size()); | |
| } | |
| } | |
| return {}; | |
| } | |
| template <typename It> | |
| bool ParseLong(It &it, It end) | |
| { | |
| const auto &chunk = *it; | |
| const auto argchunk = chunk.substr(longprefix.size()); | |
| // Try to separate it, in case of a separator: | |
| const auto separator = longseparator.empty() ? argchunk.npos : argchunk.find(longseparator); | |
| // If the separator is in the argument, separate it. | |
| const auto arg = (separator != argchunk.npos ? | |
| std::string(argchunk, 0, separator) | |
| : argchunk); | |
| const auto joined = (separator != argchunk.npos ? | |
| argchunk.substr(separator + longseparator.size()) | |
| : std::string()); | |
| if (auto flag = Match(arg)) | |
| { | |
| std::vector<std::string> values; | |
| const std::string errorMessage = ParseArgsValues(*flag, arg, it, end, allowSeparateLongValue, allowJoinedLongValue, | |
| separator != argchunk.npos, joined, false, values); | |
| if (!errorMessage.empty()) | |
| { | |
| #ifndef ARGS_NOEXCEPT | |
| throw ParseError(errorMessage); | |
| #else | |
| error = Error::Parse; | |
| errorMsg = errorMessage; | |
| return false; | |
| #endif | |
| } | |
| if (!readCompletion) | |
| { | |
| flag->ParseValue(values); | |
| } | |
| if (flag->KickOut()) | |
| { | |
| ++it; | |
| return false; | |
| } | |
| } else | |
| { | |
| const std::string errorMessage("Flag could not be matched: " + arg); | |
| #ifndef ARGS_NOEXCEPT | |
| throw ParseError(errorMessage); | |
| #else | |
| error = Error::Parse; | |
| errorMsg = errorMessage; | |
| return false; | |
| #endif | |
| } | |
| return true; | |
| } | |
| template <typename It> | |
| bool ParseShort(It &it, It end) | |
| { | |
| const auto &chunk = *it; | |
| const auto argchunk = chunk.substr(shortprefix.size()); | |
| for (auto argit = std::begin(argchunk); argit != std::end(argchunk); ++argit) | |
| { | |
| const auto arg = *argit; | |
| if (auto flag = Match(arg)) | |
| { | |
| const std::string value(argit + 1, std::end(argchunk)); | |
| std::vector<std::string> values; | |
| const std::string errorMessage = ParseArgsValues(*flag, std::string(1, arg), it, end, | |
| allowSeparateShortValue, allowJoinedShortValue, | |
| !value.empty(), value, !value.empty(), values); | |
| if (!errorMessage.empty()) | |
| { | |
| #ifndef ARGS_NOEXCEPT | |
| throw ParseError(errorMessage); | |
| #else | |
| error = Error::Parse; | |
| errorMsg = errorMessage; | |
| return false; | |
| #endif | |
| } | |
| if (!readCompletion) | |
| { | |
| flag->ParseValue(values); | |
| } | |
| if (flag->KickOut()) | |
| { | |
| ++it; | |
| return false; | |
| } | |
| if (!values.empty()) | |
| { | |
| break; | |
| } | |
| } else | |
| { | |
| const std::string errorMessage("Flag could not be matched: '" + std::string(1, arg) + "'"); | |
| #ifndef ARGS_NOEXCEPT | |
| throw ParseError(errorMessage); | |
| #else | |
| error = Error::Parse; | |
| errorMsg = errorMessage; | |
| return false; | |
| #endif | |
| } | |
| } | |
| return true; | |
| } | |
| bool AddCompletionReply(const std::string &cur, const std::string &choice) | |
| { | |
| if (cur.empty() || choice.find(cur) == 0) | |
| { | |
| if (completion->syntax == "bash" && ParseOption(choice) == OptionType::LongFlag && choice.find(longseparator) != std::string::npos) | |
| { | |
| completion->reply.push_back(choice.substr(choice.find(longseparator) + 1)); | |
| } else | |
| { | |
| completion->reply.push_back(choice); | |
| } | |
| return true; | |
| } | |
| return false; | |
| } | |
| template <typename It> | |
| bool Complete(It it, It end) | |
| { | |
| auto nextIt = it; | |
| if (!readCompletion || (++nextIt != end)) | |
| { | |
| return false; | |
| } | |
| const auto &chunk = *it; | |
| auto pos = GetNextPositional(); | |
| std::vector<Command *> commands = GetCommands(); | |
| const auto optionType = ParseOption(chunk, true); | |
| if (!commands.empty() && (chunk.empty() || optionType == OptionType::Positional)) | |
| { | |
| for (auto &cmd : commands) | |
| { | |
| if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None) | |
| { | |
| AddCompletionReply(chunk, cmd->Name()); | |
| } | |
| } | |
| } else | |
| { | |
| bool hasPositionalCompletion = true; | |
| if (!commands.empty()) | |
| { | |
| for (auto &cmd : commands) | |
| { | |
| if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None) | |
| { | |
| AddCompletionReply(chunk, cmd->Name()); | |
| } | |
| } | |
| } else if (pos) | |
| { | |
| if ((pos->GetOptions() & Options::HiddenFromCompletion) == Options::None) | |
| { | |
| auto choices = pos->HelpChoices(helpParams); | |
| hasPositionalCompletion = !choices.empty() || optionType != OptionType::Positional; | |
| for (auto &choice : choices) | |
| { | |
| AddCompletionReply(chunk, choice); | |
| } | |
| } | |
| } | |
| if (hasPositionalCompletion) | |
| { | |
| auto flags = GetAllFlags(); | |
| for (auto flag : flags) | |
| { | |
| if ((flag->GetOptions() & Options::HiddenFromCompletion) != Options::None) | |
| { | |
| continue; | |
| } | |
| auto &matcher = flag->GetMatcher(); | |
| if (!AddCompletionReply(chunk, matcher.GetShortOrAny().str(shortprefix, longprefix))) | |
| { | |
| for (auto &flagName : matcher.GetFlagStrings()) | |
| { | |
| if (AddCompletionReply(chunk, flagName.str(shortprefix, longprefix))) | |
| { | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| if (optionType == OptionType::LongFlag && allowJoinedLongValue) | |
| { | |
| const auto separator = longseparator.empty() ? chunk.npos : chunk.find(longseparator); | |
| if (separator != chunk.npos) | |
| { | |
| std::string arg(chunk, 0, separator); | |
| if (auto flag = this->Match(arg.substr(longprefix.size()))) | |
| { | |
| for (auto &choice : flag->HelpChoices(helpParams)) | |
| { | |
| AddCompletionReply(chunk, arg + longseparator + choice); | |
| } | |
| } | |
| } | |
| } else if (optionType == OptionType::ShortFlag && allowJoinedShortValue) | |
| { | |
| if (chunk.size() > shortprefix.size() + 1) | |
| { | |
| auto arg = chunk.at(shortprefix.size()); | |
| //TODO: support -abcVALUE where a and b take no value | |
| if (auto flag = this->Match(arg)) | |
| { | |
| for (auto &choice : flag->HelpChoices(helpParams)) | |
| { | |
| AddCompletionReply(chunk, shortprefix + arg + choice); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| #ifndef ARGS_NOEXCEPT | |
| throw Completion(completion->Get()); | |
| #else | |
| return true; | |
| #endif | |
| } | |
| template <typename It> | |
| It Parse(It begin, It end) | |
| { | |
| bool terminated = false; | |
| std::vector<Command *> commands = GetCommands(); | |
| // Check all arg chunks | |
| for (auto it = begin; it != end; ++it) | |
| { | |
| if (Complete(it, end)) | |
| { | |
| return end; | |
| } | |
| const auto &chunk = *it; | |
| if (!terminated && chunk == terminator) | |
| { | |
| terminated = true; | |
| } else if (!terminated && ParseOption(chunk) == OptionType::LongFlag) | |
| { | |
| if (!ParseLong(it, end)) | |
| { | |
| return it; | |
| } | |
| } else if (!terminated && ParseOption(chunk) == OptionType::ShortFlag) | |
| { | |
| if (!ParseShort(it, end)) | |
| { | |
| return it; | |
| } | |
| } else if (!terminated && !commands.empty()) | |
| { | |
| auto itCommand = std::find_if(commands.begin(), commands.end(), [&chunk](Command *c) { return c->Name() == chunk; }); | |
| if (itCommand == commands.end()) | |
| { | |
| const std::string errorMessage("Unknown command: " + chunk); | |
| #ifndef ARGS_NOEXCEPT | |
| throw ParseError(errorMessage); | |
| #else | |
| error = Error::Parse; | |
| errorMsg = errorMessage; | |
| return it; | |
| #endif | |
| } | |
| SelectCommand(*itCommand); | |
| if (const auto &coroutine = GetCoroutine()) | |
| { | |
| ++it; | |
| RaiiSubparser coro(*this, std::vector<std::string>(it, end)); | |
| coroutine(coro.Parser()); | |
| #ifdef ARGS_NOEXCEPT | |
| error = GetError(); | |
| if (error != Error::None) | |
| { | |
| return end; | |
| } | |
| if (!coro.Parser().IsParsed()) | |
| { | |
| error = Error::Usage; | |
| return end; | |
| } | |
| #else | |
| if (!coro.Parser().IsParsed()) | |
| { | |
| throw UsageError("Subparser::Parse was not called"); | |
| } | |
| #endif | |
| break; | |
| } | |
| commands = GetCommands(); | |
| } else | |
| { | |
| auto pos = GetNextPositional(); | |
| if (pos) | |
| { | |
| pos->ParseValue(chunk); | |
| if (pos->KickOut()) | |
| { | |
| return ++it; | |
| } | |
| } else | |
| { | |
| const std::string errorMessage("Passed in argument, but no positional arguments were ready to receive it: " + chunk); | |
| #ifndef ARGS_NOEXCEPT | |
| throw ParseError(errorMessage); | |
| #else | |
| error = Error::Parse; | |
| errorMsg = errorMessage; | |
| return it; | |
| #endif | |
| } | |
| } | |
| if (!readCompletion && completion != nullptr && completion->Matched()) | |
| { | |
| #ifdef ARGS_NOEXCEPT | |
| error = Error::Completion; | |
| #endif | |
| readCompletion = true; | |
| ++it; | |
| size_t argsLeft = std::distance(it, end); | |
| if (completion->cword == 0 || argsLeft <= 1 || completion->cword >= argsLeft) | |
| { | |
| #ifndef ARGS_NOEXCEPT | |
| throw Completion(""); | |
| #endif | |
| } | |
| std::vector<std::string> curArgs(++it, end); | |
| curArgs.resize(completion->cword); | |
| if (completion->syntax == "bash") | |
| { | |
| // bash tokenizes --flag=value as --flag=value | |
| for (size_t idx = 0; idx < curArgs.size(); ) | |
| { | |
| if (idx > 0 && curArgs[idx] == "=") | |
| { | |
| curArgs[idx - 1] += "="; | |
| if (idx + 1 < curArgs.size()) | |
| { | |
| curArgs[idx - 1] += curArgs[idx + 1]; | |
| curArgs.erase(curArgs.begin() + idx, curArgs.begin() + idx + 2); | |
| } else | |
| { | |
| curArgs.erase(curArgs.begin() + idx); | |
| } | |
| } else | |
| { | |
| ++idx; | |
| } | |
| } | |
| } | |
| #ifndef ARGS_NOEXCEPT | |
| try | |
| { | |
| Parse(curArgs.begin(), curArgs.end()); | |
| throw Completion(""); | |
| } | |
| catch (Completion &) | |
| { | |
| throw; | |
| } | |
| catch (args::Error&) | |
| { | |
| throw Completion(""); | |
| } | |
| #else | |
| return Parse(curArgs.begin(), curArgs.end()); | |
| #endif | |
| } | |
| } | |
| Validate(shortprefix, longprefix); | |
| return end; | |
| } | |
| public: | |
| HelpParams helpParams; | |
| ArgumentParser(const std::string &description_, const std::string &epilog_ = std::string()) | |
| { | |
| Description(description_); | |
| Epilog(epilog_); | |
| LongPrefix("--"); | |
| ShortPrefix("-"); | |
| LongSeparator("="); | |
| Terminator("--"); | |
| SetArgumentSeparations(true, true, true, true); | |
| matched = true; | |
| } | |
| void AddCompletion(CompletionFlag &completionFlag) | |
| { | |
| completion = &completionFlag; | |
| Add(completionFlag); | |
| } | |
| /** The program name for help generation | |
| */ | |
| const std::string &Prog() const | |
| { return helpParams.programName; } | |
| /** The program name for help generation | |
| */ | |
| void Prog(const std::string &prog_) | |
| { this->helpParams.programName = prog_; } | |
| /** The prefix for long flags | |
| */ | |
| const std::string &LongPrefix() const | |
| { return longprefix; } | |
| /** The prefix for long flags | |
| */ | |
| void LongPrefix(const std::string &longprefix_) | |
| { | |
| this->longprefix = longprefix_; | |
| this->helpParams.longPrefix = longprefix_; | |
| } | |
| /** The prefix for short flags | |
| */ | |
| const std::string &ShortPrefix() const | |
| { return shortprefix; } | |
| /** The prefix for short flags | |
| */ | |
| void ShortPrefix(const std::string &shortprefix_) | |
| { | |
| this->shortprefix = shortprefix_; | |
| this->helpParams.shortPrefix = shortprefix_; | |
| } | |
| /** The separator for long flags | |
| */ | |
| const std::string &LongSeparator() const | |
| { return longseparator; } | |
| /** The separator for long flags | |
| */ | |
| void LongSeparator(const std::string &longseparator_) | |
| { | |
| if (longseparator_.empty()) | |
| { | |
| const std::string errorMessage("longseparator can not be set to empty"); | |
| #ifdef ARGS_NOEXCEPT | |
| error = Error::Usage; | |
| errorMsg = errorMessage; | |
| #else | |
| throw UsageError(errorMessage); | |
| #endif | |
| } else | |
| { | |
| this->longseparator = longseparator_; | |
| this->helpParams.longSeparator = allowJoinedLongValue ? longseparator_ : " "; | |
| } | |
| } | |
| /** The terminator that forcibly separates flags from positionals | |
| */ | |
| const std::string &Terminator() const | |
| { return terminator; } | |
| /** The terminator that forcibly separates flags from positionals | |
| */ | |
| void Terminator(const std::string &terminator_) | |
| { this->terminator = terminator_; } | |
| /** Get the current argument separation parameters. | |
| * | |
| * See SetArgumentSeparations for details on what each one means. | |
| */ | |
| void GetArgumentSeparations( | |
| bool &allowJoinedShortValue_, | |
| bool &allowJoinedLongValue_, | |
| bool &allowSeparateShortValue_, | |
| bool &allowSeparateLongValue_) const | |
| { | |
| allowJoinedShortValue_ = this->allowJoinedShortValue; | |
| allowJoinedLongValue_ = this->allowJoinedLongValue; | |
| allowSeparateShortValue_ = this->allowSeparateShortValue; | |
| allowSeparateLongValue_ = this->allowSeparateLongValue; | |
| } | |
| /** Change allowed option separation. | |
| * | |
| * \param allowJoinedShortValue_ Allow a short flag that accepts an argument to be passed its argument immediately next to it (ie. in the same argv field) | |
| * \param allowJoinedLongValue_ Allow a long flag that accepts an argument to be passed its argument separated by the longseparator (ie. in the same argv field) | |
| * \param allowSeparateShortValue_ Allow a short flag that accepts an argument to be passed its argument separated by whitespace (ie. in the next argv field) | |
| * \param allowSeparateLongValue_ Allow a long flag that accepts an argument to be passed its argument separated by whitespace (ie. in the next argv field) | |
| */ | |
| void SetArgumentSeparations( | |
| const bool allowJoinedShortValue_, | |
| const bool allowJoinedLongValue_, | |
| const bool allowSeparateShortValue_, | |
| const bool allowSeparateLongValue_) | |
| { | |
| this->allowJoinedShortValue = allowJoinedShortValue_; | |
| this->allowJoinedLongValue = allowJoinedLongValue_; | |
| this->allowSeparateShortValue = allowSeparateShortValue_; | |
| this->allowSeparateLongValue = allowSeparateLongValue_; | |
| this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " "; | |
| this->helpParams.shortSeparator = allowJoinedShortValue ? "" : " "; | |
| } | |
| /** Pass the help menu into an ostream | |
| */ | |
| void Help(std::ostream &help_) const | |
| { | |
| auto &command = SelectedCommand(); | |
| const auto &commandDescription = command.Description().empty() ? command.Help() : command.Description(); | |
| const auto description_text = Wrap(commandDescription, helpParams.width - helpParams.descriptionindent); | |
| const auto epilog_text = Wrap(command.Epilog(), helpParams.width - helpParams.descriptionindent); | |
| const bool hasoptions = command.HasFlag(); | |
| const bool hasarguments = command.HasPositional(); | |
| std::vector<std::string> prognameline; | |
| prognameline.push_back(helpParams.usageString); | |
| prognameline.push_back(Prog()); | |
| auto commandProgLine = command.GetProgramLine(helpParams); | |
| prognameline.insert(prognameline.end(), commandProgLine.begin(), commandProgLine.end()); | |
| const auto proglines = Wrap(prognameline.begin(), prognameline.end(), | |
| helpParams.width - (helpParams.progindent + helpParams.progtailindent), | |
| helpParams.width - helpParams.progindent); | |
| auto progit = std::begin(proglines); | |
| if (progit != std::end(proglines)) | |
| { | |
| help_ << std::string(helpParams.progindent, ' ') << *progit << '\n'; | |
| ++progit; | |
| } | |
| for (; progit != std::end(proglines); ++progit) | |
| { | |
| help_ << std::string(helpParams.progtailindent, ' ') << *progit << '\n'; | |
| } | |
| help_ << '\n'; | |
| if (!description_text.empty()) | |
| { | |
| for (const auto &line: description_text) | |
| { | |
| help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n"; | |
| } | |
| help_ << "\n"; | |
| } | |
| bool lastDescriptionIsNewline = false; | |
| if (!helpParams.optionsString.empty()) | |
| { | |
| help_ << std::string(helpParams.progindent, ' ') << helpParams.optionsString << "\n\n"; | |
| } | |
| for (const auto &desc: command.GetDescription(helpParams, 0)) | |
| { | |
| lastDescriptionIsNewline = std::get<0>(desc).empty() && std::get<1>(desc).empty(); | |
| const auto groupindent = std::get<2>(desc) * helpParams.eachgroupindent; | |
| const auto flags = Wrap(std::get<0>(desc), helpParams.width - (helpParams.flagindent + helpParams.helpindent + helpParams.gutter)); | |
| const auto info = Wrap(std::get<1>(desc), helpParams.width - (helpParams.helpindent + groupindent)); | |
| std::string::size_type flagssize = 0; | |
| for (auto flagsit = std::begin(flags); flagsit != std::end(flags); ++flagsit) | |
| { | |
| if (flagsit != std::begin(flags)) | |
| { | |
| help_ << '\n'; | |
| } | |
| help_ << std::string(groupindent + helpParams.flagindent, ' ') << *flagsit; | |
| flagssize = Glyphs(*flagsit); | |
| } | |
| auto infoit = std::begin(info); | |
| // groupindent is on both sides of this inequality, and therefore can be removed | |
| if ((helpParams.flagindent + flagssize + helpParams.gutter) > helpParams.helpindent || infoit == std::end(info) || helpParams.addNewlineBeforeDescription) | |
| { | |
| help_ << '\n'; | |
| } else | |
| { | |
| // groupindent is on both sides of the minus sign, and therefore doesn't actually need to be in here | |
| help_ << std::string(helpParams.helpindent - (helpParams.flagindent + flagssize), ' ') << *infoit << '\n'; | |
| ++infoit; | |
| } | |
| for (; infoit != std::end(info); ++infoit) | |
| { | |
| help_ << std::string(groupindent + helpParams.helpindent, ' ') << *infoit << '\n'; | |
| } | |
| } | |
| if (hasoptions && hasarguments && helpParams.showTerminator) | |
| { | |
| lastDescriptionIsNewline = false; | |
| for (const auto &item: Wrap(std::string("\"") + terminator + "\" can be used to terminate flag options and force all following arguments to be treated as positional options", helpParams.width - helpParams.flagindent)) | |
| { | |
| help_ << std::string(helpParams.flagindent, ' ') << item << '\n'; | |
| } | |
| } | |
| if (!lastDescriptionIsNewline) | |
| { | |
| help_ << "\n"; | |
| } | |
| for (const auto &line: epilog_text) | |
| { | |
| help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n"; | |
| } | |
| } | |
| /** Generate a help menu as a string. | |
| * | |
| * \return the help text as a single string | |
| */ | |
| std::string Help() const | |
| { | |
| std::ostringstream help_; | |
| Help(help_); | |
| return help_.str(); | |
| } | |
| virtual void Reset() noexcept override | |
| { | |
| Command::Reset(); | |
| matched = true; | |
| readCompletion = false; | |
| } | |
| /** Parse all arguments. | |
| * | |
| * \param begin an iterator to the beginning of the argument list | |
| * \param end an iterator to the past-the-end element of the argument list | |
| * \return the iterator after the last parsed value. Only useful for kick-out | |
| */ | |
| template <typename It> | |
| It ParseArgs(It begin, It end) | |
| { | |
| // Reset all Matched statuses and errors | |
| Reset(); | |
| #ifdef ARGS_NOEXCEPT | |
| error = GetError(); | |
| if (error != Error::None) | |
| { | |
| return end; | |
| } | |
| #endif | |
| return Parse(begin, end); | |
| } | |
| /** Parse all arguments. | |
| * | |
| * \param args an iterable of the arguments | |
| * \return the iterator after the last parsed value. Only useful for kick-out | |
| */ | |
| template <typename T> | |
| auto ParseArgs(const T &args) -> decltype(std::begin(args)) | |
| { | |
| return ParseArgs(std::begin(args), std::end(args)); | |
| } | |
| /** Convenience function to parse the CLI from argc and argv | |
| * | |
| * Just assigns the program name and vectorizes arguments for passing into ParseArgs() | |
| * | |
| * \return whether or not all arguments were parsed. This works for detecting kick-out, but is generally useless as it can't do anything with it. | |
| */ | |
| bool ParseCLI(const int argc, const char * const * argv) | |
| { | |
| if (Prog().empty()) | |
| { | |
| Prog(argv[0]); | |
| } | |
| const std::vector<std::string> args(argv + 1, argv + argc); | |
| return ParseArgs(args) == std::end(args); | |
| } | |
| template <typename T> | |
| bool ParseCLI(const T &args) | |
| { | |
| return ParseArgs(args) == std::end(args); | |
| } | |
| }; | |
| inline Command::RaiiSubparser::RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_) | |
| : command(parser_.SelectedCommand()), parser(std::move(args_), parser_, command, parser_.helpParams), oldSubparser(command.subparser) | |
| { | |
| command.subparser = &parser; | |
| } | |
| inline Command::RaiiSubparser::RaiiSubparser(const Command &command_, const HelpParams ¶ms_): command(command_), parser(command, params_), oldSubparser(command.subparser) | |
| { | |
| command.subparser = &parser; | |
| } | |
| inline void Subparser::Parse() | |
| { | |
| isParsed = true; | |
| Reset(); | |
| command.subparserDescription = GetDescription(helpParams, 0); | |
| command.subparserHasFlag = HasFlag(); | |
| command.subparserHasPositional = HasPositional(); | |
| command.subparserHasCommand = HasCommand(); | |
| command.subparserProgramLine = GetProgramLine(helpParams); | |
| if (parser == nullptr) | |
| { | |
| #ifndef ARGS_NOEXCEPT | |
| throw args::SubparserError(); | |
| #else | |
| error = Error::Subparser; | |
| return; | |
| #endif | |
| } | |
| auto it = parser->Parse(args.begin(), args.end()); | |
| command.Validate(parser->ShortPrefix(), parser->LongPrefix()); | |
| kicked.assign(it, args.end()); | |
| #ifdef ARGS_NOEXCEPT | |
| command.subparserError = GetError(); | |
| #endif | |
| } | |
| inline std::ostream &operator<<(std::ostream &os, const ArgumentParser &parser) | |
| { | |
| parser.Help(os); | |
| return os; | |
| } | |
| /** Boolean argument matcher | |
| */ | |
| class Flag : public FlagBase | |
| { | |
| public: | |
| Flag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): FlagBase(name_, help_, std::move(matcher_), options_) | |
| { | |
| group_.Add(*this); | |
| } | |
| Flag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false): Flag(group_, name_, help_, std::move(matcher_), extraError_ ? Options::Single : Options::None) | |
| { | |
| } | |
| virtual ~Flag() {} | |
| /** Get whether this was matched | |
| */ | |
| bool Get() const | |
| { | |
| return Matched(); | |
| } | |
| virtual Nargs NumberOfArguments() const noexcept override | |
| { | |
| return 0; | |
| } | |
| virtual void ParseValue(const std::vector<std::string>&) override | |
| { | |
| } | |
| }; | |
| /** Help flag class | |
| * | |
| * Works like a regular flag, but throws an instance of Help when it is matched | |
| */ | |
| class HelpFlag : public Flag | |
| { | |
| public: | |
| HelpFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_ = {}): Flag(group_, name_, help_, std::move(matcher_), options_) {} | |
| virtual ~HelpFlag() {} | |
| virtual void ParseValue(const std::vector<std::string> &) | |
| { | |
| #ifdef ARGS_NOEXCEPT | |
| error = Error::Help; | |
| errorMsg = Name(); | |
| #else | |
| throw Help(Name()); | |
| #endif | |
| } | |
| /** Get whether this was matched | |
| */ | |
| bool Get() const noexcept | |
| { | |
| return Matched(); | |
| } | |
| }; | |
| /** A flag class that simply counts the number of times it's matched | |
| */ | |
| class CounterFlag : public Flag | |
| { | |
| private: | |
| const int startcount; | |
| int count; | |
| public: | |
| CounterFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const int startcount_ = 0, Options options_ = {}): | |
| Flag(group_, name_, help_, std::move(matcher_), options_), startcount(startcount_), count(startcount_) {} | |
| virtual ~CounterFlag() {} | |
| virtual FlagBase *Match(const EitherFlag &arg) override | |
| { | |
| auto me = FlagBase::Match(arg); | |
| if (me) | |
| { | |
| ++count; | |
| } | |
| return me; | |
| } | |
| /** Get the count | |
| */ | |
| int &Get() noexcept | |
| { | |
| return count; | |
| } | |
| virtual void Reset() noexcept override | |
| { | |
| FlagBase::Reset(); | |
| count = startcount; | |
| } | |
| }; | |
| /** A flag class that calls a function when it's matched | |
| */ | |
| class ActionFlag : public FlagBase | |
| { | |
| private: | |
| std::function<void(const std::vector<std::string> &)> action; | |
| Nargs nargs; | |
| public: | |
| ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Nargs nargs_, std::function<void(const std::vector<std::string> &)> action_, Options options_ = {}): | |
| FlagBase(name_, help_, std::move(matcher_), options_), action(std::move(action_)), nargs(nargs_) | |
| { | |
| group_.Add(*this); | |
| } | |
| ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void(const std::string &)> action_, Options options_ = {}): | |
| FlagBase(name_, help_, std::move(matcher_), options_), nargs(1) | |
| { | |
| group_.Add(*this); | |
| action = [action_](const std::vector<std::string> &a) { return action_(a.at(0)); }; | |
| } | |
| ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function<void()> action_, Options options_ = {}): | |
| FlagBase(name_, help_, std::move(matcher_), options_), nargs(0) | |
| { | |
| group_.Add(*this); | |
| action = [action_](const std::vector<std::string> &) { return action_(); }; | |
| } | |
| virtual Nargs NumberOfArguments() const noexcept override | |
| { return nargs; } | |
| virtual void ParseValue(const std::vector<std::string> &value) override | |
| { action(value); } | |
| }; | |
| /** A default Reader class for argument classes | |
| * | |
| * If destination type is assignable to std::string it uses an assignment to std::string. | |
| * Otherwise ValueReader simply uses a std::istringstream to read into the destination type, and | |
| * raises a ParseError if there are any characters left. | |
| */ | |
| struct ValueReader | |
| { | |
| template <typename T> | |
| typename std::enable_if<!std::is_assignable<T, std::string>::value, bool>::type | |
| operator ()(const std::string &name, const std::string &value, T &destination) | |
| { | |
| std::istringstream ss(value); | |
| ss >> destination >> std::ws; | |
| if (ss.rdbuf()->in_avail() > 0) | |
| { | |
| #ifdef ARGS_NOEXCEPT | |
| (void)name; | |
| return false; | |
| #else | |
| std::ostringstream problem; | |
| problem << "Argument '" << name << "' received invalid value type '" << value << "'"; | |
| throw ParseError(problem.str()); | |
| #endif | |
| } | |
| return true; | |
| } | |
| template <typename T> | |
| typename std::enable_if<std::is_assignable<T, std::string>::value, bool>::type | |
| operator()(const std::string &, const std::string &value, T &destination) | |
| { | |
| destination = value; | |
| return true; | |
| } | |
| }; | |
| /** An argument-accepting flag class | |
| * | |
| * \tparam T the type to extract the argument as | |
| * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) | |
| */ | |
| template < | |
| typename T, | |
| typename Reader = ValueReader> | |
| class ValueFlag : public ValueFlagBase | |
| { | |
| protected: | |
| T value; | |
| T defaultValue; | |
| virtual std::string GetDefaultString(const HelpParams&) const override | |
| { | |
| return detail::ToString(defaultValue); | |
| } | |
| private: | |
| Reader reader; | |
| public: | |
| ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_, Options options_): ValueFlagBase(name_, help_, std::move(matcher_), options_), value(defaultValue_), defaultValue(defaultValue_) | |
| { | |
| group_.Add(*this); | |
| } | |
| ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), const bool extraError_ = false): ValueFlag(group_, name_, help_, std::move(matcher_), defaultValue_, extraError_ ? Options::Single : Options::None) | |
| { | |
| } | |
| ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): ValueFlag(group_, name_, help_, std::move(matcher_), T(), options_) | |
| { | |
| } | |
| virtual ~ValueFlag() {} | |
| virtual void ParseValue(const std::vector<std::string> &values_) override | |
| { | |
| const std::string &value_ = values_.at(0); | |
| #ifdef ARGS_NOEXCEPT | |
| if (!reader(name, value_, this->value)) | |
| { | |
| error = Error::Parse; | |
| } | |
| #else | |
| reader(name, value_, this->value); | |
| #endif | |
| } | |
| virtual void Reset() noexcept override | |
| { | |
| ValueFlagBase::Reset(); | |
| value = defaultValue; | |
| } | |
| /** Get the value | |
| */ | |
| T &Get() noexcept | |
| { | |
| return value; | |
| } | |
| /** Get the default value | |
| */ | |
| const T &GetDefault() noexcept | |
| { | |
| return defaultValue; | |
| } | |
| }; | |
| /** An optional argument-accepting flag class | |
| * | |
| * \tparam T the type to extract the argument as | |
| * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) | |
| */ | |
| template < | |
| typename T, | |
| typename Reader = ValueReader> | |
| class ImplicitValueFlag : public ValueFlag<T, Reader> | |
| { | |
| protected: | |
| T implicitValue; | |
| public: | |
| ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &implicitValue_, const T &defaultValue_ = T(), Options options_ = {}) | |
| : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(implicitValue_) | |
| { | |
| } | |
| ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), Options options_ = {}) | |
| : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(defaultValue_) | |
| { | |
| } | |
| ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) | |
| : ValueFlag<T, Reader>(group_, name_, help_, std::move(matcher_), {}, options_), implicitValue() | |
| { | |
| } | |
| virtual ~ImplicitValueFlag() {} | |
| virtual Nargs NumberOfArguments() const noexcept override | |
| { | |
| return {0, 1}; | |
| } | |
| virtual void ParseValue(const std::vector<std::string> &value_) override | |
| { | |
| if (value_.empty()) | |
| { | |
| this->value = implicitValue; | |
| } else | |
| { | |
| ValueFlag<T, Reader>::ParseValue(value_); | |
| } | |
| } | |
| }; | |
| /** A variadic arguments accepting flag class | |
| * | |
| * \tparam T the type to extract the argument as | |
| * \tparam List the list type that houses the values | |
| * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) | |
| */ | |
| template < | |
| typename T, | |
| template <typename...> class List = std::vector, | |
| typename Reader = ValueReader> | |
| class NargsValueFlag : public FlagBase | |
| { | |
| protected: | |
| List<T> values; | |
| const List<T> defaultValues; | |
| Nargs nargs; | |
| Reader reader; | |
| public: | |
| typedef List<T> Container; | |
| typedef T value_type; | |
| typedef typename Container::allocator_type allocator_type; | |
| typedef typename Container::pointer pointer; | |
| typedef typename Container::const_pointer const_pointer; | |
| typedef T& reference; | |
| typedef const T& const_reference; | |
| typedef typename Container::size_type size_type; | |
| typedef typename Container::difference_type difference_type; | |
| typedef typename Container::iterator iterator; | |
| typedef typename Container::const_iterator const_iterator; | |
| typedef std::reverse_iterator<iterator> reverse_iterator; | |
| typedef std::reverse_iterator<const_iterator> const_reverse_iterator; | |
| NargsValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Nargs nargs_, const List<T> &defaultValues_ = {}, Options options_ = {}) | |
| : FlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_),nargs(nargs_) | |
| { | |
| group_.Add(*this); | |
| } | |
| virtual ~NargsValueFlag() {} | |
| virtual Nargs NumberOfArguments() const noexcept override | |
| { | |
| return nargs; | |
| } | |
| virtual void ParseValue(const std::vector<std::string> &values_) override | |
| { | |
| values.clear(); | |
| for (const std::string &value : values_) | |
| { | |
| T v; | |
| #ifdef ARGS_NOEXCEPT | |
| if (!reader(name, value, v)) | |
| { | |
| error = Error::Parse; | |
| } | |
| #else | |
| reader(name, value, v); | |
| #endif | |
| values.insert(std::end(values), v); | |
| } | |
| } | |
| List<T> &Get() noexcept | |
| { | |
| return values; | |
| } | |
| iterator begin() noexcept | |
| { | |
| return values.begin(); | |
| } | |
| const_iterator begin() const noexcept | |
| { | |
| return values.begin(); | |
| } | |
| const_iterator cbegin() const noexcept | |
| { | |
| return values.cbegin(); | |
| } | |
| iterator end() noexcept | |
| { | |
| return values.end(); | |
| } | |
| const_iterator end() const noexcept | |
| { | |
| return values.end(); | |
| } | |
| const_iterator cend() const noexcept | |
| { | |
| return values.cend(); | |
| } | |
| virtual void Reset() noexcept override | |
| { | |
| FlagBase::Reset(); | |
| values = defaultValues; | |
| } | |
| virtual FlagBase *Match(const EitherFlag &arg) override | |
| { | |
| const bool wasMatched = Matched(); | |
| auto me = FlagBase::Match(arg); | |
| if (me && !wasMatched) | |
| { | |
| values.clear(); | |
| } | |
| return me; | |
| } | |
| }; | |
| /** An argument-accepting flag class that pushes the found values into a list | |
| * | |
| * \tparam T the type to extract the argument as | |
| * \tparam List the list type that houses the values | |
| * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) | |
| */ | |
| template < | |
| typename T, | |
| template <typename...> class List = std::vector, | |
| typename Reader = ValueReader> | |
| class ValueFlagList : public ValueFlagBase | |
| { | |
| private: | |
| using Container = List<T>; | |
| Container values; | |
| const Container defaultValues; | |
| Reader reader; | |
| public: | |
| typedef T value_type; | |
| typedef typename Container::allocator_type allocator_type; | |
| typedef typename Container::pointer pointer; | |
| typedef typename Container::const_pointer const_pointer; | |
| typedef T& reference; | |
| typedef const T& const_reference; | |
| typedef typename Container::size_type size_type; | |
| typedef typename Container::difference_type difference_type; | |
| typedef typename Container::iterator iterator; | |
| typedef typename Container::const_iterator const_iterator; | |
| typedef std::reverse_iterator<iterator> reverse_iterator; | |
| typedef std::reverse_iterator<const_iterator> const_reverse_iterator; | |
| ValueFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Container &defaultValues_ = Container(), Options options_ = {}): | |
| ValueFlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), defaultValues(defaultValues_) | |
| { | |
| group_.Add(*this); | |
| } | |
| virtual ~ValueFlagList() {} | |
| virtual void ParseValue(const std::vector<std::string> &values_) override | |
| { | |
| const std::string &value_ = values_.at(0); | |
| T v; | |
| #ifdef ARGS_NOEXCEPT | |
| if (!reader(name, value_, v)) | |
| { | |
| error = Error::Parse; | |
| } | |
| #else | |
| reader(name, value_, v); | |
| #endif | |
| values.insert(std::end(values), v); | |
| } | |
| /** Get the values | |
| */ | |
| Container &Get() noexcept | |
| { | |
| return values; | |
| } | |
| virtual std::string Name() const override | |
| { | |
| return name + std::string("..."); | |
| } | |
| virtual void Reset() noexcept override | |
| { | |
| ValueFlagBase::Reset(); | |
| values = defaultValues; | |
| } | |
| virtual FlagBase *Match(const EitherFlag &arg) override | |
| { | |
| const bool wasMatched = Matched(); | |
| auto me = FlagBase::Match(arg); | |
| if (me && !wasMatched) | |
| { | |
| values.clear(); | |
| } | |
| return me; | |
| } | |
| iterator begin() noexcept | |
| { | |
| return values.begin(); | |
| } | |
| const_iterator begin() const noexcept | |
| { | |
| return values.begin(); | |
| } | |
| const_iterator cbegin() const noexcept | |
| { | |
| return values.cbegin(); | |
| } | |
| iterator end() noexcept | |
| { | |
| return values.end(); | |
| } | |
| const_iterator end() const noexcept | |
| { | |
| return values.end(); | |
| } | |
| const_iterator cend() const noexcept | |
| { | |
| return values.cend(); | |
| } | |
| }; | |
| /** A mapping value flag class | |
| * | |
| * \tparam K the type to extract the argument as | |
| * \tparam T the type to store the result as | |
| * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) | |
| * \tparam Map The Map type. Should operate like std::map or std::unordered_map | |
| */ | |
| template < | |
| typename K, | |
| typename T, | |
| typename Reader = ValueReader, | |
| template <typename...> class Map = std::unordered_map> | |
| class MapFlag : public ValueFlagBase | |
| { | |
| private: | |
| const Map<K, T> map; | |
| T value; | |
| const T defaultValue; | |
| Reader reader; | |
| protected: | |
| virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override | |
| { | |
| return detail::MapKeysToStrings(map); | |
| } | |
| public: | |
| MapFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const T &defaultValue_, Options options_): ValueFlagBase(name_, help_, std::move(matcher_), options_), map(map_), value(defaultValue_), defaultValue(defaultValue_) | |
| { | |
| group_.Add(*this); | |
| } | |
| MapFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const T &defaultValue_ = T(), const bool extraError_ = false): MapFlag(group_, name_, help_, std::move(matcher_), map_, defaultValue_, extraError_ ? Options::Single : Options::None) | |
| { | |
| } | |
| MapFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, Options options_): MapFlag(group_, name_, help_, std::move(matcher_), map_, T(), options_) | |
| { | |
| } | |
| virtual ~MapFlag() {} | |
| virtual void ParseValue(const std::vector<std::string> &values_) override | |
| { | |
| const std::string &value_ = values_.at(0); | |
| K key; | |
| #ifdef ARGS_NOEXCEPT | |
| if (!reader(name, value_, key)) | |
| { | |
| error = Error::Parse; | |
| } | |
| #else | |
| reader(name, value_, key); | |
| #endif | |
| auto it = map.find(key); | |
| if (it == std::end(map)) | |
| { | |
| std::ostringstream problem; | |
| problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; | |
| #ifdef ARGS_NOEXCEPT | |
| error = Error::Map; | |
| errorMsg = problem.str(); | |
| #else | |
| throw MapError(problem.str()); | |
| #endif | |
| } else | |
| { | |
| this->value = it->second; | |
| } | |
| } | |
| /** Get the value | |
| */ | |
| T &Get() noexcept | |
| { | |
| return value; | |
| } | |
| virtual void Reset() noexcept override | |
| { | |
| ValueFlagBase::Reset(); | |
| value = defaultValue; | |
| } | |
| }; | |
| /** A mapping value flag list class | |
| * | |
| * \tparam K the type to extract the argument as | |
| * \tparam T the type to store the result as | |
| * \tparam List the list type that houses the values | |
| * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) | |
| * \tparam Map The Map type. Should operate like std::map or std::unordered_map | |
| */ | |
| template < | |
| typename K, | |
| typename T, | |
| template <typename...> class List = std::vector, | |
| typename Reader = ValueReader, | |
| template <typename...> class Map = std::unordered_map> | |
| class MapFlagList : public ValueFlagBase | |
| { | |
| private: | |
| using Container = List<T>; | |
| const Map<K, T> map; | |
| Container values; | |
| const Container defaultValues; | |
| Reader reader; | |
| protected: | |
| virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override | |
| { | |
| return detail::MapKeysToStrings(map); | |
| } | |
| public: | |
| typedef T value_type; | |
| typedef typename Container::allocator_type allocator_type; | |
| typedef typename Container::pointer pointer; | |
| typedef typename Container::const_pointer const_pointer; | |
| typedef T& reference; | |
| typedef const T& const_reference; | |
| typedef typename Container::size_type size_type; | |
| typedef typename Container::difference_type difference_type; | |
| typedef typename Container::iterator iterator; | |
| typedef typename Container::const_iterator const_iterator; | |
| typedef std::reverse_iterator<iterator> reverse_iterator; | |
| typedef std::reverse_iterator<const_iterator> const_reverse_iterator; | |
| MapFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map<K, T> &map_, const Container &defaultValues_ = Container()): ValueFlagBase(name_, help_, std::move(matcher_)), map(map_), values(defaultValues_), defaultValues(defaultValues_) | |
| { | |
| group_.Add(*this); | |
| } | |
| virtual ~MapFlagList() {} | |
| virtual void ParseValue(const std::vector<std::string> &values_) override | |
| { | |
| const std::string &value = values_.at(0); | |
| K key; | |
| #ifdef ARGS_NOEXCEPT | |
| if (!reader(name, value, key)) | |
| { | |
| error = Error::Parse; | |
| } | |
| #else | |
| reader(name, value, key); | |
| #endif | |
| auto it = map.find(key); | |
| if (it == std::end(map)) | |
| { | |
| std::ostringstream problem; | |
| problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; | |
| #ifdef ARGS_NOEXCEPT | |
| error = Error::Map; | |
| errorMsg = problem.str(); | |
| #else | |
| throw MapError(problem.str()); | |
| #endif | |
| } else | |
| { | |
| this->values.emplace_back(it->second); | |
| } | |
| } | |
| /** Get the value | |
| */ | |
| Container &Get() noexcept | |
| { | |
| return values; | |
| } | |
| virtual std::string Name() const override | |
| { | |
| return name + std::string("..."); | |
| } | |
| virtual void Reset() noexcept override | |
| { | |
| ValueFlagBase::Reset(); | |
| values = defaultValues; | |
| } | |
| virtual FlagBase *Match(const EitherFlag &arg) override | |
| { | |
| const bool wasMatched = Matched(); | |
| auto me = FlagBase::Match(arg); | |
| if (me && !wasMatched) | |
| { | |
| values.clear(); | |
| } | |
| return me; | |
| } | |
| iterator begin() noexcept | |
| { | |
| return values.begin(); | |
| } | |
| const_iterator begin() const noexcept | |
| { | |
| return values.begin(); | |
| } | |
| const_iterator cbegin() const noexcept | |
| { | |
| return values.cbegin(); | |
| } | |
| iterator end() noexcept | |
| { | |
| return values.end(); | |
| } | |
| const_iterator end() const noexcept | |
| { | |
| return values.end(); | |
| } | |
| const_iterator cend() const noexcept | |
| { | |
| return values.cend(); | |
| } | |
| }; | |
| /** A positional argument class | |
| * | |
| * \tparam T the type to extract the argument as | |
| * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) | |
| */ | |
| template < | |
| typename T, | |
| typename Reader = ValueReader> | |
| class Positional : public PositionalBase | |
| { | |
| private: | |
| T value; | |
| const T defaultValue; | |
| Reader reader; | |
| public: | |
| Positional(Group &group_, const std::string &name_, const std::string &help_, const T &defaultValue_ = T(), Options options_ = {}): PositionalBase(name_, help_, options_), value(defaultValue_), defaultValue(defaultValue_) | |
| { | |
| group_.Add(*this); | |
| } | |
| Positional(Group &group_, const std::string &name_, const std::string &help_, Options options_): Positional(group_, name_, help_, T(), options_) | |
| { | |
| } | |
| virtual ~Positional() {} | |
| virtual void ParseValue(const std::string &value_) override | |
| { | |
| #ifdef ARGS_NOEXCEPT | |
| if (!reader(name, value_, this->value)) | |
| { | |
| error = Error::Parse; | |
| } | |
| #else | |
| reader(name, value_, this->value); | |
| #endif | |
| ready = false; | |
| matched = true; | |
| } | |
| /** Get the value | |
| */ | |
| T &Get() noexcept | |
| { | |
| return value; | |
| } | |
| virtual void Reset() noexcept override | |
| { | |
| PositionalBase::Reset(); | |
| value = defaultValue; | |
| } | |
| }; | |
| /** A positional argument class that pushes the found values into a list | |
| * | |
| * \tparam T the type to extract the argument as | |
| * \tparam List the list type that houses the values | |
| * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) | |
| */ | |
| template < | |
| typename T, | |
| template <typename...> class List = std::vector, | |
| typename Reader = ValueReader> | |
| class PositionalList : public PositionalBase | |
| { | |
| private: | |
| using Container = List<T>; | |
| Container values; | |
| const Container defaultValues; | |
| Reader reader; | |
| public: | |
| typedef T value_type; | |
| typedef typename Container::allocator_type allocator_type; | |
| typedef typename Container::pointer pointer; | |
| typedef typename Container::const_pointer const_pointer; | |
| typedef T& reference; | |
| typedef const T& const_reference; | |
| typedef typename Container::size_type size_type; | |
| typedef typename Container::difference_type difference_type; | |
| typedef typename Container::iterator iterator; | |
| typedef typename Container::const_iterator const_iterator; | |
| typedef std::reverse_iterator<iterator> reverse_iterator; | |
| typedef std::reverse_iterator<const_iterator> const_reverse_iterator; | |
| PositionalList(Group &group_, const std::string &name_, const std::string &help_, const Container &defaultValues_ = Container(), Options options_ = {}): PositionalBase(name_, help_, options_), values(defaultValues_), defaultValues(defaultValues_) | |
| { | |
| group_.Add(*this); | |
| } | |
| PositionalList(Group &group_, const std::string &name_, const std::string &help_, Options options_): PositionalList(group_, name_, help_, {}, options_) | |
| { | |
| } | |
| virtual ~PositionalList() {} | |
| virtual void ParseValue(const std::string &value_) override | |
| { | |
| T v; | |
| #ifdef ARGS_NOEXCEPT | |
| if (!reader(name, value_, v)) | |
| { | |
| error = Error::Parse; | |
| } | |
| #else | |
| reader(name, value_, v); | |
| #endif | |
| values.insert(std::end(values), v); | |
| matched = true; | |
| } | |
| virtual std::string Name() const override | |
| { | |
| return name + std::string("..."); | |
| } | |
| /** Get the values | |
| */ | |
| Container &Get() noexcept | |
| { | |
| return values; | |
| } | |
| virtual void Reset() noexcept override | |
| { | |
| PositionalBase::Reset(); | |
| values = defaultValues; | |
| } | |
| virtual PositionalBase *GetNextPositional() override | |
| { | |
| const bool wasMatched = Matched(); | |
| auto me = PositionalBase::GetNextPositional(); | |
| if (me && !wasMatched) | |
| { | |
| values.clear(); | |
| } | |
| return me; | |
| } | |
| iterator begin() noexcept | |
| { | |
| return values.begin(); | |
| } | |
| const_iterator begin() const noexcept | |
| { | |
| return values.begin(); | |
| } | |
| const_iterator cbegin() const noexcept | |
| { | |
| return values.cbegin(); | |
| } | |
| iterator end() noexcept | |
| { | |
| return values.end(); | |
| } | |
| const_iterator end() const noexcept | |
| { | |
| return values.end(); | |
| } | |
| const_iterator cend() const noexcept | |
| { | |
| return values.cend(); | |
| } | |
| }; | |
| /** A positional argument mapping class | |
| * | |
| * \tparam K the type to extract the argument as | |
| * \tparam T the type to store the result as | |
| * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) | |
| * \tparam Map The Map type. Should operate like std::map or std::unordered_map | |
| */ | |
| template < | |
| typename K, | |
| typename T, | |
| typename Reader = ValueReader, | |
| template <typename...> class Map = std::unordered_map> | |
| class MapPositional : public PositionalBase | |
| { | |
| private: | |
| const Map<K, T> map; | |
| T value; | |
| const T defaultValue; | |
| Reader reader; | |
| protected: | |
| virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override | |
| { | |
| return detail::MapKeysToStrings(map); | |
| } | |
| public: | |
| MapPositional(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const T &defaultValue_ = T(), Options options_ = {}): | |
| PositionalBase(name_, help_, options_), map(map_), value(defaultValue_), defaultValue(defaultValue_) | |
| { | |
| group_.Add(*this); | |
| } | |
| virtual ~MapPositional() {} | |
| virtual void ParseValue(const std::string &value_) override | |
| { | |
| K key; | |
| #ifdef ARGS_NOEXCEPT | |
| if (!reader(name, value_, key)) | |
| { | |
| error = Error::Parse; | |
| } | |
| #else | |
| reader(name, value_, key); | |
| #endif | |
| auto it = map.find(key); | |
| if (it == std::end(map)) | |
| { | |
| std::ostringstream problem; | |
| problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; | |
| #ifdef ARGS_NOEXCEPT | |
| error = Error::Map; | |
| errorMsg = problem.str(); | |
| #else | |
| throw MapError(problem.str()); | |
| #endif | |
| } else | |
| { | |
| this->value = it->second; | |
| ready = false; | |
| matched = true; | |
| } | |
| } | |
| /** Get the value | |
| */ | |
| T &Get() noexcept | |
| { | |
| return value; | |
| } | |
| virtual void Reset() noexcept override | |
| { | |
| PositionalBase::Reset(); | |
| value = defaultValue; | |
| } | |
| }; | |
| /** A positional argument mapping list class | |
| * | |
| * \tparam K the type to extract the argument as | |
| * \tparam T the type to store the result as | |
| * \tparam List the list type that houses the values | |
| * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) | |
| * \tparam Map The Map type. Should operate like std::map or std::unordered_map | |
| */ | |
| template < | |
| typename K, | |
| typename T, | |
| template <typename...> class List = std::vector, | |
| typename Reader = ValueReader, | |
| template <typename...> class Map = std::unordered_map> | |
| class MapPositionalList : public PositionalBase | |
| { | |
| private: | |
| using Container = List<T>; | |
| const Map<K, T> map; | |
| Container values; | |
| const Container defaultValues; | |
| Reader reader; | |
| protected: | |
| virtual std::vector<std::string> GetChoicesStrings(const HelpParams &) const override | |
| { | |
| return detail::MapKeysToStrings(map); | |
| } | |
| public: | |
| typedef T value_type; | |
| typedef typename Container::allocator_type allocator_type; | |
| typedef typename Container::pointer pointer; | |
| typedef typename Container::const_pointer const_pointer; | |
| typedef T& reference; | |
| typedef const T& const_reference; | |
| typedef typename Container::size_type size_type; | |
| typedef typename Container::difference_type difference_type; | |
| typedef typename Container::iterator iterator; | |
| typedef typename Container::const_iterator const_iterator; | |
| typedef std::reverse_iterator<iterator> reverse_iterator; | |
| typedef std::reverse_iterator<const_iterator> const_reverse_iterator; | |
| MapPositionalList(Group &group_, const std::string &name_, const std::string &help_, const Map<K, T> &map_, const Container &defaultValues_ = Container(), Options options_ = {}): | |
| PositionalBase(name_, help_, options_), map(map_), values(defaultValues_), defaultValues(defaultValues_) | |
| { | |
| group_.Add(*this); | |
| } | |
| virtual ~MapPositionalList() {} | |
| virtual void ParseValue(const std::string &value_) override | |
| { | |
| K key; | |
| #ifdef ARGS_NOEXCEPT | |
| if (!reader(name, value_, key)) | |
| { | |
| error = Error::Parse; | |
| } | |
| #else | |
| reader(name, value_, key); | |
| #endif | |
| auto it = map.find(key); | |
| if (it == std::end(map)) | |
| { | |
| std::ostringstream problem; | |
| problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; | |
| #ifdef ARGS_NOEXCEPT | |
| error = Error::Map; | |
| errorMsg = problem.str(); | |
| #else | |
| throw MapError(problem.str()); | |
| #endif | |
| } else | |
| { | |
| this->values.emplace_back(it->second); | |
| matched = true; | |
| } | |
| } | |
| /** Get the value | |
| */ | |
| Container &Get() noexcept | |
| { | |
| return values; | |
| } | |
| virtual std::string Name() const override | |
| { | |
| return name + std::string("..."); | |
| } | |
| virtual void Reset() noexcept override | |
| { | |
| PositionalBase::Reset(); | |
| values = defaultValues; | |
| } | |
| virtual PositionalBase *GetNextPositional() override | |
| { | |
| const bool wasMatched = Matched(); | |
| auto me = PositionalBase::GetNextPositional(); | |
| if (me && !wasMatched) | |
| { | |
| values.clear(); | |
| } | |
| return me; | |
| } | |
| iterator begin() noexcept | |
| { | |
| return values.begin(); | |
| } | |
| const_iterator begin() const noexcept | |
| { | |
| return values.begin(); | |
| } | |
| const_iterator cbegin() const noexcept | |
| { | |
| return values.cbegin(); | |
| } | |
| iterator end() noexcept | |
| { | |
| return values.end(); | |
| } | |
| const_iterator end() const noexcept | |
| { | |
| return values.end(); | |
| } | |
| const_iterator cend() const noexcept | |
| { | |
| return values.cend(); | |
| } | |
| }; | |
| } | |
| #endif |