7#ifndef ARGS__HELP_PRINTER_HPP__INCLUDED
8#define ARGS__HELP_PRINTER_HPP__INCLUDED
42 using ArgPtr = std::unique_ptr<ArgIface, details::Deleter<ArgIface>>;
60 Command *parent =
nullptr)
override;
89 String::size_type currentPos,
90 String::size_type leftMargin,
91 String::size_type rightMargin)
const;
96 void printDefaultValue(
ArgIface *arg,
98 String::size_type currentPos,
99 String::size_type leftMargin,
100 String::size_type rightMargin)
const;
102 void sortArg(
const ArgPtr &arg,
103 std::vector<Command *> &commands,
104 std::vector<ArgIface *> &required,
105 std::vector<ArgIface *> &optional,
106 String::size_type &maxFlag,
107 String::size_type &maxName,
108 String::size_type &maxCommand,
109 bool requiredAllOfGroup =
false)
const;
113 String::size_type beforeDescription,
114 String::size_type maxFlag)
const;
126 String::size_type m_lineLength;
129static const String c_positional =
SL(
"[positional]");
131static const String c_commands =
SL(
"<command>");
133static const String c_options =
SL(
"<options>");
150 String::size_type ¤tPos,
151 String::size_type leftMargin)
153 if (currentPos < leftMargin) {
154 to <<
String(leftMargin - currentPos,
' ');
155 currentPos = leftMargin;
159static inline void calcMaxFlagAndName(
ArgIface *arg,
160 String::size_type &maxFlag,
161 String::size_type &maxName)
163 String::size_type f = 1;
164 String::size_type n = (!arg->argumentName().empty() ? arg->argumentName().length()
165 : (arg->flag().empty() ? arg->name().length() : 0));
167 if (arg->isWithValue()) {
169 n += 3 + arg->valueSpecifier().length();
171 f += 3 + arg->valueSpecifier().length();
184inline void HelpPrinter::sortArg(
const ArgPtr &arg,
185 std::vector<Command *> &commands,
186 std::vector<ArgIface *> &required,
187 std::vector<ArgIface *> &optional,
188 String::size_type &maxFlag,
189 String::size_type &maxName,
190 String::size_type &maxCommand,
191 bool requiredAllOfGroup)
const
193 GroupIface *g =
dynamic_cast<GroupIface *
>(arg.get());
198 commands.push_back(cmd);
200 String::size_type length = cmd->name().length() + (cmd->isWithValue() ? 3 + cmd->valueSpecifier().length() : 0);
202 if (length > maxCommand) {
207 requiredAllOfGroup =
true;
209 requiredAllOfGroup =
false;
212 auto f = std::bind(&HelpPrinter::sortArg,
214 std::placeholders::_1,
220 std::ref(maxCommand),
223 std::for_each(g->children().cbegin(), g->children().cend(), f);
225 if (arg->isRequired() || requiredAllOfGroup) {
226 required.push_back(arg.get());
228 optional.push_back(arg.get());
231 calcMaxFlagAndName(arg.get(), maxFlag, maxName);
235inline void HelpPrinter::printOnlyFor(
ArgIface *arg,
237 String::size_type beforeDescription,
238 String::size_type)
const
240 String::size_type pos = 0;
242 if (!arg->flag().empty()) {
245 to <<
'-' << arg->flag();
246 pos += arg->flag().length() + 1;
248 if (!arg->argumentName().empty()) {
251 }
else if (arg->isWithValue()) {
252 to <<
" <" << arg->valueSpecifier() <<
'>';
253 pos += arg->valueSpecifier().length() + 3;
256 printOffset(to, pos, 4);
260 if (!arg->argumentName().empty()) {
263 to <<
"--" << arg->argumentName();
264 pos += arg->argumentName().length() + 2;
266 if (arg->isWithValue()) {
267 to <<
" <" << arg->valueSpecifier() <<
'>';
268 pos += arg->valueSpecifier().length() + 3;
270 }
else if (arg->flag().empty() && arg->argumentName().empty()) {
274 pos += arg->name().length();
276 if (arg->isWithValue()) {
277 to <<
" <" << arg->valueSpecifier() <<
'>';
278 pos += arg->valueSpecifier().length() + 3;
282 printString(to, splitToWords(arg->description()), pos, beforeDescription, 0);
286 printDefaultValue(arg, to, 0, beforeDescription, 0);
298 static const Char dash =
SL(
'-');
300 if (!a1->name().empty() && !a2->name().empty()) {
301 if (*(a1->name().cbegin()) != dash && *(a2->name().cbegin()) == dash) {
303 }
else if (*(a1->name().cbegin()) == dash && *(a2->name().cbegin()) != dash) {
305 }
else if (a1->argumentName().empty() && !a2->argumentName().empty()) {
307 }
else if (!a1->argumentName().empty() && a2->argumentName().empty()) {
310 return (a1->name() < a2->name());
313 return (a1->name() < a2->name());
321 std::vector<ArgIface *> required;
322 std::vector<ArgIface *> optional;
323 std::vector<Command *> commands;
325 String::size_type maxFlag = 0;
326 String::size_type maxName = 0;
327 String::size_type maxCommand = 0;
329 bool requiredAllOfGroup =
false;
331 auto f = std::bind(&HelpPrinter::sortArg,
333 std::placeholders::_1,
339 std::ref(maxCommand),
342 std::for_each(m_cmdLine->arguments().cbegin(), m_cmdLine->arguments().cend(), f);
345 std::sort(required.begin(), required.end(), [](
const auto &a1,
const auto &a2) {
346 return details::argNameLess(a1, a2);
349 std::sort(optional.begin(), optional.end(), [](
const auto &a1,
const auto &a2) {
350 return details::argNameLess(a1, a2);
353 std::sort(commands.begin(), commands.end(), [](
const auto &c1,
const auto &c2) {
354 return details::argNameLess(c1, c2);
361 printString(to, splitToWords(m_appDescription), 0, 0, 0);
365 if (commands.empty()) {
368 usage.push_back(m_exeName);
370 bool requiredFlag =
true;
373 const StringList words = createUsageString(arg, requiredFlag);
380 std::for_each(required.cbegin(), required.cend(), createUsageAndAppend);
382 requiredFlag =
false;
384 std::for_each(optional.cbegin(), optional.cend(), createUsageAndAppend);
389 usage.push_back(m_cmdLine->positionalDescription().empty() ? c_positional
390 : m_cmdLine->positionalDescription());
393 printString(to, usage, 7, 7, 1);
399 usage.push_back(m_exeName);
400 usage.push_back(c_commands);
402 if (!optional.empty() || !required.empty()) {
403 usage.push_back(c_options);
407 usage.push_back(m_cmdLine->positionalDescription().empty() ? c_positional
408 : m_cmdLine->positionalDescription());
413 printString(to, usage, 7, 7, 1);
417 std::for_each(commands.cbegin(), commands.cend(), [&](
Command *cmd) {
418 String::size_type pos = 2;
420 to <<
" " << cmd->name();
422 pos += cmd->name().length();
424 if (cmd->isWithValue()) {
425 to <<
" <" << cmd->valueSpecifier() <<
">";
427 pos += 3 + cmd->valueSpecifier().length();
430 printString(to, splitToWords(cmd->
description()), pos, maxCommand + 1, 0);
438 auto printArg = std::bind(&HelpPrinter::printOnlyFor,
440 std::placeholders::_1,
442 (maxFlag == 1 ? maxName + 6 : (maxName + 6 > maxFlag + 1 ? maxName + 6 : maxFlag + 1)),
445 if (!required.empty()) {
446 to <<
"REQUIRED:" <<
"\n";
448 std::for_each(required.cbegin(), required.cend(), printArg);
451 if (!optional.empty()) {
452 to <<
"OPTIONAL:" <<
"\n";
454 std::for_each(optional.cbegin(), optional.cend(), printArg);
466 if (parent && !arg) {
467 arg = m_cmdLine->findArgument(name);
474 std::vector<ArgIface *> grequired;
475 std::vector<ArgIface *> goptional;
476 std::vector<Command *> gcommands;
478 String::size_type gmaxFlag = 0;
479 String::size_type gmaxName = 0;
480 String::size_type gmaxCommand = 0;
482 bool requiredAllOfGroup =
false;
484 auto gf = std::bind(&HelpPrinter::sortArg,
486 std::placeholders::_1,
492 std::ref(gmaxCommand),
495 std::for_each(m_cmdLine->arguments().cbegin(), m_cmdLine->arguments().cend(), gf);
498 std::sort(grequired.begin(), grequired.end(), [](
const auto &a1,
const auto &a2) {
499 return details::argNameLess(a1, a2);
502 std::sort(goptional.begin(), goptional.end(), [](
const auto &a1,
const auto &a2) {
503 return details::argNameLess(a1, a2);
510 std::vector<ArgIface *> required;
511 std::vector<ArgIface *> optional;
512 std::vector<Command *> commands;
514 String::size_type maxFlag = 0;
515 String::size_type maxName = 0;
516 String::size_type maxCommand = 0;
518 requiredAllOfGroup =
false;
520 auto f = std::bind(&HelpPrinter::sortArg,
522 std::placeholders::_1,
528 std::ref(maxCommand),
534 std::sort(required.begin(), required.end(), [](
const auto &a1,
const auto &a2) {
535 return details::argNameLess(a1, a2);
538 std::sort(optional.begin(), optional.end(), [](
const auto &a1,
const auto &a2) {
539 return details::argNameLess(a1, a2);
542 std::sort(commands.begin(), commands.end(), [](
const auto &c1,
const auto &c2) {
543 return details::argNameLess(c1, c2);
555 if (commands.empty()) {
556 to <<
"USAGE: " << name;
562 if (!grequired.empty() || !goptional.empty() || !required.empty() || !optional.empty()) {
568 to <<
"USAGE: " << name <<
" <command>";
570 if (!optional.empty() || !required.empty()) {
576 std::for_each(commands.cbegin(), commands.cend(), [&](
Command *c) {
577 String::size_type pos = 2;
579 to <<
" " << c->name();
581 pos += c->name().length();
583 if (c->isWithValue()) {
584 to <<
" <" << c->valueSpecifier() <<
">";
586 pos += 3 + c->valueSpecifier().length();
589 printString(to, splitToWords(c->
description()), pos, maxCommand + 1, 0);
599 std::bind(&HelpPrinter::printOnlyFor,
601 std::placeholders::_1,
603 (maxFlag == 1 ? maxName + 6 : (maxName + 6 > maxFlag + 1 ? maxName + 6 : maxFlag + 1)),
606 if (!required.empty()) {
607 to <<
"REQUIRED:" <<
"\n";
609 std::for_each(required.cbegin(), required.cend(), printArg);
612 if (!optional.empty()) {
613 to <<
"OPTIONAL:" <<
"\n";
615 std::for_each(optional.cbegin(), optional.cend(), printArg);
619 if (!grequired.empty() || !goptional.empty()) {
620 auto printGlobalArg =
621 std::bind(&HelpPrinter::printOnlyFor,
623 std::placeholders::_1,
625 (gmaxFlag == 1 ? gmaxName + 6 : (gmaxName + 6 > gmaxFlag + 1 ? gmaxName + 6 : gmaxFlag + 1)),
628 if (!grequired.empty()) {
629 to <<
"GLOBAL REQUIRED:" <<
"\n";
631 std::for_each(grequired.cbegin(), grequired.cend(), printGlobalArg);
634 if (!goptional.empty()) {
635 to <<
"GLOBAL OPTIONAL:" <<
"\n";
637 std::for_each(goptional.cbegin(), goptional.cend(), printGlobalArg);
649inline void HelpPrinter::printDefaultValue(ArgIface *arg,
651 String::size_type currentPos,
652 String::size_type leftMargin,
653 String::size_type rightMargin)
const
655 if (!arg->defaultValue().empty()) {
657 words.push_back(
String(
"Default"));
658 words.push_back(
String(
"value:"));
659 words.push_back(arg->defaultValue());
661 if (arg->valueSpecifier() !=
SL(
"arg")) {
662 words.push_back(arg->valueSpecifier());
665 printString(to, words, currentPos, leftMargin, rightMargin);
671inline void HelpPrinter::print(ArgIface *arg,
672 OutStreamType &to)
const
674 StringList usage = createUsageString(arg, arg->isRequired());
678 std::for_each(usage.cbegin(), usage.cend(), [&](
const String &s) {
684 printString(to, splitToWords(arg->longDescription()), 0, 7, 7);
688 printDefaultValue(arg, to, 0, 7, 7);
700 m_appDescription = desc;
711 m_lineLength = length;
725 usage.append(
SL(
"[ "));
728 if (!arg->
flag().empty()) {
729 usage.append(
SL(
"-"));
730 usage.append(arg->
flag());
733 usage.append(
SL(
","));
735 result.push_back(usage);
742 usage.append(
SL(
"--"));
745 usage.append(arg->
name());
749 usage.append(
SL(
" <"));
751 usage.append(
SL(
">"));
755 usage.append(
SL(
" ]"));
758 result.push_back(usage);
763static inline bool isSpaceChar(
const Char &c)
765 static const String spaceChars =
SL(
" \t");
767 return (spaceChars.find(c) != String::npos);
770inline StringList HelpPrinter::splitToWords(
const String &s)
const
775 const auto pushWord = [&word, &result]() {
777 result.push_back(word);
783 std::for_each(s.cbegin(), s.cend(), [&](
const Char &c) {
784 if (isSpaceChar(c)) {
790 result.push_back(String(1, c));
802inline void HelpPrinter::printString(OutStreamType &to,
803 const StringList &words,
804 String::size_type currentPos,
805 String::size_type leftMargin,
806 String::size_type rightMargin)
const
808 String::size_type maxLineLength = (rightMargin < m_lineLength ? m_lineLength - rightMargin : 0);
810 if (maxLineLength - leftMargin < 20) {
813 maxLineLength = m_lineLength;
820 String::size_type length = 0;
822 bool makeOffset = (currentPos < leftMargin);
824 const auto moveToNewLine = [&]() {
829 printOffset(to, currentPos, leftMargin);
834 std::function<void(
const String &)> printWord;
836 printWord = [&](
const String &word) {
837 const auto print = [&]() {
838 length += word.length();
842 if (length < maxLineLength) {
848 if (length + word.length() < maxLineLength) {
851 if (length != leftMargin) {
855 const auto available = maxLineLength - length;
857 if (available < word.length()) {
858 to << word.substr(0, available);
861 printWord(word.substr(available));
868 std::for_each(words.cbegin(), words.cend(), [&](
const String &word) {
870 printOffset(to, currentPos, leftMargin);
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 ...
@ HandlePositionalArguments
Handle positional arguments.
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.
std::unique_ptr< ArgIface, details::Deleter< ArgIface > > ArgPtr
Smart pointer to the argument.
void setCmdLine(CmdLine *cmd) override
Set command line.
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.
constexpr std::add_const< T >::type & asConst(T &t) noexcept
Adds const to non-const objects.
bool argNameLess(const T &a1, const T &a2)
std::ostream OutStreamType
Out stream type.
std::string String
String type.
@ AllOfGroup
"All of" group.
String::value_type Char
Char type.
std::vector< String > StringList
List of strings.
#define DISABLE_COPY(Class)
Macro for disabling copy.