31#ifndef ARGS__HELP_PRINTER_HPP__INCLUDED
32#define ARGS__HELP_PRINTER_HPP__INCLUDED
68 using ArgPtr = std::unique_ptr< ArgIface, details::Deleter< ArgIface > >;
86 Command * parent =
nullptr )
override;
109 bool required )
const;
115 String::size_type currentPos, String::size_type leftMargin,
116 String::size_type rightMargin )
const;
120 void sortArg(
const ArgPtr & arg,
121 std::vector< Command* > & commands,
122 std::vector< ArgIface* > & required,
123 std::vector< ArgIface* > & optional,
124 String::size_type & maxFlag,
125 String::size_type & maxName,
126 String::size_type & maxCommand,
127 bool requiredAllOfGroup =
false )
const;
130 String::size_type beforeDescription, String::size_type maxFlag )
const;
142 String::size_type m_lineLength;
145static const String c_positional =
SL(
"[positional]" );
147static const String c_commands =
SL(
"<command>" );
149static const String c_options =
SL(
"<options>" );
169printOffset(
OutStreamType & to, String::size_type currentPos,
170 String::size_type leftMargin )
172 if( currentPos < leftMargin )
173 to <<
String( leftMargin - currentPos,
' ' );
177calcMaxFlagAndName( ArgIface * arg, String::size_type & maxFlag,
178 String::size_type & maxName )
180 String::size_type f = 1;
181 String::size_type n = ( !arg->argumentName().empty() ?
182 arg->argumentName().length() : ( arg->flag().empty() ?
183 arg->name().length() : 0 ) );
185 if( arg->isWithValue() )
188 n += 3 + arg->valueSpecifier().length();
190 f += 3 + arg->valueSpecifier().length();
201HelpPrinter::sortArg(
const ArgPtr & arg,
202 std::vector< Command* > & commands,
203 std::vector< ArgIface* > & required,
204 std::vector< ArgIface* > & optional,
205 String::size_type & maxFlag,
206 String::size_type & maxName,
207 String::size_type & maxCommand,
208 bool requiredAllOfGroup )
const
210 GroupIface * g =
dynamic_cast< GroupIface*
> ( arg.get() );
216 commands.push_back( cmd );
218 String::size_type length = cmd->name().length() + ( cmd->isWithValue() ?
219 3 + cmd->valueSpecifier().length() : 0 );
221 if( length > maxCommand )
227 requiredAllOfGroup =
true;
229 requiredAllOfGroup =
false;
231 auto f = std::bind( &HelpPrinter::sortArg,
this, std::placeholders::_1,
232 std::ref( commands ), std::ref( required ),
233 std::ref( optional ), std::ref( maxFlag ),
234 std::ref( maxName ), std::ref( maxCommand ),
235 requiredAllOfGroup );
237 std::for_each( g->children().cbegin(),
238 g->children().cend(), f );
242 if( arg->isRequired() || requiredAllOfGroup )
243 required.push_back( arg.get() );
245 optional.push_back( arg.get() );
247 calcMaxFlagAndName( arg.get(), maxFlag, maxName );
252HelpPrinter::printOnlyFor( ArgIface * arg,
OutStreamType & to,
253 String::size_type beforeDescription, String::size_type )
const
255 String::size_type pos = 0;
257 if( !arg->flag().empty() )
261 to <<
'-' << arg->flag();
262 pos += arg->flag().length() + 1;
264 if( !arg->argumentName().empty() )
269 else if( arg->isWithValue() )
271 to <<
" <" << arg->valueSpecifier() <<
'>';
272 pos += arg->valueSpecifier().length() + 3;
277 printOffset( to, pos, 4 );
281 if( !arg->argumentName().empty() )
285 to <<
"--" << arg->argumentName();
286 pos += arg->argumentName().length() + 2;
288 if( arg->isWithValue() )
290 to <<
" <" << arg->valueSpecifier() <<
'>';
291 pos += arg->valueSpecifier().length() + 3;
294 else if( arg->flag().empty() && arg->argumentName().empty() )
299 pos += arg->name().length();
301 if( arg->isWithValue() )
303 to <<
" <" << arg->valueSpecifier() <<
'>';
304 pos += arg->valueSpecifier().length() + 3;
308 printString( to, splitToWords( arg->description() ), pos,
309 beforeDescription, 0 );
318template<
typename T >
319bool argNameLess(
const T & a1,
const T & a2 )
321 static const Char dash =
SL(
'-' );
323 if( !a1->name().empty() && !a2->name().empty() )
325 if( *( a1->name().cbegin() ) != dash &&
326 *( a2->name().cbegin() ) == dash )
328 else if( *( a1->name().cbegin() ) == dash &&
329 *( a2->name().cbegin() ) != dash )
331 else if( a1->argumentName().empty() && !a2->argumentName().empty() )
333 else if( !a1->argumentName().empty() && a2->argumentName().empty() )
336 return ( a1->name() < a2->name() );
339 return ( a1->name() < a2->name() );
347 std::vector< ArgIface* > required;
348 std::vector< ArgIface* > optional;
349 std::vector< Command* > commands;
351 String::size_type maxFlag = 0;
352 String::size_type maxName = 0;
353 String::size_type maxCommand = 0;
355 bool requiredAllOfGroup =
false;
357 auto f = std::bind( &HelpPrinter::sortArg,
this, std::placeholders::_1,
358 std::ref( commands ), std::ref( required ),
359 std::ref( optional ), std::ref( maxFlag ),
360 std::ref( maxName ), std::ref( maxCommand ),
361 requiredAllOfGroup );
363 std::for_each( m_cmdLine->
arguments().cbegin(),
367 std::sort( required.begin(), required.end(),
368 [] (
const auto & a1,
const auto & a2 )
369 { return details::argNameLess( a1, a2 ); } );
371 std::sort( optional.begin(), optional.end(),
372 [] (
const auto & a1,
const auto & a2 )
373 { return details::argNameLess( a1, a2 ); } );
375 std::sort( commands.begin(), commands.end(),
376 [] (
const auto & c1,
const auto & c2 )
377 { return details::argNameLess( c1, c2 ); } );
383 printString( to, splitToWords( m_appDescription ), 0, 0, 0 );
387 if( commands.empty() )
391 usage.push_back( m_exeName );
393 bool requiredFlag =
true;
395 std::function< void (
ArgIface* ) > createUsageAndAppend =
398 const StringList words = createUsageString( arg,
402 usage.push_back( w );
405 std::for_each( required.cbegin(), required.cend(),
406 createUsageAndAppend );
408 requiredFlag =
false;
410 std::for_each( optional.cbegin(), optional.cend(),
411 createUsageAndAppend );
419 printString( to, usage, 7, 7, 1 );
427 usage.push_back( m_exeName );
428 usage.push_back( c_commands );
430 if( !optional.empty() || !required.empty() )
431 usage.push_back( c_options );
439 printString( to, usage, 7, 7, 1 );
443 std::for_each( commands.cbegin(), commands.cend(),
446 String::size_type pos = 2;
448 to <<
" " << cmd->name();
450 pos += cmd->name().length();
452 if( cmd->isWithValue() )
454 to <<
" <" << cmd->valueSpecifier() <<
">";
456 pos += 3 + cmd->valueSpecifier().length();
459 printString( to, splitToWords( cmd->
description() ), pos,
468 auto printArg = std::bind( &HelpPrinter::printOnlyFor,
this,
469 std::placeholders::_1, std::ref( to ),
470 ( maxFlag == 1 ? maxName + 6 : ( maxName + 6 > maxFlag + 1 ?
471 maxName + 6 : maxFlag + 1 ) ),
474 if( !required.empty() )
476 to <<
"REQUIRED:" <<
"\n";
478 std::for_each( required.cbegin(), required.cend(), printArg );
481 if( !optional.empty() )
483 to <<
"OPTIONAL:" <<
"\n";
485 std::for_each( optional.cbegin(), optional.cend(), printArg );
497 arg = m_cmdLine->findArgument( name );
499 if( arg && arg->type() == ArgType::Command )
504 std::vector< ArgIface* > grequired;
505 std::vector< ArgIface* > goptional;
506 std::vector< Command* > gcommands;
508 String::size_type gmaxFlag = 0;
509 String::size_type gmaxName = 0;
510 String::size_type gmaxCommand = 0;
512 bool requiredAllOfGroup =
false;
514 auto gf = std::bind( &HelpPrinter::sortArg,
this, std::placeholders::_1,
515 std::ref( gcommands ), std::ref( grequired ),
516 std::ref( goptional ), std::ref( gmaxFlag ),
517 std::ref( gmaxName ), std::ref( gmaxCommand ),
518 requiredAllOfGroup );
520 std::for_each( m_cmdLine->arguments().cbegin(),
521 m_cmdLine->arguments().cend(), gf );
524 std::sort( grequired.begin(), grequired.end(),
525 [] (
const auto & a1,
const auto & a2 )
526 { return details::argNameLess( a1, a2 ); } );
528 std::sort( goptional.begin(), goptional.end(),
529 [] (
const auto & a1,
const auto & a2 )
530 { return details::argNameLess( a1, a2 ); } );
536 std::vector< ArgIface* > required;
537 std::vector< ArgIface* > optional;
538 std::vector< Command* > commands;
540 String::size_type maxFlag = 0;
541 String::size_type maxName = 0;
542 String::size_type maxCommand = 0;
544 requiredAllOfGroup =
false;
546 auto f = std::bind( &HelpPrinter::sortArg,
this, std::placeholders::_1,
547 std::ref( commands ), std::ref( required ),
548 std::ref( optional ), std::ref( maxFlag ),
549 std::ref( maxName ), std::ref( maxCommand ),
550 requiredAllOfGroup );
552 std::for_each( cmd->
children().cbegin(),
556 std::sort( required.begin(), required.end(),
557 [] (
const auto & a1,
const auto & a2 )
558 { return details::argNameLess( a1, a2 ); } );
560 std::sort( optional.begin(), optional.end(),
561 [] (
const auto & a1,
const auto & a2 )
562 { return details::argNameLess( a1, a2 ); } );
564 std::sort( commands.begin(), commands.end(),
565 [] (
const auto & c1,
const auto & c2 )
566 { return details::argNameLess( c1, c2 ); } );
577 if( commands.empty() )
579 to <<
"USAGE: " << name;
584 if( !grequired.empty() || !goptional.empty() ||
585 !required.empty() || !optional.empty() )
592 to <<
"USAGE: " << name <<
" <command>";
594 if( !optional.empty() || !required.empty() )
599 std::for_each( commands.cbegin(), commands.cend(),
602 String::size_type pos = 2;
604 to <<
" " << c->name();
606 pos += c->name().length();
608 if( c->isWithValue() )
610 to <<
" <" << c->valueSpecifier() <<
">";
612 pos += 3 + c->valueSpecifier().length();
615 printString( to, splitToWords( c->
description() ), pos,
625 auto printArg = std::bind( &HelpPrinter::printOnlyFor,
this,
626 std::placeholders::_1, std::ref( to ),
627 ( maxFlag == 1 ? maxName + 6 : ( maxName + 6 > maxFlag + 1 ?
628 maxName + 6 : maxFlag + 1 ) ),
631 if( !required.empty() )
633 to <<
"REQUIRED:" <<
"\n";
635 std::for_each( required.cbegin(), required.cend(), printArg );
638 if( !optional.empty() )
640 to <<
"OPTIONAL:" <<
"\n";
642 std::for_each( optional.cbegin(), optional.cend(), printArg );
646 if( !grequired.empty() || !goptional.empty() )
648 auto printGlobalArg = std::bind( &HelpPrinter::printOnlyFor,
this,
649 std::placeholders::_1, std::ref( to ),
650 ( gmaxFlag == 1 ? gmaxName + 6 : ( gmaxName + 6 > gmaxFlag + 1 ?
651 gmaxName + 6 : gmaxFlag + 1 ) ), gmaxFlag );
653 if( !grequired.empty() )
655 to <<
"GLOBAL REQUIRED:" <<
"\n";
657 std::for_each( grequired.cbegin(), grequired.cend(),
661 if( !goptional.empty() )
663 to <<
"GLOBAL OPTIONAL:" <<
"\n";
665 std::for_each( goptional.cbegin(), goptional.cend(),
679HelpPrinter::print( ArgIface * arg, OutStreamType & to )
const
681 StringList usage = createUsageString( arg,
686 std::for_each( usage.cbegin(), usage.cend(),
687 [ & ] (
const String & s )
693 printString( to, splitToWords( arg->longDescription() ),
702HelpPrinter::setExecutable(
const String & exe )
708HelpPrinter::setAppDescription(
const String & desc )
710 m_appDescription = desc;
720HelpPrinter::setLineLength( String::size_type length )
723 m_lineLength = length;
729HelpPrinter::createUsageString(
ArgIface * arg,
bool required )
const
736 usage.append(
SL(
"[ " ) );
738 if( !arg->
flag().empty() )
740 usage.append(
SL(
"-" ) );
741 usage.append( arg->
flag() );
745 usage.append(
SL(
"," ) );
747 result.push_back( usage );
755 usage.append(
SL(
"--" ) );
759 usage.append( arg->
name() );
763 usage.append(
SL(
" <" ) );
765 usage.append(
SL(
">" ) );
769 usage.append(
SL(
" ]" ) );
771 result.push_back( usage );
777isSpaceChar(
const Char & c )
779 static const String spaceChars =
SL(
" \n\t\r" );
781 return ( spaceChars.find( c ) != String::npos );
785HelpPrinter::splitToWords(
const String & s )
const
790 std::for_each( s.cbegin(), s.cend(),
791 [ &word, &result ] (
const Char & c )
793 if( isSpaceChar( c ) )
796 result.push_back( word );
806 result.push_back( word );
812HelpPrinter::printString( OutStreamType & to,
813 const StringList & words,
814 String::size_type currentPos, String::size_type leftMargin,
815 String::size_type rightMargin )
const
817 const String::size_type occupied = leftMargin + rightMargin;
819 String::size_type maxLineLength = ( occupied < m_lineLength ?
820 m_lineLength - occupied : 0 );
822 if( maxLineLength < 20 )
826 maxLineLength = m_lineLength - 20;
833 String::size_type length = 0;
835 bool makeOffset = ( currentPos < leftMargin );
837 std::for_each( words.cbegin(), words.cend(),
838 [ & ] (
const String & word )
842 printOffset( to, currentPos, leftMargin );
847 if( length + word.length() < maxLineLength )
849 length += word.length();
853 if( length < maxLineLength )
865 maxLineLength = m_lineLength - leftMargin - rightMargin;
869 printOffset( to, currentPos, leftMargin );
873 length += word.length();
875 if( length < maxLineLength )
virtual ArgIface * findArgument(const String &name)=0
virtual const String & valueSpecifier() const =0
virtual String name() const =0
virtual const String & argumentName() const =0
virtual bool isWithValue() const =0
virtual const String & flag() const =0
CmdLine is class that holds all rguments and parse command line arguments in the correspondence with ...
void clear()
Clear state of the arguments.
@ HandlePositionalArguments
Handle positional arguments.
const String & positionalDescription() const
ArgIface * findArgument(const String &name)
const Arguments & arguments() const
CmdLineOpts parserOptions() const
Command in the command line interface.
ArgIface * findChild(const String &name)
bool isWithValue() const override
const String & longDescription() const override
const String & description() const override
const String & valueSpecifier() const override
const Arguments & children() const
HelpPrinter is a class that prints help.
void setCmdLine(CmdLine *cmd) override
Set command line.
std::unique_ptr< ArgIface, details::Deleter< ArgIface > > ArgPtr
Smart pointer to the argument.
void setExecutable(const String &exe) override
Set executable name.
ArgIface * findArgument(const String &name) override
void setLineLength(String::size_type length) override
Set line length for the help.
void setAppDescription(const String &desc) override
Set description for the application.
void print(OutStreamType &to) override
Print help for all arguments.
Interface for HelpPrinter.
constexpr std::add_const< T >::type & asConst(T &t) noexcept
Adds const to non-const objects.
std::string String
String type.
std::vector< String > StringList
List of strings.
@ AllOfGroup
"All of" group.
std::ostream OutStreamType
Out stream type.
String::value_type Char
Char type.
#define DISABLE_COPY(Class)
Macro for disabling copy.