Reporter command line parser errors more eagerly

- show all “unrecognised option” errors
This commit is contained in:
Phil Nash 2013-12-20 19:06:02 +00:00
parent f385a0b13d
commit 886d9d397c
3 changed files with 35 additions and 23 deletions

View File

@ -142,9 +142,8 @@ namespace Catch {
int applyCommandLine( int argc, char* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { int applyCommandLine( int argc, char* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
try { try {
m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
m_unusedTokens = m_cli.parseInto( argc, argv, m_configData ); m_unusedTokens = m_cli.parseInto( argc, argv, m_configData );
if( unusedOptionBehaviour == OnUnusedOptions::Fail )
enforceNoUsedTokens();
if( m_configData.showHelp ) if( m_configData.showHelp )
showHelp( m_configData.processName ); showHelp( m_configData.processName );
m_config.reset(); m_config.reset();
@ -152,7 +151,7 @@ namespace Catch {
catch( std::exception& ex ) { catch( std::exception& ex ) {
{ {
Colour colourGuard( Colour::Red ); Colour colourGuard( Colour::Red );
std::cerr << "\nError in input:\n" std::cerr << "\nError(s) in input:\n"
<< Text( ex.what(), TextAttributes().setIndent(2) ) << Text( ex.what(), TextAttributes().setIndent(2) )
<< "\n\n"; << "\n\n";
} }
@ -167,18 +166,6 @@ namespace Catch {
m_config.reset(); m_config.reset();
} }
void enforceNoUsedTokens() const {
if( !m_unusedTokens.empty() ) {
std::vector<Clara::Parser::Token>::const_iterator
it = m_unusedTokens.begin(),
itEnd = m_unusedTokens.end();
std::string msg;
for(; it != itEnd; ++it )
msg += " unrecognised option: " + it->data + "\n";
throw std::runtime_error( msg.substr( 0, msg.size()-1 ) );
}
}
int run( int argc, char* const argv[] ) { int run( int argc, char* const argv[] ) {
int returnCode = applyCommandLine( argc, argv ); int returnCode = applyCommandLine( argc, argv );

View File

@ -371,18 +371,25 @@ namespace Clara {
CommandLine() CommandLine()
: m_boundProcessName( new Detail::NullBinder<ConfigT>() ), : m_boundProcessName( new Detail::NullBinder<ConfigT>() ),
m_highestSpecifiedArgPosition( 0 ) m_highestSpecifiedArgPosition( 0 ),
m_throwOnUnrecognisedTokens( false )
{} {}
CommandLine( CommandLine const& other ) CommandLine( CommandLine const& other )
: m_boundProcessName( other.m_boundProcessName ), : m_boundProcessName( other.m_boundProcessName ),
m_options ( other.m_options ), m_options ( other.m_options ),
m_positionalArgs( other.m_positionalArgs ), m_positionalArgs( other.m_positionalArgs ),
m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ) m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ),
m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens )
{ {
if( other.m_arg.get() ) if( other.m_arg.get() )
m_arg = ArgAutoPtr( new Arg( *other.m_arg ) ); m_arg = ArgAutoPtr( new Arg( *other.m_arg ) );
} }
CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) {
m_throwOnUnrecognisedTokens = shouldThrow;
return *this;
}
template<typename F> template<typename F>
ArgBinder bind( F f ) { ArgBinder bind( F f ) {
ArgBinder binder( this, f ); ArgBinder binder( this, f );
@ -488,6 +495,7 @@ namespace Clara {
std::vector<Parser::Token> populateOptions( std::vector<Parser::Token> const& tokens, ConfigT& config ) const { std::vector<Parser::Token> populateOptions( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
std::vector<Parser::Token> unusedTokens; std::vector<Parser::Token> unusedTokens;
std::vector<std::string> errors;
for( std::size_t i = 0; i < tokens.size(); ++i ) { for( std::size_t i = 0; i < tokens.size(); ++i ) {
Parser::Token const& token = tokens[i]; Parser::Token const& token = tokens[i];
typename std::vector<Arg>::const_iterator it = m_options.begin(), itEnd = m_options.end(); typename std::vector<Arg>::const_iterator it = m_options.begin(), itEnd = m_options.end();
@ -499,8 +507,9 @@ namespace Clara {
( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) { ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) {
if( arg.takesArg() ) { if( arg.takesArg() ) {
if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional ) if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional )
throw std::domain_error( "Expected argument to option " + token.data ); errors.push_back( "Expected argument to option: " + token.data );
arg.boundField.set( config, tokens[++i].data ); else
arg.boundField.set( config, tokens[++i].data );
} }
else { else {
arg.boundField.setFlag( config ); arg.boundField.setFlag( config );
@ -509,11 +518,26 @@ namespace Clara {
} }
} }
catch( std::exception& ex ) { catch( std::exception& ex ) {
throw std::runtime_error( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" ); errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" );
} }
} }
if( it == itEnd ) if( it == itEnd ) {
unusedTokens.push_back( token ); if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens )
unusedTokens.push_back( token );
else if( m_throwOnUnrecognisedTokens )
errors.push_back( "unrecognised option: " + token.data );
}
}
if( !errors.empty() ) {
std::ostringstream oss;
for( std::vector<std::string>::const_iterator it = errors.begin(), itEnd = errors.end();
it != itEnd;
++it ) {
if( it != errors.begin() )
oss << "\n";
oss << *it;
}
throw std::runtime_error( oss.str() );
} }
return unusedTokens; return unusedTokens;
} }
@ -552,6 +576,7 @@ namespace Clara {
std::map<int, Arg> m_positionalArgs; std::map<int, Arg> m_positionalArgs;
ArgAutoPtr m_arg; ArgAutoPtr m_arg;
int m_highestSpecifiedArgPosition; int m_highestSpecifiedArgPosition;
bool m_throwOnUnrecognisedTokens;
}; };
} // end namespace Clara } // end namespace Clara

View File

@ -116,7 +116,7 @@ TEST_CASE( "cmdline" ) {
.shortOpt( "d" ) .shortOpt( "d" )
.longOpt( "description" ) .longOpt( "description" )
.hint( "some text" ); .hint( "some text" );
const char* argv[] = { "test", "-n 42", "-d some text" }; const char* argv[] = { "test", "-n 42", "-d some text" };
std::vector<Clara::Parser::Token> unusedTokens = parseInto( cli, argv, config1 ); std::vector<Clara::Parser::Token> unusedTokens = parseInto( cli, argv, config1 );