From 4c40004f0b2f7002658951f6fac52fe10677b388 Mon Sep 17 00:00:00 2001 From: Martchus Date: Sun, 2 Oct 2016 21:53:58 +0200 Subject: [PATCH] Improve CLI utils - Add ArgumentParser::readArgs() - Add Indentation - Fix eraseLine() --- application/argumentparser.cpp | 63 +++++++++++++++++++--------------- application/argumentparser.h | 50 ++++++++++++++++++++++++++- application/commandlineutils.h | 30 ++++++++++++++++ io/ansiescapecodes.h | 2 +- 4 files changed, 115 insertions(+), 30 deletions(-) diff --git a/application/argumentparser.cpp b/application/argumentparser.cpp index 0116256..24f81ea 100644 --- a/application/argumentparser.cpp +++ b/application/argumentparser.cpp @@ -12,9 +12,6 @@ #include #include #include -#ifdef LOGGING_ENABLED -# include -#endif using namespace std; using namespace std::placeholders; @@ -110,9 +107,9 @@ const char *Argument::firstValue() const /*! * \brief Writes the name, the abbreviation and other information about the Argument to the give ostream. */ -void Argument::printInfo(ostream &os, unsigned char indentionLevel) const +void Argument::printInfo(ostream &os, unsigned char indentation) const { - for(unsigned char i = 0; i < indentionLevel; ++i) os << ' ' << ' '; + os << Indentation(indentation); EscapeCodes::setStyle(os, EscapeCodes::TextAttribute::Bold); if(notEmpty(name())) { os << '-' << '-' << name(); @@ -138,24 +135,25 @@ void Argument::printInfo(ostream &os, unsigned char indentionLevel) const } } } - ++indentionLevel; + indentation += 2; if(notEmpty(description())) { - os << endl; - for(unsigned char i = 0; i < indentionLevel; ++i) os << ' ' << ' '; - os << description(); + os << '\n' << Indentation(indentation) << description(); } if(isRequired()) { - os << endl; - for(unsigned char i = 0; i < indentionLevel; ++i) os << ' ' << ' '; - os << "This argument is required."; + os << '\n' << Indentation(indentation) << "particularities: mandatory"; + if(!isMainArgument()) { + os << " if parent argument is present"; + } + } + if(environmentVariable()) { + os << '\n' << Indentation(indentation) << "default environment variable: " << environmentVariable(); } if(notEmpty(example())) { - for(unsigned char i = 0; i < indentionLevel; ++i) os << ' ' << ' '; - os << endl << "Usage: " << example(); + os << '\n' << Indentation(indentation) << "\nusage: " << example(); } - os << endl; + os << '\n'; for(const auto *arg : subArguments()) { - arg->printInfo(os, indentionLevel + 1); + arg->printInfo(os, indentation); } } @@ -365,21 +363,32 @@ void ArgumentParser::printHelp(ostream &os) const * \remarks * - The results are stored in the Argument instances assigned as main arguments and sub arguments. * - Calls the assigned callbacks if no constraints are violated. - * \throws Throws Failure if the specified arguments violate the constraints defined + * \throws Throws Failure if the specified arguments are invalid or violate the constraints defined * by the Argument instances. + * \sa readArgs() */ void ArgumentParser::parseArgs(int argc, const char *const *argv) { -#ifdef LOGGING_ENABLED - { - fstream logFile("/tmp/args.log", ios_base::out); - for(const char *const *i = argv, *const *end = argv + argc; i != end; ++i) { - logFile << *i << '\n'; - } + readArgs(argc, argv); + if(argc) { + checkConstraints(m_mainArgs); + invokeCallbacks(m_mainArgs); } -#endif +} + +/*! + * \brief Parses the specified command line arguments. + * \remarks + * - The results are stored in the Argument instances assigned as main arguments and sub arguments. + * - In contrast to parseArgs() this method does not check whether constraints are violated and it + * does not call any callbacks. + * \throws Throws Failure if the specified arguments are invalid. + * \sa readArgs() + */ +void ArgumentParser::readArgs(int argc, const char * const *argv) +{ IF_DEBUG_BUILD(verifyArgs(m_mainArgs);) - m_actualArgc = 0; + m_actualArgc = 0; if(argc) { // the first argument is the executable name m_executable = *argv; @@ -395,7 +404,7 @@ void ArgumentParser::parseArgs(int argc, const char *const *argv) currentWordIndex = (--argc ? stringToNumber(*(++argv)) : 0); ++argv, --argc; } catch(const ConversionException &) { - currentWordIndex = argc - 1; + currentWordIndex = static_cast(argc - 1); } } @@ -423,8 +432,6 @@ void ArgumentParser::parseArgs(int argc, const char *const *argv) m_defaultArg->m_occurrences.emplace_back(0); } } - checkConstraints(m_mainArgs); - invokeCallbacks(m_mainArgs); } else { m_executable = nullptr; } diff --git a/application/argumentparser.h b/application/argumentparser.h index cbea491..22f8205 100644 --- a/application/argumentparser.h +++ b/application/argumentparser.h @@ -163,7 +163,7 @@ public: bool denotesOperation() const; void setDenotesOperation(bool denotesOperation); void setCallback(CallbackFunction callback); - void printInfo(std::ostream &os, unsigned char indentionLevel = 0) const; + void printInfo(std::ostream &os, unsigned char indentation = 0) const; const ArgumentVector &subArguments() const; void setSubArguments(const ArgumentInitializerList &subArguments); void addSubArgument(Argument *arg); @@ -211,12 +211,15 @@ public: void addMainArgument(Argument *argument); void printHelp(std::ostream &os) const; void parseArgs(int argc, const char *const *argv); + void readArgs(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); + void checkConstraints(); + void invokeCallbacks(); private: IF_DEBUG_BUILD(void verifyArgs(const ArgumentVector &args);) @@ -767,12 +770,57 @@ inline void ArgumentParser::setDefaultArgument(Argument *argument) m_defaultArg = argument; } +/*! + * \brief Checks whether contraints are violated. + * \remarks Automatically called by parseArgs(). + * \throws Throws Failure if constraints are violated. + */ +inline void ArgumentParser::checkConstraints() +{ + checkConstraints(m_mainArgs); +} + +/*! + * \brief Invokes all assigned callbacks. + * \remarks Automatically called by parseArgs(). + */ +inline void ArgumentParser::invokeCallbacks() +{ + invokeCallbacks(m_mainArgs); +} + class CPP_UTILITIES_EXPORT HelpArgument : public Argument { public: HelpArgument(ArgumentParser &parser); }; +class CPP_UTILITIES_EXPORT OperationArgument : public Argument +{ +public: + OperationArgument(const char *name, char abbreviation = '\0', const char *description = nullptr, const char *example = nullptr); +}; + +inline OperationArgument::OperationArgument(const char *name, char abbreviation, const char *description, const char *example) : + Argument(name, abbreviation, description, example) +{ + setDenotesOperation(true); +} + +class CPP_UTILITIES_EXPORT ConfigValueArgument : public Argument +{ +public: + ConfigValueArgument(const char *name, char abbreviation = '\0', const char *description = nullptr, std::initializer_list valueNames = std::initializer_list()); +}; + +inline ConfigValueArgument::ConfigValueArgument(const char *name, char abbreviation, const char *description, std::initializer_list valueNames) : + Argument(name, abbreviation, description) +{ + setCombinable(true); + setRequiredValueCount(valueNames.size()); + setValueNames(valueNames); +} + } #endif // APPLICATION_UTILITIES_ARGUMENTPARSER_H diff --git a/application/commandlineutils.h b/application/commandlineutils.h index 5439fc7..276b14e 100644 --- a/application/commandlineutils.h +++ b/application/commandlineutils.h @@ -3,6 +3,8 @@ #include "../global.h" +#include + namespace ApplicationUtilities { /*! @@ -24,6 +26,34 @@ void CPP_UTILITIES_EXPORT startConsole(); # define CMD_UTILS_START_CONSOLE #endif +/*! + * \brief The Indent class allows printing indentation conveniently, eg. cout << Ident(4) << ... + */ +class CPP_UTILITIES_EXPORT Indentation +{ +public: + Indentation(unsigned char level = 4, char character = ' ') : + level(level), + character(character) + {} + + Indentation operator +(unsigned char level) + { + return Indentation(this->level + level, character); + } + + unsigned char level; + char character; +}; + +inline CPP_UTILITIES_EXPORT std::ostream &operator<< (std::ostream &out, Indentation indentation) +{ + for(unsigned char i = 0; i < indentation.level; ++i) { + out << indentation.character; + } + return out; +} + } // namespace ApplicationUtilities #endif // APPLICATIONUTILITIES_COMMANDLINEUTILS_H diff --git a/io/ansiescapecodes.h b/io/ansiescapecodes.h index 70002cd..89be599 100644 --- a/io/ansiescapecodes.h +++ b/io/ansiescapecodes.h @@ -102,7 +102,7 @@ inline void eraseDisplay(std::ostream &stream) inline void eraseLine(std::ostream &stream) { - stream << "\e[K"; + stream << "\33[2K"; } }