diff --git a/include/ArgParse.hpp b/include/ArgParse.hpp index 93b2a65..fb249b4 100644 --- a/include/ArgParse.hpp +++ b/include/ArgParse.hpp @@ -23,7 +23,7 @@ public: BOOL }; - enum ValueRequired { + enum Required { OPTIONAL, REQUIRED }; @@ -42,7 +42,10 @@ public: * @param name short and/or long form: "-f,--foo" * The value can be retreived with this string passed to the argAs... functions. * @param help Description of the argument, printed when --help is given. - * @param type Type of the paramterer, required by the argument. + * @param argRequired Argument is required or optional. + * It's a bad practice to have a required argument, + * "options", shall be optional. + * @param valueType Type of the paramterer, required by the argument. * @param valueRequired Parameter requiered/optional after the argument. * @param valueName Default is the type. But some short text can be better. * @param choices Comma separeted list of strings without whitespaces: @@ -51,8 +54,9 @@ public: */ void addArgument(const std::string name, const std::string help, - const enum ValueType type = NONE, - const enum ValueRequired valueRequired = REQUIRED, + const ValueType valueType = NONE, + const Required argRequired = OPTIONAL, + const Required valueRequired = REQUIRED, const std::string valueName = std::string(""), const std::string choices = std::string("")); @@ -71,8 +75,6 @@ public: void parseArgs(const std::list argList); - // is this arg in the map of understood arguments? - bool isArg(const std::string arg) const; // is this argument passed as a command line parameter? bool foundArg(const std::string arg) const; // argument is passed as a command line parameter, but has a value? @@ -90,6 +92,12 @@ public: private: + bool thereAreOptionalArgs() const; + bool thereAreRequiredArgs() const; + std::string printArgs(const Required argRequired) const; + + void checkRequiredArgsFound() const; + void validateValue(const ArgParse::ValueType type, const std::string name, const std::string choices, @@ -112,8 +120,9 @@ private: struct Argument { const std::string m_help; - const enum ValueType m_type; - const enum ValueRequired m_valueRequired; + const ValueType m_type; + const Required m_argRequired; + const Required m_valueRequired; const std::string m_valueName; const std::string m_choices; std::string m_value; @@ -121,8 +130,9 @@ private: bool m_valueHasBeenSet; Argument (const std::string help, - const enum ValueType type = NONE, - const enum ValueRequired valueRequired = REQUIRED, + const ValueType type = NONE, + const Required argRequired = OPTIONAL, + const Required valueRequired = REQUIRED, const std::string valueName = std::string(""), const std::string choices = std::string(""), const std::string value = std::string("")); diff --git a/other/mysqlclient_main.cpp b/other/mysqlclient_main.cpp index 6522671..a3ea231 100644 --- a/other/mysqlclient_main.cpp +++ b/other/mysqlclient_main.cpp @@ -20,13 +20,16 @@ void setUpArgs(ArgParse &argParse) ArgParse::STRING ); argParse.addArgument("-u, --user", "Username", - ArgParse::STRING ); + ArgParse::STRING, + ArgParse::REQUIRED ); argParse.addArgument("-db, --database", "Database", - ArgParse::STRING ); + ArgParse::STRING, + ArgParse::REQUIRED ); argParse.addArgument("-p, --password", "Password", - ArgParse::STRING ); + ArgParse::STRING, + ArgParse::REQUIRED ); argParse.addArgument("-port", "Port", ArgParse::INT ); @@ -39,7 +42,7 @@ void setUpArgs(ArgParse &argParse) } -bool getArgs( int argc, char* argv[], +void getArgs( int argc, char* argv[], ArgParse &argParse, std::string &host, std::string &user, @@ -49,12 +52,7 @@ bool getArgs( int argc, char* argv[], int &port, int &clientflags ) { - try { - argParse.parseArgs(argc, argv); - } catch (std::runtime_error e) { - std::cerr << e.what() << std::endl << std::endl; - return false; - } + argParse.parseArgs(argc, argv); argParse.argAsString("--host", host); argParse.argAsString("-u, --user", user); @@ -63,8 +61,6 @@ bool getArgs( int argc, char* argv[], argParse.argAsInt("-port", port); argParse.argAsString("-s, --unix-socket", unixsocket); argParse.argAsInt("-f, --client-flags", clientflags); - - return true; } @@ -94,12 +90,24 @@ int main(int argc, char* argv[] ) std::string host, user, db, pass, unixsocket; int port, clientflags; - if ( !getArgs(argc, argv, - argParse, - host, user, db, pass, unixsocket, - port, clientflags ) || - argParse.foundArg("-h, --help") ) - { + + try { + getArgs( argc, argv, + argParse, + host, user, db, pass, unixsocket, + port, clientflags ); + } catch (std::runtime_error e) { + if ( argParse.foundArg("-h, --help") ) { + std::cout << argParse.usage() << std::endl; + return 1; + } + std::cerr << e.what() << std::endl + << "Check usage: " << argv[0] << " --help" << std::endl; + return 1; + } + + + if ( argParse.foundArg("-h, --help") ) { std::cout << argParse.usage() << std::endl; return 1; } diff --git a/src/ArgParse.cpp b/src/ArgParse.cpp index adea27b..533c034 100644 --- a/src/ArgParse.cpp +++ b/src/ArgParse.cpp @@ -30,8 +30,9 @@ ArgParse::ArgParse(const std::string description, void ArgParse::addArgument(const std::string arg, const std::string help, - const ValueType type, - const ValueRequired valueRequired, + const ValueType valueType, + const Required argRequired, + const Required valueRequired, const std::string valueName, const std::string choices) { @@ -44,7 +45,7 @@ void ArgParse::addArgument(const std::string arg, append(" has been given before.")); int i; - if ( type == INT && + if ( valueType == INT && !choices.empty() && sscanf( choices.c_str(), "%d..%d", &i, &i ) != 2 ) throw std::logic_error(std::string( arg ). @@ -52,7 +53,7 @@ void ArgParse::addArgument(const std::string arg, append("Range expected in a INT..INT format" )); float f; - if ( type == FLOAT && + if ( valueType == FLOAT && !choices.empty() && sscanf( choices.c_str(), "%f..%f", &f, &f ) != 2 ) throw std::logic_error(std::string( arg ). @@ -60,9 +61,10 @@ void ArgParse::addArgument(const std::string arg, append("Range expected in a FLOAT..FLOAT format" )); Argument argument(help, - type, + valueType, + argRequired, valueRequired, - typeToString(type, valueName), + typeToString(valueType, valueName), choices, ""); m_params.insert(std::pair(arg, argument)); @@ -121,6 +123,182 @@ void ArgParse::parseArgs(const std::list argList) (*argMapIt).second.m_valueHasBeenSet = true; ++it; } + + checkRequiredArgsFound(); +} + + +bool ArgParse::foundArg(const std::string arg) const +{ + ArgMap::const_iterator it = m_params.find(arg); + return it != m_params.end() && (*it).second.m_found == true; +} + + +bool ArgParse::argHasValue(const std::string arg) const +{ + ArgMap::const_iterator it = m_params.find(arg); + return it != m_params.end() && + (*it).second.m_found == true && + (*it).second.m_valueHasBeenSet; +} + + +bool ArgParse::argAsString(const std::string arg, std::string &value) const +{ + if ( !argHasValue(arg) ) + return false; + + ArgMap::const_iterator it = m_params.find(arg); + value = (*it).second.m_value; + return true; +} + + +bool ArgParse::argAsInt(const std::string arg, int &value) const +{ + if ( !argHasValue(arg) ) + return false; + + ArgMap::const_iterator it = m_params.find(arg); + value = atoi((*it).second.m_value.c_str()); + return true; +} + + +bool ArgParse::argAsFloat(const std::string arg, float &value) const +{ + if ( !argHasValue(arg) ) + return false; + + ArgMap::const_iterator it = m_params.find(arg); + value = atof((*it).second.m_value.c_str()); + return true; +} + + +bool ArgParse::argAsBool(const std::string arg, bool &value) const +{ + if ( !argHasValue(arg) ) + return false; + + ArgMap::const_iterator it = m_params.find(arg); + + std::string temp = (*it).second.m_value; + std::transform(temp.begin(), temp.end(),temp.begin(), ::toupper); + + value = temp == "TRUE"; + return true; +} + + +std::string ArgParse::usage() const +{ + std::stringstream ss; + + ss << m_description << std::endl << std::endl; + + bool oprionals = thereAreOptionalArgs(); + bool required = thereAreRequiredArgs(); + ss << "usage: " << m_programName + << (required ? " " : "") + << (oprionals ? " [OPTIONS]" : "") + << std::endl; + + if ( required ) { + ss << std::endl << "Required arguments:" << std::endl; + ss << printArgs(REQUIRED); + } + if ( oprionals ) { + ss << std::endl << "Options:" << std::endl; + ss << printArgs(OPTIONAL); + } + + ss << std::endl; + ss << m_epilog; + + return ss.str();; +} + + +bool ArgParse::thereAreOptionalArgs() const +{ + for (ArgMap::const_iterator it = m_params.begin(); it != m_params.end(); ++it ) + if ( (*it).second.m_argRequired == OPTIONAL ) + return true; + + return false; +} + + +bool ArgParse::thereAreRequiredArgs() const +{ + for (ArgMap::const_iterator it = m_params.begin(); it != m_params.end(); ++it ) + if ( (*it).second.m_argRequired == REQUIRED ) + return true; + + return false; +} + + +std::string +ArgParse::printArgs(const Required argRequired) const +{ + int length; + std::string ret(""); + + ArgMap::const_iterator it; + for ( it = m_params.begin(); it != m_params.end(); it++ ) { + if ( (*it).second.m_argRequired != argRequired ) + continue; + + ret.append( (*it).first ); + length = (*it).first.length(); + if ( (*it).second.m_type != NONE ) { + + ret.append( " " ); + length++; + + if ( (*it).second.m_valueRequired == OPTIONAL ) { + ret.append( "[" ); + length ++; + } + + if ( !(*it).second.m_choices.empty() ) { + ret.append( "{" ).append( (*it).second.m_choices).append( "}" ); + length += (*it).second.m_choices.length() + 2; + } else { + ret.append( (*it).second.m_valueName ); + length += (*it).second.m_valueName.length(); + } + + if ( (*it).second.m_valueRequired == OPTIONAL ) { + ret.append( "]" ); + length++; + } + } + + ret.append( std::string(30-length, ' ') ); + ret.append( (*it).second.m_help ); + ret.append("\n"); + } + + return ret; +} + + +void ArgParse::checkRequiredArgsFound() const +{ + ArgMap::const_iterator it; + + std::string missed; + for ( it = m_params.begin(); it != m_params.end(); ++it ) + if ( (*it).second.m_argRequired && !(*it).second.m_found ) + missed.append((*it).first).append("\n"); + + if ( !missed.empty() ) + throw std::runtime_error( + std::string("Required argument(s) missing: \n").append(missed)); } @@ -228,133 +406,16 @@ void ArgParse::validateBool( const std::string name, } -bool ArgParse::isArg(const std::string arg) const -{ - ArgMap::const_iterator it = m_params.find(arg); - return it != m_params.end(); -} - - -bool ArgParse::foundArg(const std::string arg) const -{ - ArgMap::const_iterator it = m_params.find(arg); - return it != m_params.end() && (*it).second.m_found == true; -} - -bool ArgParse::argHasValue(const std::string arg) const -{ - ArgMap::const_iterator it = m_params.find(arg); - return it != m_params.end() && - (*it).second.m_found == true && - (*it).second.m_valueHasBeenSet; -} - - -bool ArgParse::argAsString(const std::string arg, std::string &value) const -{ - if ( !argHasValue(arg) ) - return false; - - ArgMap::const_iterator it = m_params.find(arg); - value = (*it).second.m_value; - return true; -} - - -bool ArgParse::argAsInt(const std::string arg, int &value) const -{ - if ( !argHasValue(arg) ) - return false; - - ArgMap::const_iterator it = m_params.find(arg); - value = atoi((*it).second.m_value.c_str()); - return true; -} - - -bool ArgParse::argAsFloat(const std::string arg, float &value) const -{ - if ( !argHasValue(arg) ) - return false; - - ArgMap::const_iterator it = m_params.find(arg); - value = atof((*it).second.m_value.c_str()); - return true; -} - - -bool ArgParse::argAsBool(const std::string arg, bool &value) const -{ - if ( !argHasValue(arg) ) - return false; - - ArgMap::const_iterator it = m_params.find(arg); - - std::string temp = (*it).second.m_value; - std::transform(temp.begin(), temp.end(),temp.begin(), ::toupper); - - value = temp == "TRUE"; - return true; -} - - -std::string ArgParse::usage() const -{ - std::stringstream ss; - - ss << m_description << std::endl << std::endl; - - ss << "usage: " << m_programName; - if (!m_params.empty()) { - ss << " [OPTION]" << std::endl << std::endl; - ss << "Options:" << std::endl; - int length; - ArgMap::const_iterator it; - for ( it = m_params.begin(); it != m_params.end(); it++ ) { - ss << (*it).first; - length = (*it).first.length(); - if ( (*it).second.m_type != NONE ) { - - ss << " "; - length++; - - if ( (*it).second.m_valueRequired == OPTIONAL ) { - ss << "["; - length ++; - } - - if ( !(*it).second.m_choices.empty() ) { - ss << "{" << (*it).second.m_choices << "}"; - length += (*it).second.m_choices.length() + 2; - } else { - ss << (*it).second.m_valueName; - length += (*it).second.m_valueName.length(); - } - - if ( (*it).second.m_valueRequired == OPTIONAL ) { - ss << "]"; - length++; - } - } - ss << std::string(30-length, ' '); - ss << (*it).second.m_help << std::endl; - } - } - ss << std::endl; - ss << m_epilog << std::endl; - - return ss.str();; -} - - ArgParse::Argument::Argument (const std::string help, - const enum ValueType type, - const enum ValueRequired valueRequired, + const ValueType type, + const Required argRequired, + const Required valueRequired, const std::string valueName, const std::string choices, const std::string value) : m_help(help) , m_type(type) + , m_argRequired(argRequired) , m_valueRequired(valueRequired) , m_valueName(valueName) , m_choices(choices) diff --git a/test/test_ArgParse.hpp b/test/test_ArgParse.hpp index a3e8984..1da40d0 100644 --- a/test/test_ArgParse.hpp +++ b/test/test_ArgParse.hpp @@ -23,10 +23,10 @@ public: TS_ASSERT_EQUALS( argParse.usage(), std::string( "intro\n\n" - "usage: [OPTION]\n\n" + "usage: [OPTIONS]\n\n" "Options:\n" "-h, --help Prints this help message\n\n" - "outro\n") ); + "outro") ); } void testfindKeyinArgMap ( void ) @@ -66,8 +66,8 @@ public: argParse.parseArgs(argc, argv); - TS_ASSERT_EQUALS( argParse.isArg("-s, --unix-socket"), false ); - TS_ASSERT_EQUALS( argParse.isArg("-u, --user"), true ); + TS_ASSERT_EQUALS( argParse.foundArg("-s, --unix-socket"), false ); + TS_ASSERT_EQUALS( argParse.foundArg("-u, --user"), true ); } };