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 "ArgParser.hpp"
#include "EncodingHelper.hpp" #include "EncodingHelper.hpp"
#include "ConsoleHelper.hpp"
#if YYCC_OS == YYCC_OS_WINDOWS #if YYCC_OS == YYCC_OS_WINDOWS
#include "WinImportPrefix.hpp" #include "WinImportPrefix.hpp"
@ -63,7 +64,7 @@ namespace YYCC::ArgParser {
++m_ArgumentsIterator; ++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."); if (IsEOF()) throw std::runtime_error("attempt to get data on the tail of iterator.");
return *m_ArgumentsIterator; return *m_ArgumentsIterator;
} }
@ -114,7 +115,7 @@ namespace YYCC::ArgParser {
return true; return true;
} }
bool ArgumentList::IsValue(yycc_u8string* val) const { bool ArgumentList::IsParameter(yycc_u8string* val) const {
bool is_value = !IsSwitch(); bool is_value = !IsSwitch();
if (is_value && val != nullptr) if (is_value && val != nullptr)
*val = *m_ArgumentsIterator; *val = *m_ArgumentsIterator;
@ -240,7 +241,7 @@ namespace YYCC::ArgParser {
while (!al.IsEOF()) { while (!al.IsEOF()) {
// if we can not find any switches, return with error // if we can not find any switches, return with error
if (!al.IsSwitch(&is_long_name, &long_name, &short_name)) return false; if (!al.IsSwitch(&is_long_name, &long_name, &short_name)) return false;
// find corresponding argument by long name or short name. // find corresponding argument by long name or short name.
// if we can not find it, return with error. // if we can not find it, return with error.
AbstractArgument* arg; AbstractArgument* arg;
@ -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 #pragma endregion
} }

View File

@ -28,12 +28,12 @@ namespace YYCC::ArgParser {
public: public:
void Prev(); void Prev();
void Next(); void Next();
const yycc_u8string& Current() const; const yycc_u8string& Argument() const;
bool IsSwitch( bool IsSwitch(
bool* is_long_name = nullptr, bool* is_long_name = nullptr,
yycc_u8string* long_name = nullptr, yycc_u8string* long_name = nullptr,
yycc_char8_t* short_name = nullptr) const; yycc_char8_t* short_name = nullptr) const;
bool IsValue(yycc_u8string* val = nullptr) const; bool IsParameter(yycc_u8string* val = nullptr) const;
bool IsEOF() const; bool IsEOF() const;
void Reset(); void Reset();
private: private:
@ -104,6 +104,7 @@ namespace YYCC::ArgParser {
public: public:
bool Parse(ArgumentList& al); bool Parse(ArgumentList& al);
void Reset(); void Reset();
void Help() const;
private: private:
yycc_u8string m_Summary; yycc_u8string m_Summary;
@ -138,7 +139,7 @@ namespace YYCC::ArgParser {
// try get corresponding value // try get corresponding value
yycc_u8string strval; yycc_u8string strval;
al.Next(); al.Next();
if (al.IsEOF() || !al.IsValue(&strval)) { if (al.IsEOF() || !al.IsParameter(&strval)) {
al.Prev(); al.Prev();
return false; return false;
} }
@ -163,8 +164,10 @@ namespace YYCC::ArgParser {
public: public:
SwitchArgument( SwitchArgument(
const yycc_char8_t* long_name, yycc_char8_t short_name, const yycc_char8_t* long_name, yycc_char8_t short_name,
const yycc_char8_t* description = nullptr, const yycc_char8_t* argument_example = nullptr) : const yycc_char8_t* description = 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. // 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() {} virtual ~SwitchArgument() {}
public: public:
@ -201,7 +204,7 @@ namespace YYCC::ArgParser {
virtual bool Parse(ArgumentList& al) override { virtual bool Parse(ArgumentList& al) override {
// try get corresponding value // try get corresponding value
al.Next(); al.Next();
if (al.IsEOF() || !al.IsValue(&m_Data)) { if (al.IsEOF() || !al.IsParameter(&m_Data)) {
al.Prev(); al.Prev();
return false; 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_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_FloatArgument(nullptr, YYCC_U8_CHAR('f'), nullptr, nullptr, true),
m_StringArgument(YYCC_U8("string"), YYCC::ArgParser::AbstractArgument::NO_SHORT_NAME, 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_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_OptionContext(YYCC_U8("TestArgParser"), YYCC_U8("This is the testbench of argument parser."), {
&m_IntArgument, &m_FloatArgument, &m_StringArgument, &m_IntArgument, &m_FloatArgument, &m_StringArgument,
@ -511,7 +511,7 @@ namespace YYCCTestbench {
YYCC::ConsoleHelper::WriteLine(YYCC_U8("YYCC::ArgParser::ArgumentList::CreateFromStd")); YYCC::ConsoleHelper::WriteLine(YYCC_U8("YYCC::ArgParser::ArgumentList::CreateFromStd"));
auto result = YYCC::ArgParser::ArgumentList::CreateFromStd(argc, argv); auto result = YYCC::ArgParser::ArgumentList::CreateFromStd(argc, argv);
for (result.Reset(); !result.IsEOF(); result.Next()) { 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 #if YYCC_OS == YYCC_OS_WINDOWS
@ -519,7 +519,7 @@ namespace YYCCTestbench {
YYCC::ConsoleHelper::WriteLine(YYCC_U8("YYCC::ArgParser::ArgumentList::CreateFromWin32")); YYCC::ConsoleHelper::WriteLine(YYCC_U8("YYCC::ArgParser::ArgumentList::CreateFromWin32"));
auto result = YYCC::ArgParser::ArgumentList::CreateFromWin32(); auto result = YYCC::ArgParser::ArgumentList::CreateFromWin32();
for (result.Reset(); !result.IsEOF(); result.Next()) { 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 #endif
@ -581,6 +581,9 @@ auto al = YYCC::ArgParser::ArgumentList::CreateFromStd(sizeof(test_argv) / sizeo
test.m_OptionContext.Reset(); test.m_OptionContext.Reset();
} }
// Help text
test.m_OptionContext.Help();
#undef PREPARE_DATA #undef PREPARE_DATA
} }