Improve CLI utils

- Add ArgumentParser::readArgs()
- Add Indentation
- Fix eraseLine()
This commit is contained in:
Martchus 2016-10-02 21:53:58 +02:00
parent 4d0807de9b
commit 4c40004f0b
4 changed files with 115 additions and 30 deletions

View File

@ -12,9 +12,6 @@
#include <sstream>
#include <cstring>
#include <cstdlib>
#ifdef LOGGING_ENABLED
# include <fstream>
#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<unsigned int, string>(*(++argv)) : 0);
++argv, --argc;
} catch(const ConversionException &) {
currentWordIndex = argc - 1;
currentWordIndex = static_cast<unsigned int>(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;
}

View File

@ -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<const char *> valueNames = std::initializer_list<const char *>());
};
inline ConfigValueArgument::ConfigValueArgument(const char *name, char abbreviation, const char *description, std::initializer_list<const char *> valueNames) :
Argument(name, abbreviation, description)
{
setCombinable(true);
setRequiredValueCount(valueNames.size());
setValueNames(valueNames);
}
}
#endif // APPLICATION_UTILITIES_ARGUMENTPARSER_H

View File

@ -3,6 +3,8 @@
#include "../global.h"
#include <ostream>
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

View File

@ -102,7 +102,7 @@ inline void eraseDisplay(std::ostream &stream)
inline void eraseLine(std::ostream &stream)
{
stream << "\e[K";
stream << "\33[2K";
}
}