/* * Created by Phil on 02/11/2010. * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #ifndef TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED #define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED #include "catch_config.hpp" #include "catch_common.h" namespace Catch { class Command { public: Command(){} explicit Command( std::string const& name ) : m_name( name ) { } Command& operator += ( std::string const& arg ) { m_args.push_back( arg ); return *this; } Command& operator += ( Command const& other ) { std::copy( other.m_args.begin(), other.m_args.end(), std::back_inserter( m_args ) ); if( m_name.empty() ) m_name = other.m_name; return *this; } Command operator + ( Command const& other ) { Command newCommand( *this ); newCommand += other; return newCommand; } operator SafeBool::type() const { return SafeBool::makeSafe( !m_name.empty() || !m_args.empty() ); } std::string name() const { return m_name; } std::string operator[]( std::size_t i ) const { return m_args[i]; } std::size_t argsCount() const { return m_args.size(); } void raiseError( std::string const& message ) const { std::ostringstream oss; if( m_name.empty() ) oss << "Error while parsing " << m_name << ". " << message << "."; else oss << "Error while parsing arguments. " << message << "."; if( m_args.size() > 0 ) oss << " Arguments were:"; for( std::size_t i = 0; i < m_args.size(); ++i ) oss << " " << m_args[i]; if( isTrue( true ) ) throw std::domain_error( oss.str() ); } private: std::string m_name; std::vector m_args; }; class CommandParser { public: CommandParser( int argc, char const * const * argv ) : m_argc( static_cast( argc ) ), m_argv( argv ) {} std::string exeName() const { std::string exeName = m_argv[0]; std::string::size_type pos = exeName.find_last_of( "/\\" ); if( pos != std::string::npos ) exeName = exeName.substr( pos+1 ); return exeName; } Command find( std::string const& arg1, std::string const& arg2, std::string const& arg3 ) const { return find( arg1 ) + find( arg2 ) + find( arg3 ); } Command find( std::string const& shortArg, std::string const& longArg ) const { return find( shortArg ) + find( longArg ); } Command find( std::string const& arg ) const { if( arg.empty() ) return getArgs( "", 1 ); else for( std::size_t i = 1; i < m_argc; ++i ) if( m_argv[i] == arg ) return getArgs( m_argv[i], i+1 ); return Command(); } Command getDefaultArgs() const { return getArgs( "", 1 ); } private: Command getArgs( std::string const& cmdName, std::size_t from ) const { Command command( cmdName ); for( std::size_t i = from; i < m_argc && m_argv[i][0] != '-'; ++i ) command += m_argv[i]; return command; } std::size_t m_argc; char const * const * m_argv; }; class OptionParser : public SharedImpl { public: OptionParser( int minArgs = 0, int maxArgs = 0 ) : m_minArgs( minArgs ), m_maxArgs( maxArgs ) {} virtual ~OptionParser() {} Command find( CommandParser const& parser ) const { Command cmd; for( std::vector::const_iterator it = m_optionNames.begin(); it != m_optionNames.end(); ++it ) cmd += parser.find( *it ); return cmd; } void validateArgs( Command const& args ) const { if( tooFewArgs( args ) || tooManyArgs( args ) ) { std::ostringstream oss; if( m_maxArgs == -1 ) oss <<"Expected at least " << pluralise( static_cast( m_minArgs ), "argument" ); else if( m_minArgs == m_maxArgs ) oss <<"Expected " << pluralise( static_cast( m_minArgs ), "argument" ); else oss <<"Expected between " << m_minArgs << " and " << m_maxArgs << " argument"; args.raiseError( oss.str() ); } } void parseIntoConfig( CommandParser const& parser, ConfigData& config ) { if( Command cmd = find( parser ) ) { validateArgs( cmd ); parseIntoConfig( cmd, config ); } } virtual void parseIntoConfig( Command const& cmd, ConfigData& config ) = 0; virtual std::string argsSynopsis() const = 0; virtual std::string optionSummary() const = 0; virtual std::string optionDescription() const { return ""; } std::string optionNames() const { std::string names; for( std::vector::const_iterator it = m_optionNames.begin(); it != m_optionNames.end(); ++it ) { if( !it->empty() ) { if( !names.empty() ) names += ", "; names += *it; } else { names = "[" + names; } } if( names[0] == '[' ) names += "]"; return names; } protected: bool tooFewArgs( Command const& args ) const { return args.argsCount() < static_cast( m_minArgs ); } bool tooManyArgs( Command const& args ) const { return m_maxArgs >= 0 && args.argsCount() > static_cast( m_maxArgs ); } std::vector m_optionNames; int m_minArgs; int m_maxArgs; }; namespace Options { class HelpOptionParser : public OptionParser { public: HelpOptionParser() { m_optionNames.push_back( "-?" ); m_optionNames.push_back( "-h" ); m_optionNames.push_back( "--help" ); } virtual std::string argsSynopsis() const { return "[