Skip to content
This repository has been archived by the owner on Oct 28, 2021. It is now read-only.

Support arguments starting with a dash #59

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions include/clara.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ namespace detail {

if( it != itEnd ) {
auto const &next = *it;
if( isOptPrefix( next[0] ) ) {
if( isOptPrefix( next[0] ) && next.size() > 1 ) {
auto delimiterPos = next.find_first_of( " :=" );
if( delimiterPos != std::string::npos ) {
m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } );
Expand Down Expand Up @@ -157,6 +157,20 @@ namespace detail {
}
return *this;
}

void fetchArgument() {
if( m_tokenBuffer.size() >= 2 ) {
m_tokenBuffer.erase( m_tokenBuffer.begin() );
} else {
if( it != itEnd )
++it;

m_tokenBuffer.resize( 0 );

if( it != itEnd )
m_tokenBuffer.push_back( { TokenType::Argument, *it } );
}
}
};


Expand Down Expand Up @@ -663,7 +677,7 @@ namespace detail {
if( result.value() == ParseResultType::ShortCircuitAll )
return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );
} else {
++remainingTokens;
remainingTokens.fetchArgument();
if( !remainingTokens )
return InternalParseResult::runtimeError( "Expected argument following " + token.token );
auto const &argToken = *remainingTokens;
Expand Down
26 changes: 21 additions & 5 deletions single_include/clara.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ namespace detail {
template<typename ClassT, typename ReturnT, typename ArgT>
struct UnaryLambdaTraits<ReturnT( ClassT::* )( ArgT ) const> {
static const bool isValid = true;
using ArgType = typename std::remove_const<typename std::remove_reference<ArgT>::type>::type;;
using ArgType = typename std::remove_const<typename std::remove_reference<ArgT>::type>::type;
using ReturnType = ReturnT;
};

Expand Down Expand Up @@ -433,7 +433,7 @@ namespace detail {

if( it != itEnd ) {
auto const &next = *it;
if( isOptPrefix( next[0] ) ) {
if( isOptPrefix( next[0] ) && next.size() > 1 ) {
auto delimiterPos = next.find_first_of( " :=" );
if( delimiterPos != std::string::npos ) {
m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } );
Expand Down Expand Up @@ -488,6 +488,20 @@ namespace detail {
}
return *this;
}

void fetchArgument() {
if( m_tokenBuffer.size() >= 2 ) {
m_tokenBuffer.erase( m_tokenBuffer.begin() );
} else {
if( it != itEnd )
++it;

m_tokenBuffer.resize( 0 );

if( it != itEnd )
m_tokenBuffer.push_back( { TokenType::Argument, *it } );
}
}
};


Expand Down Expand Up @@ -535,7 +549,7 @@ namespace detail {
return *this;
}

~ResultValueBase() {
~ResultValueBase() override {
if( m_type == Ok )
m_value.~T();
}
Expand Down Expand Up @@ -573,7 +587,7 @@ namespace detail {
auto errorMessage() const -> std::string { return m_errorMessage; }

protected:
virtual void enforceOk() const {
void enforceOk() const override {
// !TBD: If no exceptions, std::terminate here or something
switch( m_type ) {
case ResultBase::LogicError:
Expand Down Expand Up @@ -994,7 +1008,7 @@ namespace detail {
if( result.value() == ParseResultType::ShortCircuitAll )
return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );
} else {
++remainingTokens;
remainingTokens.fetchArgument();
if( !remainingTokens )
return InternalParseResult::runtimeError( "Expected argument following " + token.token );
auto const &argToken = *remainingTokens;
Expand Down Expand Up @@ -1116,6 +1130,8 @@ namespace detail {
for( auto const &cols : rows )
optWidth = (std::max)(optWidth, cols.left.size() + 2);

optWidth = (std::min)(optWidth, consoleWidth/2);

for( auto const &cols : rows ) {
auto row =
TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) +
Expand Down
51 changes: 51 additions & 0 deletions src/ClaraTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,57 @@ std::string toString( Opt const& opt ) {
return oss.str();
}

TEST_CASE( "Opt value can start with a dash" ) {
std::string name;
bool showHelp = false;
auto parser
= Help( showHelp )
| Opt( name, "name" )
["-n"]["--name"]
( "the name to use" );

SECTION( "args" ) {
auto result = parser.parse( Args{ "TestApp", "-n", "-foo" } );
CHECK( result );
REQUIRE( name == "-foo" );
}
SECTION( "arg separated by =" ) {
auto result = parser.parse( Args{ "TestApp", "-n=-bar" } );
CHECK( result );
REQUIRE( name == "-bar" );
}
SECTION( "empty args" ) {
auto result = parser.parse( Args{ "TestApp", "-n", "" } );
CHECK( result );
REQUIRE( name == "" );
}
}

TEST_CASE( "dash as positional argument" ) {
std::string name;
bool showHelp = false;
auto parser
= Help( showHelp )
| Arg( name, "input file" )
( "Input file" );

SECTION( "args" ) {
auto result = parser.parse( Args{ "cat", "filename" } );
CHECK( result );
REQUIRE( name == "filename" );
}
SECTION( "dash arg" ) {
auto result = parser.parse( Args{ "cat", "-" } );
CHECK( result );
REQUIRE( name == "-" );
}
SECTION( "slash arg" ) {
auto result = parser.parse( Args{ "cat", "/" } );
CHECK( result );
REQUIRE( name == "/" );
}
}

TEST_CASE( "different widths" ) {

std::string s;
Expand Down