diff --git a/src/ArgParser.cpp b/src/ArgParser.cpp index 8f6f578..89594d8 100644 --- a/src/ArgParser.cpp +++ b/src/ArgParser.cpp @@ -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; @@ -240,7 +241,7 @@ namespace YYCC::ArgParser { while (!al.IsEOF()) { // if we can not find any switches, return with error if (!al.IsSwitch(&is_long_name, &long_name, &short_name)) return false; - + // find corresponding argument by long name or short name. // if we can not find it, return with error. 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 } diff --git a/src/ArgParser.hpp b/src/ArgParser.hpp index e882994..ecdf2cd 100644 --- a/src/ArgParser.hpp +++ b/src/ArgParser.hpp @@ -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; } diff --git a/testbench/main.cpp b/testbench/main.cpp index a9aaca8..9f65520 100644 --- a/testbench/main.cpp +++ b/testbench/main.cpp @@ -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(-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 }