feat: finish ArgParser help text output

This commit is contained in:
yyc12345 2024-07-29 19:31:17 +08:00
parent d1c1743dc9
commit e8a0299fbc
3 changed files with 73 additions and 12 deletions

View File

@ -1,6 +1,7 @@
#include "ArgParser.hpp"
#include "EncodingHelper.hpp"
#include "ConsoleHelper.hpp"
#if YYCC_OS == YYCC_OS_WINDOWS
#include "WinImportPrefix.hpp"
@ -63,7 +64,7 @@ namespace YYCC::ArgParser {
++m_ArgumentsIterator;
}
const yycc_u8string& ArgumentList::Current() const {
const yycc_u8string& ArgumentList::Argument() const {
if (IsEOF()) throw std::runtime_error("attempt to get data on the tail of iterator.");
return *m_ArgumentsIterator;
}
@ -114,7 +115,7 @@ namespace YYCC::ArgParser {
return true;
}
bool ArgumentList::IsValue(yycc_u8string* val) const {
bool ArgumentList::IsParameter(yycc_u8string* val) const {
bool is_value = !IsSwitch();
if (is_value && val != nullptr)
*val = *m_ArgumentsIterator;
@ -288,6 +289,60 @@ namespace YYCC::ArgParser {
}
}
void OptionContext::Help() const {
// print summary and description if necessary
if (!m_Summary.empty())
YYCC::ConsoleHelper::WriteLine(m_Summary.c_str());
if (!m_Description.empty())
YYCC::ConsoleHelper::WriteLine(m_Description.c_str());
// blank line
YYCC::ConsoleHelper::WriteLine(YYCC_U8(""));
// print argument list
for (const auto* arg : m_Arguments) {
yycc_u8string argstr;
// print indent
argstr += YYCC_U8("\t");
// print optional head
bool is_optional = arg->IsOptional();
if (is_optional) argstr += YYCC_U8("[");
// switch name
bool short_name = arg->HasShortName(), long_name = arg->HasLongName();
if (short_name) {
argstr += YYCC_U8("-");
argstr += arg->GetShortName();
}
if (long_name) {
if (short_name) argstr += YYCC_U8(", ");
argstr += YYCC_U8("--");
argstr += arg->GetLongName();
}
// argument example
if (arg->HasArgumentExample()) {
argstr += YYCC_U8(" ");
argstr += arg->GetArgumentExample();
}
// optional tail
if (is_optional) argstr += YYCC_U8("]");
// argument description
if (arg->HasDescription()) {
// eol and double indent
argstr += YYCC_U8("\n\t\t");
// description
argstr += arg->GetDescription();
}
// write into console
YYCC::ConsoleHelper::WriteLine(argstr.c_str());
}
}
#pragma endregion
}

View File

@ -28,12 +28,12 @@ namespace YYCC::ArgParser {
public:
void Prev();
void Next();
const yycc_u8string& Current() const;
const yycc_u8string& Argument() const;
bool IsSwitch(
bool* is_long_name = nullptr,
yycc_u8string* long_name = nullptr,
yycc_char8_t* short_name = nullptr) const;
bool IsValue(yycc_u8string* val = nullptr) const;
bool IsParameter(yycc_u8string* val = nullptr) const;
bool IsEOF() const;
void Reset();
private:
@ -104,6 +104,7 @@ namespace YYCC::ArgParser {
public:
bool Parse(ArgumentList& al);
void Reset();
void Help() const;
private:
yycc_u8string m_Summary;
@ -138,7 +139,7 @@ namespace YYCC::ArgParser {
// try get corresponding value
yycc_u8string strval;
al.Next();
if (al.IsEOF() || !al.IsValue(&strval)) {
if (al.IsEOF() || !al.IsParameter(&strval)) {
al.Prev();
return false;
}
@ -163,8 +164,10 @@ namespace YYCC::ArgParser {
public:
SwitchArgument(
const yycc_char8_t* long_name, yycc_char8_t short_name,
const yycc_char8_t* description = nullptr, const yycc_char8_t* argument_example = nullptr) :
AbstractArgument(long_name, short_name, description, argument_example, true), m_Data(false) {} // bool switch must be optional, because it is false if no given switch.
const yycc_char8_t* description = nullptr) :
// bool switch must be optional, because it is false if no given switch.
// bool switch doesn't have argument, so it doesn't have example property.
AbstractArgument(long_name, short_name, description, nullptr, true), m_Data(false) {}
virtual ~SwitchArgument() {}
public:
@ -201,7 +204,7 @@ namespace YYCC::ArgParser {
virtual bool Parse(ArgumentList& al) override {
// try get corresponding value
al.Next();
if (al.IsEOF() || !al.IsValue(&m_Data)) {
if (al.IsEOF() || !al.IsParameter(&m_Data)) {
al.Prev();
return false;
}

View File

@ -487,7 +487,7 @@ namespace YYCCTestbench {
m_IntArgument(YYCC_U8("int"), YYCC_U8_CHAR('i'), YYCC_U8("integral argument"), YYCC_U8("114514")),
m_FloatArgument(nullptr, YYCC_U8_CHAR('f'), nullptr, nullptr, true),
m_StringArgument(YYCC_U8("string"), YYCC::ArgParser::AbstractArgument::NO_SHORT_NAME, nullptr, nullptr, true),
m_BoolArgument(nullptr, YYCC_U8_CHAR('b'), nullptr, nullptr),
m_BoolArgument(nullptr, YYCC_U8_CHAR('b'), nullptr),
m_ClampedFloatArgument(YYCC_U8("clamped-float"), YYCC::ArgParser::AbstractArgument::NO_SHORT_NAME, nullptr, nullptr, true, YYCC::Constraints::GetMinMaxRangeConstraint<float>(-1.0f, 1.0f)),
m_OptionContext(YYCC_U8("TestArgParser"), YYCC_U8("This is the testbench of argument parser."), {
&m_IntArgument, &m_FloatArgument, &m_StringArgument,
@ -511,7 +511,7 @@ namespace YYCCTestbench {
YYCC::ConsoleHelper::WriteLine(YYCC_U8("YYCC::ArgParser::ArgumentList::CreateFromStd"));
auto result = YYCC::ArgParser::ArgumentList::CreateFromStd(argc, argv);
for (result.Reset(); !result.IsEOF(); result.Next()) {
YYCC::ConsoleHelper::FormatLine(YYCC_U8("\t%s"), result.Current().c_str());
YYCC::ConsoleHelper::FormatLine(YYCC_U8("\t%s"), result.Argument().c_str());
}
}
#if YYCC_OS == YYCC_OS_WINDOWS
@ -519,7 +519,7 @@ namespace YYCCTestbench {
YYCC::ConsoleHelper::WriteLine(YYCC_U8("YYCC::ArgParser::ArgumentList::CreateFromWin32"));
auto result = YYCC::ArgParser::ArgumentList::CreateFromWin32();
for (result.Reset(); !result.IsEOF(); result.Next()) {
YYCC::ConsoleHelper::FormatLine(YYCC_U8("\t%s"), result.Current().c_str());
YYCC::ConsoleHelper::FormatLine(YYCC_U8("\t%s"), result.Argument().c_str());
}
}
#endif
@ -581,6 +581,9 @@ auto al = YYCC::ArgParser::ArgumentList::CreateFromStd(sizeof(test_argv) / sizeo
test.m_OptionContext.Reset();
}
// Help text
test.m_OptionContext.Help();
#undef PREPARE_DATA
}