#ifndef APPLICATION_UTILITIES_ARGUMENTPARSER_H #define APPLICATION_UTILITIES_ARGUMENTPARSER_H #include "./global.h" #include #include #include #ifdef DEBUG_BUILD # include #endif class ArgumentParserTests; namespace ApplicationUtilities { LIB_EXPORT extern const char *applicationName; LIB_EXPORT extern const char *applicationAuthor; LIB_EXPORT extern const char *applicationVersion; LIB_EXPORT extern const char *applicationUrl; #define SET_APPLICATION_INFO \ ::ApplicationUtilities::applicationName = APP_NAME; \ ::ApplicationUtilities::applicationAuthor = APP_AUTHOR; \ ::ApplicationUtilities::applicationVersion = APP_VERSION; \ ::ApplicationUtilities::applicationUrl = APP_URL class Argument; class ArgumentParser; typedef std::initializer_list ArgumentInitializerList; typedef std::vector ArgumentVector; typedef std::function ArgumentPredicate; /*! * \brief The UnknownArgumentBehavior enum specifies the behavior of the argument parser when an unknown * argument is detected. */ enum class UnknownArgumentBehavior { Ignore, /**< Unknown arguments are ignored without warnings. */ Warn, /**< A warning is printed to std::cerr if an unknown argument is detected. */ Fail /**< Further parsing is aborted and an ApplicationUtilities::Failure instance with an error message is thrown. */ }; /*! * \brief The ValueCompletionBehavior enum specifies the items to be considered when generating completion for an argument value. */ enum class ValueCompletionBehavior : unsigned char { None = 0, PreDefinedValues = 2, Files = 4, Directories = 8, FileSystemIfNoPreDefinedValues = 16, AppendEquationSign = 32 }; constexpr ValueCompletionBehavior operator|(ValueCompletionBehavior lhs, ValueCompletionBehavior rhs) { return static_cast(static_cast(lhs) | static_cast(rhs)); } constexpr bool operator&(ValueCompletionBehavior lhs, ValueCompletionBehavior rhs) { return static_cast(static_cast(lhs) & static_cast(rhs)); } Argument LIB_EXPORT *firstPresentUncombinableArg(const ArgumentVector &args, const Argument *except); struct LIB_EXPORT ArgumentOccurance { ArgumentOccurance(std::size_t index); ArgumentOccurance(std::size_t index, const std::vector parentPath, Argument *parent); std::size_t index; std::vector values; std::vector path; }; inline ArgumentOccurance::ArgumentOccurance(std::size_t index) : index(index) {} inline ArgumentOccurance::ArgumentOccurance(std::size_t index, const std::vector parentPath, Argument *parent) : index(index), path(parentPath) { if(parent) { path.push_back(parent); } } class LIB_EXPORT Argument { friend class ArgumentParser; public: typedef std::function &)> CallbackFunction; Argument(const char *name, char abbreviation = '\0', const char *description = nullptr, const char *example = nullptr); ~Argument(); const char *name() const; void setName(const char *name); char abbreviation() const; void setAbbreviation(char abbreviation); const char *description() const; void setDescription(const char *description); const char *example() const; void setExample(const char *example); const std::vector &values(std::size_t occurrance = 0) const; std::size_t requiredValueCount() const; void setRequiredValueCount(std::size_t requiredValueCount); const std::vector &valueNames() const; void setValueNames(std::initializer_list valueNames); void appendValueName(const char *valueName); bool allRequiredValuesPresent(std::size_t occurrance = 0) const; bool isPresent() const; std::size_t occurrences() const; std::size_t index(std::size_t occurrance) const; std::size_t minOccurrences() const; std::size_t maxOccurrences() const; void setConstraints(std::size_t minOccurrences, std::size_t maxOccurrences); const std::vector &path(std::size_t occurrance = 0) const; bool isRequired() const; void setRequired(bool required); bool isCombinable() const; void setCombinable(bool value); bool isImplicit() const; void setImplicit(bool value); bool denotesOperation() const; void setDenotesOperation(bool denotesOperation); void setCallback(CallbackFunction callback); void printInfo(std::ostream &os, unsigned char indentionLevel = 0) const; const ArgumentVector &subArguments() const; void setSubArguments(const ArgumentInitializerList &subArguments); void addSubArgument(Argument *arg); bool hasSubArguments() const; const ArgumentVector parents() const; bool isMainArgument() const; bool isParentPresent() const; ValueCompletionBehavior valueCompletionBehaviour() const; void setValueCompletionBehavior(ValueCompletionBehavior valueCompletionBehaviour); const char *preDefinedCompletionValues() const; void setPreDefinedCompletionValues(const char *preDefinedCompletionValues); Argument *conflictsWithArgument() const; void reset(); private: const char *m_name; char m_abbreviation; const char *m_description; const char *m_example; std::size_t m_minOccurrences; std::size_t m_maxOccurrences; bool m_combinable; bool m_denotesOperation; std::size_t m_requiredValueCount; std::vector m_valueNames; bool m_implicit; std::vector m_occurances; ArgumentVector m_subArgs; CallbackFunction m_callbackFunction; ArgumentVector m_parents; bool m_isMainArg; ValueCompletionBehavior m_valueCompletionBehavior; const char *m_preDefinedCompletionValues; }; class LIB_EXPORT ArgumentParser { friend ArgumentParserTests; public: ArgumentParser(); const ArgumentVector &mainArguments() const; void setMainArguments(const ArgumentInitializerList &mainArguments); void addMainArgument(Argument *argument); void printHelp(std::ostream &os) const; Argument *findArg(const ArgumentPredicate &predicate) const; static Argument *findArg(const ArgumentVector &arguments, const ArgumentPredicate &predicate); void parseArgs(int argc, const char *const *argv); unsigned int actualArgumentCount() const; const char *executable() const; UnknownArgumentBehavior unknownArgumentBehavior() const; void setUnknownArgumentBehavior(UnknownArgumentBehavior behavior); Argument *defaultArgument() const; void setDefaultArgument(Argument *argument); private: IF_DEBUG_BUILD(void verifyArgs(const ArgumentVector &args);) void readSpecifiedArgs(ArgumentVector &args, std::size_t &index, const char *const *&argv, const char *const *end, Argument *&lastArg, bool completionMode = false); void printBashCompletion(int argc, const char * const *argv, unsigned int cursorPos, const Argument *lastDetectedArg); void checkConstraints(const ArgumentVector &args); void invokeCallbacks(const ArgumentVector &args); ArgumentVector m_mainArgs; unsigned int m_actualArgc; const char *m_executable; UnknownArgumentBehavior m_unknownArgBehavior; Argument *m_defaultArg; }; /*! * \brief Returns the name of the argument. * * The parser compares the name with the characters following a "--" prefix to * identify arguments. */ inline const char *Argument::name() const { return m_name; } /*! * \brief Sets the name of the argument. * * The name mustn't be empty or contain white spaces or equation chars. * * The parser compares the name with the characters following a "--" prefix to * identify arguments. */ inline void Argument::setName(const char *name) { #ifdef DEBUG_BUILD if(name && *name) { for(const char *c = name; *c; ++c) { assert(*c != ' ' && *c != '='); } } #endif m_name = name; } /*! * \brief Returns the abbreviation of the argument. * * The parser compares the abbreviation with the characters following a "-" prefix to * identify arguments. */ inline char Argument::abbreviation() const { return m_abbreviation; } /*! * \brief Sets the abbreviation of the argument. * * The abbreviation might be empty but mustn't contain any white spaces or * equation chars when provided. * * The parser compares the abbreviation with the characters following a "-" prefix to * identify arguments. */ inline void Argument::setAbbreviation(char abbreviation) { IF_DEBUG_BUILD(assert(abbreviation != ' ' && abbreviation != '=')); m_abbreviation = abbreviation; } /*! * \brief Returns the description of the argument. * * The parser uses the description when printing help information. */ inline const char *Argument::description() const { return m_description; } /*! * \brief Sets the description of the argument. * * The parser uses the description when printing help information. */ inline void Argument::setDescription(const char *description) { m_description = description; } /*! * \brief Returns the usage example of the argument. * * The parser uses the description when printing help information. */ inline const char *Argument::example() const { return m_example; } /*! * \brief Sets the a usage example for the argument. * * The parser uses the description when printing help information. */ inline void Argument::setExample(const char *example) { m_example = example; } /*! * \brief Returns the additional values for the argument. * * These values set by the parser when parsing the command line arguments. */ inline const std::vector &Argument::values(std::size_t occurrance) const { return m_occurances[occurrance].values; } /*! * \brief Returns the number of values which are required to be given * for this argument. * * The parser will expect that many values when parsing command line arguments. * A negative value indicates a variable number of arguments to be expected. * * The default value is 0. * * \sa setRequiredValueCount() * \sa valueNames() * \sa setValueNames() */ inline std::size_t Argument::requiredValueCount() const { return m_requiredValueCount; } /*! * \brief Sets the number of values which are required to be given * for this argument. * * The parser will expect that many values when parsing command line arguments. * A negative value indicates a variable number of arguments to be expected. * * \sa requiredValueCount() * \sa valueNames() * \sa setValueNames() */ inline void Argument::setRequiredValueCount(std::size_t requiredValueCount) { m_requiredValueCount = requiredValueCount; } /*! * \brief Returns the names of the requried values. * * These names will be shown when printing information about the argument. * * \sa setValueNames() * \sa appendValueNames() */ inline const std::vector &Argument::valueNames() const { return m_valueNames; } /*! * \brief Sets the names of the requried values. These names will be used * when printing information about the argument. * * If the number of value names is higher then the number of requried values * the additional value names will be ignored. * If the number of value names is lesser then the number of requried values * generic values will be used for the missing names. * * \sa appendValueName() * \sa valueNames() * \sa requiredValueCount() */ inline void Argument::setValueNames(std::initializer_list valueNames) { m_valueNames.assign(valueNames); } /*! * \brief Appends a value name. The value names names will be shown * when printing information about the argument. * \sa setValueNames() * \sa valueNames() */ inline void Argument::appendValueName(const char *valueName) { m_valueNames.emplace_back(valueName); } /*! * \brief Returns an indication whether all required values are present. */ inline bool Argument::allRequiredValuesPresent(std::size_t occurrance) const { return m_requiredValueCount == static_cast(-1) || (m_occurances[occurrance].values.size() >= static_cast(m_requiredValueCount)); } /*! * \brief Returns an indication whether the argument is an implicit argument. * \sa setImplicit() */ inline bool Argument::isImplicit() const { return m_implicit; } /*! * \brief Sets whether the argument is an implicit argument. * \sa isImplicit() */ inline void Argument::setImplicit(bool implicit) { m_implicit = implicit; } /*! * \brief Returns an indication whether the argument could be detected when parsing. */ inline bool Argument::isPresent() const { return !m_occurances.empty(); } /*! * \brief Returns how often the argument could be detected when parsing. */ inline std::size_t Argument::occurrences() const { return m_occurances.size(); } /*! * \brief Returns the indices of the argument's occurences which could be detected when parsing. */ inline std::size_t Argument::index(std::size_t occurrance) const { return m_occurances[occurrance].index; } /*! * \brief Returns the minimum number of occurrences. * * If the argument occurs not that many times, the parser will complain. */ inline std::size_t Argument::minOccurrences() const { return m_minOccurrences; } /*! * \brief Returns the maximum number of occurrences. * * If the argument occurs more often, the parser will complain. */ inline std::size_t Argument::maxOccurrences() const { return m_maxOccurrences; } /*! * \brief Sets the allowed number of occurrences. * \sa minOccurrences() * \sa maxOccurrences() */ inline void Argument::setConstraints(std::size_t minOccurrences, std::size_t maxOccurrences) { m_minOccurrences = minOccurrences; m_maxOccurrences = maxOccurrences; } /*! * \brief Returns the path of the specified \a occurrance. */ inline const std::vector &Argument::path(std::size_t occurrance) const { return m_occurances[occurrance].path; } /*! * \brief Returns an indication whether the argument is mandatory. * * The parser will complain if a mandatory argument is not present. * * The default value is false. * * \sa setRequired() */ inline bool Argument::isRequired() const { return m_minOccurrences; } /*! * \brief Sets whether this argument is mandatory or not. * * The parser will complain if a mandatory argument is not present. * * * \sa isRequired() */ inline void Argument::setRequired(bool required) { if(required) { if(!m_minOccurrences) { m_minOccurrences = 1; } } else { m_minOccurrences = 0; } } /*! * \brief Returns an indication whether the argument is combinable. * * The parser will complain if two arguments labeled as uncombinable are * present at the same time. * * \sa setCombinable() */ inline bool Argument::isCombinable() const { return m_combinable; } /*! * \brief Sets if this argument can be combined. * * The parser will complain if two arguments labeled as uncombinable are * present at the same time. * * \sa isCombinable() */ inline void Argument::setCombinable(bool value) { m_combinable = value; } /*! * \brief Returns whether the argument denotes the operation. * * An argument which denotes the operation might be specified * withouth "--" or "-" prefix as first main argument. * * The default value is false. * * \sa setDenotesOperation() */ inline bool Argument::denotesOperation() const { return m_denotesOperation; } /*! * \brief Sets whether the argument denotes the operation. * \sa denotesOperation() */ inline void Argument::setDenotesOperation(bool denotesOperation) { m_denotesOperation = denotesOperation; } /*! * \brief Sets a \a callback function which will be called by the parser if * the argument could be found and no parsing errors occured. * \remarks The \a callback will be called for each occurrance of the argument. */ inline void Argument::setCallback(Argument::CallbackFunction callback) { m_callbackFunction = callback; } /*! * \brief Returns the secondary arguments for this argument. * * \sa setSecondaryArguments() * \sa hasSecondaryArguments() */ inline const ArgumentVector &Argument::subArguments() const { return m_subArgs; } /*! * \brief Returns an indication whether the argument has secondary arguments. * * \sa secondaryArguments() * \sa setSecondaryArguments() */ inline bool Argument::hasSubArguments() const { return !m_subArgs.empty(); } /*! * \brief Returns the parents of this argument. * * If this argument is used as secondary argument, the arguments which * contain this argument as secondary arguments are returned * as "parents" of this argument. * * If this argument is used as a main argument shouldn't be used as * secondary argument at the same time and thus have no parents. */ inline const ArgumentVector Argument::parents() const { return m_parents; } /*! * \brief Returns an indication whether the argument is used as main argument. * * An argument used as main argument shouldn't be used as secondary * arguments at the same time. */ inline bool Argument::isMainArgument() const { return m_isMainArg; } /*! * \brief Returns the items to be considered when generating completion for the values. */ inline ValueCompletionBehavior Argument::valueCompletionBehaviour() const { return m_valueCompletionBehavior; } /*! * \brief Sets the items to be considered when generating completion for the values. */ inline void Argument::setValueCompletionBehavior(ValueCompletionBehavior completionValues) { m_valueCompletionBehavior = completionValues; } /*! * \brief Returns the assigned values used when generating completion for the values. */ inline const char *Argument::preDefinedCompletionValues() const { return m_preDefinedCompletionValues; } /*! * \brief Assignes the values to be used when generating completion for the values. */ inline void Argument::setPreDefinedCompletionValues(const char *preDefinedCompletionValues) { m_preDefinedCompletionValues = preDefinedCompletionValues; } /*! * \brief Resets occurrences (indices, values and paths). */ inline void Argument::reset() { m_occurances.clear(); } /*! * \brief Returns the main arguments. * \sa setMainArguments() */ inline const ArgumentVector &ArgumentParser::mainArguments() const { return m_mainArgs; } /*! * \brief Returns the actual number of arguments that could be found when parsing. */ inline unsigned int ArgumentParser::actualArgumentCount() const { return m_actualArgc; } /*! * \brief Returns the name of the current executable. */ inline const char *ArgumentParser::executable() const { return m_executable; } /*! * \brief Returns how unknown arguments are treated. * * The default value is UnknownArgumentBehavior::Fail. */ inline UnknownArgumentBehavior ArgumentParser::unknownArgumentBehavior() const { return m_unknownArgBehavior; } /*! * \brief Sets how unknown arguments are treated. * * The default value is UnknownArgumentBehavior::Fail. */ inline void ArgumentParser::setUnknownArgumentBehavior(UnknownArgumentBehavior behavior) { m_unknownArgBehavior = behavior; } /*! * \brief Returns the default argument. * \remarks The default argument is assumed to be present if no other arguments have been specified. */ inline Argument *ArgumentParser::defaultArgument() const { return m_defaultArg; } /*! * \brief Sets the default argument. * \remarks The default argument is assumed to be present if no other arguments have been specified. */ inline void ArgumentParser::setDefaultArgument(Argument *argument) { m_defaultArg = argument; } class LIB_EXPORT HelpArgument : public Argument { public: HelpArgument(ArgumentParser &parser); }; } #endif // APPLICATION_UTILITIES_ARGUMENTPARSER_H