mirror of
https://github.com/catchorg/Catch2.git
synced 2024-11-26 15:26:11 +01:00
Clara: support for positional arguments
This commit is contained in:
parent
30cb460d42
commit
ed79d726be
@ -235,43 +235,8 @@ namespace Clara {
|
|||||||
template<typename ConfigT>
|
template<typename ConfigT>
|
||||||
class CommandLine {
|
class CommandLine {
|
||||||
|
|
||||||
class ArgBinder {
|
|
||||||
public:
|
|
||||||
ArgBinder( CommandLine* cl ) : m_cl( cl ) {}
|
|
||||||
|
|
||||||
template<typename F>
|
|
||||||
ArgBinder& bind( F f ) {
|
|
||||||
if( !m_cl->m_args.empty() )
|
|
||||||
m_cl->m_args.back().validate();
|
|
||||||
m_cl->m_args.push_back( Arg( Detail::makeBoundField( f ) ) );
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
ArgBinder& shortOpt( std::string const& name ) {
|
|
||||||
m_cl->m_args.back().shortNames.push_back( name );
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
ArgBinder& longOpt( std::string const& name ) {
|
|
||||||
m_cl->m_args.back().longName = name;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
ArgBinder& describe( std::string const& description ) {
|
|
||||||
m_cl->m_args.back().description = description;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
ArgBinder& argName( std::string const& argName ) {
|
|
||||||
m_cl->m_args.back().argName = argName;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
ArgBinder& position( int /*position*/ ) {
|
|
||||||
// !TBD: Support for positional args in fixed positions
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
CommandLine* m_cl;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Arg {
|
struct Arg {
|
||||||
Arg( Detail::BoundArgFunction<ConfigT> const& _boundField ) : boundField( _boundField ) {}
|
Arg( Detail::BoundArgFunction<ConfigT> const& _boundField ) : boundField( _boundField ), position( -1 ) {}
|
||||||
|
|
||||||
bool hasShortName( std::string const& shortName ) const {
|
bool hasShortName( std::string const& shortName ) const {
|
||||||
for( std::vector<std::string>::const_iterator
|
for( std::vector<std::string>::const_iterator
|
||||||
@ -288,8 +253,11 @@ namespace Clara {
|
|||||||
bool takesArg() const {
|
bool takesArg() const {
|
||||||
return !argName.empty();
|
return !argName.empty();
|
||||||
}
|
}
|
||||||
bool isPositional() const {
|
bool isFixedPositional() const {
|
||||||
return shortNames.empty() && longName.empty();
|
return position != -1;
|
||||||
|
}
|
||||||
|
bool isAnyPositional() const {
|
||||||
|
return position == -1 && shortNames.empty() && longName.empty();
|
||||||
}
|
}
|
||||||
std::string dbgName() const {
|
std::string dbgName() const {
|
||||||
if( !longName.empty() )
|
if( !longName.empty() )
|
||||||
@ -328,18 +296,70 @@ namespace Clara {
|
|||||||
std::string longName;
|
std::string longName;
|
||||||
std::string description;
|
std::string description;
|
||||||
std::string argName;
|
std::string argName;
|
||||||
|
int position;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ArgBinder {
|
||||||
|
public:
|
||||||
|
template<typename F>
|
||||||
|
ArgBinder( CommandLine* cl, F f )
|
||||||
|
: m_cl( cl ),
|
||||||
|
m_arg( Detail::makeBoundField( f ) )
|
||||||
|
{}
|
||||||
|
ArgBinder( ArgBinder& other )
|
||||||
|
: m_cl( other.m_cl ),
|
||||||
|
m_arg( other.m_arg )
|
||||||
|
{
|
||||||
|
other.m_cl = NULL;
|
||||||
|
}
|
||||||
|
~ArgBinder() {
|
||||||
|
if( m_cl ) {
|
||||||
|
m_arg.validate();
|
||||||
|
if( m_arg.isFixedPositional() )
|
||||||
|
m_cl->m_positionalArgs.push_back( m_arg );
|
||||||
|
else if( m_arg.isAnyPositional() ) {
|
||||||
|
if( m_cl->m_arg.get() )
|
||||||
|
throw std::logic_error( "Only one unpositional argument can be added" );
|
||||||
|
m_cl->m_arg = std::auto_ptr<Arg>( new Arg( m_arg ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_cl->m_options.push_back( m_arg );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ArgBinder& shortOpt( std::string const& name ) {
|
||||||
|
m_arg.shortNames.push_back( name );
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ArgBinder& longOpt( std::string const& name ) {
|
||||||
|
m_arg.longName = name;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ArgBinder& describe( std::string const& description ) {
|
||||||
|
m_arg.description = description;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ArgBinder& argName( std::string const& argName ) {
|
||||||
|
m_arg.argName = argName;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ArgBinder& position( int position ) {
|
||||||
|
m_arg.position = position;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
CommandLine* m_cl;
|
||||||
|
Arg m_arg;
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template<typename F>
|
template<typename F>
|
||||||
ArgBinder bind( F f ) {
|
ArgBinder bind( F f ) {
|
||||||
ArgBinder binder( this );
|
ArgBinder binder( this, f );
|
||||||
binder.bind( f );
|
|
||||||
return binder;
|
return binder;
|
||||||
}
|
}
|
||||||
|
|
||||||
void usage( std::ostream& os ) const {
|
void usage( std::ostream& os ) const {
|
||||||
typename std::vector<Arg>::const_iterator itBegin = m_args.begin(), itEnd = m_args.end(), it;
|
typename std::vector<Arg>::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it;
|
||||||
std::size_t maxWidth = 0;
|
std::size_t maxWidth = 0;
|
||||||
for( it = itBegin; it != itEnd; ++it )
|
for( it = itBegin; it != itEnd; ++it )
|
||||||
maxWidth = (std::max)( maxWidth, it->commands().size() );
|
maxWidth = (std::max)( maxWidth, it->commands().size() );
|
||||||
@ -378,14 +398,20 @@ namespace Clara {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Parser::Token> populate( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
|
std::vector<Parser::Token> populate( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
|
||||||
if( m_args.empty() )
|
if( m_options.empty() && m_positionalArgs.empty() )
|
||||||
throw std::logic_error( "No options or arguments specified" );
|
throw std::logic_error( "No options or arguments specified" );
|
||||||
m_args.back().validate();
|
|
||||||
|
|
||||||
|
std::vector<Parser::Token> unusedTokens = populateOptions( tokens, config );
|
||||||
|
unusedTokens = populateFixedArgs( unusedTokens, config );
|
||||||
|
unusedTokens = populateFloatingArgs( unusedTokens, config );
|
||||||
|
return unusedTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Parser::Token> populateOptions( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
|
||||||
std::vector<Parser::Token> unusedTokens;
|
std::vector<Parser::Token> unusedTokens;
|
||||||
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_args.begin(), itEnd = m_args.end();
|
typename std::vector<Arg>::const_iterator it = m_options.begin(), itEnd = m_options.end();
|
||||||
for(; it != itEnd; ++it ) {
|
for(; it != itEnd; ++it ) {
|
||||||
Arg const& arg = *it;
|
Arg const& arg = *it;
|
||||||
|
|
||||||
@ -401,7 +427,23 @@ namespace Clara {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if( token.type == Parser::Token::Positional && arg.isPositional() ) {
|
}
|
||||||
|
if( it == itEnd )
|
||||||
|
unusedTokens.push_back( token );
|
||||||
|
}
|
||||||
|
return unusedTokens;
|
||||||
|
}
|
||||||
|
std::vector<Parser::Token> populateFixedArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
|
||||||
|
std::vector<Parser::Token> unusedTokens;
|
||||||
|
int position = 1;
|
||||||
|
for( std::size_t i = 0; i < tokens.size(); ++i ) {
|
||||||
|
Parser::Token const& token = tokens[i];
|
||||||
|
typename std::vector<Arg>::const_iterator it = m_positionalArgs.begin(), itEnd = m_positionalArgs.end();
|
||||||
|
for(; it != itEnd; ++it ) {
|
||||||
|
Arg const& arg = *it;
|
||||||
|
if( token.type == Parser::Token::Positional )
|
||||||
|
if( arg.position == position ) {
|
||||||
|
position++;
|
||||||
arg.boundField.set( config, token.data );
|
arg.boundField.set( config, token.data );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -411,9 +453,24 @@ namespace Clara {
|
|||||||
}
|
}
|
||||||
return unusedTokens;
|
return unusedTokens;
|
||||||
}
|
}
|
||||||
|
std::vector<Parser::Token> populateFloatingArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
|
||||||
|
if( !m_arg.get() )
|
||||||
|
return tokens;
|
||||||
|
std::vector<Parser::Token> unusedTokens;
|
||||||
|
for( std::size_t i = 0; i < tokens.size(); ++i ) {
|
||||||
|
Parser::Token const& token = tokens[i];
|
||||||
|
if( token.type == Parser::Token::Positional )
|
||||||
|
m_arg->boundField.set( config, token.data );
|
||||||
|
else
|
||||||
|
unusedTokens.push_back( token );
|
||||||
|
}
|
||||||
|
return unusedTokens;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<Arg> m_args;
|
std::vector<Arg> m_options;
|
||||||
|
std::vector<Arg> m_positionalArgs;
|
||||||
|
std::auto_ptr<Arg> m_arg;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end namespace Clara
|
} // end namespace Clara
|
||||||
@ -432,6 +489,9 @@ struct TestOpt {
|
|||||||
int number;
|
int number;
|
||||||
int index;
|
int index;
|
||||||
bool flag;
|
bool flag;
|
||||||
|
std::string firstPos;
|
||||||
|
std::string secondPos;
|
||||||
|
std::string unpositional;
|
||||||
|
|
||||||
void setValidIndex( int i ) {
|
void setValidIndex( int i ) {
|
||||||
if( i < 0 || i > 10 )
|
if( i < 0 || i > 10 )
|
||||||
@ -557,6 +617,26 @@ TEST_CASE( "cmdline" ) {
|
|||||||
REQUIRE( config.flag == false );
|
REQUIRE( config.flag == false );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SECTION( "positional" ) {
|
||||||
|
cli.bind( &TestOpt::secondPos )
|
||||||
|
.describe( "Second position" )
|
||||||
|
.argName( "second arg" )
|
||||||
|
.position( 2 );
|
||||||
|
cli.bind( &TestOpt::unpositional )
|
||||||
|
.argName( "any arg" )
|
||||||
|
.describe( "Unpositional" );
|
||||||
|
cli.bind( &TestOpt::firstPos )
|
||||||
|
.describe( "First position" )
|
||||||
|
.argName( "first arg" )
|
||||||
|
.position( 1 );
|
||||||
|
|
||||||
|
const char* argv[] = { "test", "-f", "1st", "-o", "filename", "2nd", "3rd" };
|
||||||
|
parseInto( cli, argv, config );
|
||||||
|
|
||||||
|
REQUIRE( config.firstPos == "1st" );
|
||||||
|
REQUIRE( config.secondPos == "2nd" );
|
||||||
|
REQUIRE( config.unpositional == "3rd" );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Config {
|
struct Config {
|
||||||
|
Loading…
Reference in New Issue
Block a user