feat: basically finish ArgParser
- finish ArgParser and test it. - Help output function will be added in next commit.
This commit is contained in:
parent
35318505e4
commit
d1c1743dc9
|
@ -16,7 +16,7 @@ namespace YYCC::ArgParser {
|
||||||
|
|
||||||
ArgumentList ArgumentList::CreateFromStd(int argc, char* argv[]) {
|
ArgumentList ArgumentList::CreateFromStd(int argc, char* argv[]) {
|
||||||
std::vector<yycc_u8string> args;
|
std::vector<yycc_u8string> args;
|
||||||
for (int i = 0; i < argc; ++i) {
|
for (int i = 1; i < argc; ++i) { // starts with 1 to remove first part (executable self)
|
||||||
if (argv[i] != nullptr)
|
if (argv[i] != nullptr)
|
||||||
args.emplace_back(yycc_u8string(YYCC::EncodingHelper::ToUTF8(argv[i])));
|
args.emplace_back(yycc_u8string(YYCC::EncodingHelper::ToUTF8(argv[i])));
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ namespace YYCC::ArgParser {
|
||||||
int argc;
|
int argc;
|
||||||
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
||||||
if (argv != NULL) {
|
if (argv != NULL) {
|
||||||
for (int i = 0; i < argc; ++i) {
|
for (int i = 1; i < argc; ++i) { // starts with 1 to remove first part (executable self)
|
||||||
if (argv[i] != nullptr) {
|
if (argv[i] != nullptr) {
|
||||||
yycc_u8string u8_argv;
|
yycc_u8string u8_argv;
|
||||||
if (YYCC::EncodingHelper::WcharToUTF8(argv[i], u8_argv))
|
if (YYCC::EncodingHelper::WcharToUTF8(argv[i], u8_argv))
|
||||||
|
@ -63,44 +63,63 @@ namespace YYCC::ArgParser {
|
||||||
++m_ArgumentsIterator;
|
++m_ArgumentsIterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsLongName(const yycc_u8string& param, yycc_u8string_view* name_part) {
|
const yycc_u8string& ArgumentList::Current() const {
|
||||||
if (param.find(AbstractArgument::DOUBLE_DASH) != 0u) return false;
|
if (IsEOF()) throw std::runtime_error("attempt to get data on the tail of iterator.");
|
||||||
if (name_part != nullptr)
|
return *m_ArgumentsIterator;
|
||||||
*name_part = yycc_u8string_view(param).substr(2u);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
static bool IsShortName(const yycc_u8string& param, yycc_char8_t* name_part) {
|
|
||||||
if (param.size() != 2u ||
|
bool ArgumentList::IsSwitch(bool* is_long_name, yycc_u8string* long_name, yycc_char8_t* short_name) const {
|
||||||
param[0] != AbstractArgument::DASH ||
|
// check eof first
|
||||||
param[1] == AbstractArgument::DASH ||
|
|
||||||
param[1] < AbstractArgument::MIN_SHORT_NAME || param[1] > AbstractArgument::MAX_SHORT_NAME) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (name_part != nullptr)
|
|
||||||
*name_part = param[1];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool ArgumentList::IsSwitch(
|
|
||||||
bool* is_long_name = nullptr,
|
|
||||||
yycc_u8string_view* long_name = nullptr,
|
|
||||||
yycc_char8_t* short_name = nullptr) const {
|
|
||||||
// get argument first
|
|
||||||
if (IsEOF()) throw std::runtime_error("attempt to fetch data on the tail of iterator.");
|
if (IsEOF()) throw std::runtime_error("attempt to fetch data on the tail of iterator.");
|
||||||
const auto& param = *m_ArgumentsIterator;
|
|
||||||
// check long name first, then check short name
|
// check long name first, then check short name
|
||||||
if (IsLongName(param, long_name)) {
|
if (IsLongNameSwitch(long_name)) {
|
||||||
if (is_long_name != nullptr) *is_long_name = true;
|
if (is_long_name != nullptr) *is_long_name = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (IsShortName(param, short_name)) {
|
if (IsShortNameSwitch(short_name)) {
|
||||||
if (is_long_name != nullptr) *is_long_name = false;
|
if (is_long_name != nullptr) *is_long_name = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// not matched
|
// not matched
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
bool ArgumentList::IsLongNameSwitch(yycc_u8string* name_part) const {
|
||||||
|
// fetch current parameter
|
||||||
|
if (IsEOF()) throw std::runtime_error("attempt to fetch data on the tail of iterator.");
|
||||||
|
const yycc_u8string& param = *m_ArgumentsIterator;
|
||||||
|
// find double slash
|
||||||
|
if (param.find(AbstractArgument::DOUBLE_DASH) != 0u) return false;
|
||||||
|
// check gotten long name
|
||||||
|
yycc_u8string_view long_name = yycc_u8string_view(param).substr(2u);
|
||||||
|
if (!AbstractArgument::IsLegalLongName(long_name)) return false;
|
||||||
|
// set checked long name if possible and return
|
||||||
|
if (name_part != nullptr)
|
||||||
|
*name_part = long_name;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool ArgumentList::IsShortNameSwitch(yycc_char8_t* name_part) const {
|
||||||
|
// fetch current parameter
|
||||||
|
if (IsEOF()) throw std::runtime_error("attempt to fetch data on the tail of iterator.");
|
||||||
|
const yycc_u8string& param = *m_ArgumentsIterator;
|
||||||
|
// if the length is not exactly equal to 2,
|
||||||
|
// or it not starts with dash,
|
||||||
|
// it is impossible a short name
|
||||||
|
if (param.size() != 2u || param[0] != AbstractArgument::DASH) return false;
|
||||||
|
// check gotten short name
|
||||||
|
yycc_char8_t short_name = param[1];
|
||||||
|
if (!AbstractArgument::IsLegalShortName(short_name)) return false;
|
||||||
|
// set checked short name if possible and return
|
||||||
|
if (name_part != nullptr)
|
||||||
|
*name_part = short_name;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool ArgumentList::IsValue() const { return !IsSwitch(); }
|
bool ArgumentList::IsValue(yycc_u8string* val) const {
|
||||||
|
bool is_value = !IsSwitch();
|
||||||
|
if (is_value && val != nullptr)
|
||||||
|
*val = *m_ArgumentsIterator;
|
||||||
|
return is_value;
|
||||||
|
}
|
||||||
|
|
||||||
bool ArgumentList::IsEOF() const { return m_ArgumentsIterator == m_Arguments.end(); }
|
bool ArgumentList::IsEOF() const { return m_ArgumentsIterator == m_Arguments.end(); }
|
||||||
|
|
||||||
|
@ -116,23 +135,46 @@ namespace YYCC::ArgParser {
|
||||||
const yycc_char8_t AbstractArgument::MIN_SHORT_NAME = YYCC_U8_CHAR('!');
|
const yycc_char8_t AbstractArgument::MIN_SHORT_NAME = YYCC_U8_CHAR('!');
|
||||||
const yycc_char8_t AbstractArgument::MAX_SHORT_NAME = YYCC_U8_CHAR('~');
|
const yycc_char8_t AbstractArgument::MAX_SHORT_NAME = YYCC_U8_CHAR('~');
|
||||||
|
|
||||||
|
bool AbstractArgument::IsLegalShortName(yycc_char8_t short_name) {
|
||||||
|
if (short_name == AbstractArgument::DASH || // dash is not allowed
|
||||||
|
short_name < AbstractArgument::MIN_SHORT_NAME || short_name > AbstractArgument::MAX_SHORT_NAME) { // non-display ASCII chars are not allowed
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// okey
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool AbstractArgument::IsLegalLongName(const yycc_u8string_view& long_name) {
|
||||||
|
// empty is not allowed
|
||||||
|
if (long_name.empty()) return false;
|
||||||
|
// non-display ASCII chars are not allowed
|
||||||
|
for (const auto& val : long_name) {
|
||||||
|
if (val < AbstractArgument::MIN_SHORT_NAME || val > AbstractArgument::MAX_SHORT_NAME)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// okey
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
AbstractArgument::AbstractArgument(
|
AbstractArgument::AbstractArgument(
|
||||||
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, const yycc_char8_t* argument_example,
|
const yycc_char8_t* description, const yycc_char8_t* argument_example,
|
||||||
bool is_optional) :
|
bool is_optional) :
|
||||||
m_LongName(), m_ShortName(NO_SHORT_NAME), m_Description(), m_ArgumentExample(),
|
m_LongName(), m_ShortName(AbstractArgument::NO_SHORT_NAME), m_Description(), m_ArgumentExample(),
|
||||||
m_IsOptional(is_optional), m_IsCaptured(false) {
|
m_IsOptional(is_optional), m_IsCaptured(false) {
|
||||||
|
|
||||||
// try to assign long name
|
// try to assign long name and check it
|
||||||
if (long_name != nullptr) m_LongName = long_name;
|
if (long_name != nullptr) {
|
||||||
// try to assign short name
|
m_LongName = long_name;
|
||||||
if (short_name == AbstractArgument::DASH ||
|
if (!AbstractArgument::IsLegalLongName(m_LongName))
|
||||||
short_name < AbstractArgument::MIN_SHORT_NAME ||
|
throw std::invalid_argument("Given long name is invalid.");
|
||||||
short_name > AbstractArgument::MAX_SHORT_NAME) {
|
|
||||||
throw std::invalid_argument("given short name character is invalid.");
|
|
||||||
}
|
}
|
||||||
m_ShortName = short_name;
|
// try to assign short name and check it
|
||||||
// check short name and long name
|
if (short_name != AbstractArgument::NO_SHORT_NAME) {
|
||||||
|
m_ShortName = short_name;
|
||||||
|
if (!AbstractArgument::IsLegalShortName(m_ShortName))
|
||||||
|
throw std::invalid_argument("Given short name is invalid.");
|
||||||
|
}
|
||||||
|
// check short name and long name existence
|
||||||
if (!HasShortName() && !HasLongName())
|
if (!HasShortName() && !HasLongName())
|
||||||
throw std::invalid_argument("you must specify an one of long name or short name.");
|
throw std::invalid_argument("you must specify an one of long name or short name.");
|
||||||
|
|
||||||
|
@ -187,8 +229,55 @@ namespace YYCC::ArgParser {
|
||||||
|
|
||||||
OptionContext::~OptionContext() {}
|
OptionContext::~OptionContext() {}
|
||||||
|
|
||||||
bool OptionContext::Parse(ArgumentList* al) {
|
bool OptionContext::Parse(ArgumentList& al) {
|
||||||
return false; //todo
|
// reset argument list first
|
||||||
|
al.Reset();
|
||||||
|
|
||||||
|
// prepare variables and start loop
|
||||||
|
yycc_u8string long_name;
|
||||||
|
yycc_char8_t short_name;
|
||||||
|
bool is_long_name;
|
||||||
|
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;
|
||||||
|
if (is_long_name) {
|
||||||
|
auto finder = m_LongNameMap.find(long_name);
|
||||||
|
if (finder == m_LongNameMap.end()) return false;
|
||||||
|
arg = finder->second;
|
||||||
|
} else {
|
||||||
|
auto finder = m_ShortNameMap.find(short_name);
|
||||||
|
if (finder == m_ShortNameMap.end()) return false;
|
||||||
|
arg = finder->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this argument has been captured, raise error
|
||||||
|
if (arg->IsCaptured()) return false;
|
||||||
|
// call user parse function of found argument
|
||||||
|
if (arg->Parse(al)) {
|
||||||
|
// success. mark it is captured
|
||||||
|
arg->SetCaptured(true);
|
||||||
|
} else {
|
||||||
|
// failed, return error
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// move to next argument
|
||||||
|
al.Next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// after processing all argument,
|
||||||
|
// we should check whether all non-optional argument are captured.
|
||||||
|
for (const auto* arg : m_Arguments) {
|
||||||
|
if (!arg->IsOptional() && !arg->IsCaptured())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// okey
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OptionContext::Reset() {
|
void OptionContext::Reset() {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "Constraints.hpp"
|
#include "Constraints.hpp"
|
||||||
#include "EncodingHelper.hpp"
|
#include "EncodingHelper.hpp"
|
||||||
|
#include "ParserHelper.hpp"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
@ -18,17 +19,26 @@ namespace YYCC::ArgParser {
|
||||||
#endif
|
#endif
|
||||||
private:
|
private:
|
||||||
ArgumentList(std::vector<yycc_u8string>&& arguments);
|
ArgumentList(std::vector<yycc_u8string>&& arguments);
|
||||||
|
public:
|
||||||
|
ArgumentList(const ArgumentList&) = default;
|
||||||
|
ArgumentList& operator=(const ArgumentList&) = default;
|
||||||
|
ArgumentList(ArgumentList&&) = default;
|
||||||
|
ArgumentList& operator=(ArgumentList&&) = default;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void Prev();
|
void Prev();
|
||||||
void Next();
|
void Next();
|
||||||
|
const yycc_u8string& Current() const;
|
||||||
bool IsSwitch(
|
bool IsSwitch(
|
||||||
bool* is_long_name = nullptr,
|
bool* is_long_name = nullptr,
|
||||||
yycc_u8string_view* long_name = nullptr,
|
yycc_u8string* long_name = nullptr,
|
||||||
yycc_char8_t* short_name = nullptr) const;
|
yycc_char8_t* short_name = nullptr) const;
|
||||||
bool IsValue() const;
|
bool IsValue(yycc_u8string* val = nullptr) const;
|
||||||
bool IsEOF() const;
|
bool IsEOF() const;
|
||||||
void Reset();
|
void Reset();
|
||||||
|
private:
|
||||||
|
bool IsLongNameSwitch(yycc_u8string* name_part = nullptr) const;
|
||||||
|
bool IsShortNameSwitch(yycc_char8_t* name_part = nullptr) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<yycc_u8string> m_Arguments;
|
std::vector<yycc_u8string> m_Arguments;
|
||||||
|
@ -37,12 +47,18 @@ namespace YYCC::ArgParser {
|
||||||
|
|
||||||
class AbstractArgument {
|
class AbstractArgument {
|
||||||
friend class OptionContext;
|
friend class OptionContext;
|
||||||
|
|
||||||
|
// Long name and short name constants and checker.
|
||||||
public:
|
public:
|
||||||
static const yycc_u8string DOUBLE_DASH;
|
static const yycc_u8string DOUBLE_DASH;
|
||||||
static const yycc_char8_t DASH;
|
static const yycc_char8_t DASH;
|
||||||
static const yycc_char8_t NO_SHORT_NAME;
|
static const yycc_char8_t NO_SHORT_NAME;
|
||||||
static const yycc_char8_t MIN_SHORT_NAME;
|
static const yycc_char8_t MIN_SHORT_NAME;
|
||||||
static const yycc_char8_t MAX_SHORT_NAME;
|
static const yycc_char8_t MAX_SHORT_NAME;
|
||||||
|
static bool IsLegalShortName(yycc_char8_t short_name);
|
||||||
|
static bool IsLegalLongName(const yycc_u8string_view& long_name);
|
||||||
|
|
||||||
|
// Constructor & destructor
|
||||||
public:
|
public:
|
||||||
AbstractArgument(
|
AbstractArgument(
|
||||||
const yycc_char8_t* long_name, yycc_char8_t short_name = AbstractArgument::NO_SHORT_NAME,
|
const yycc_char8_t* long_name, yycc_char8_t short_name = AbstractArgument::NO_SHORT_NAME,
|
||||||
|
@ -86,7 +102,7 @@ namespace YYCC::ArgParser {
|
||||||
~OptionContext();
|
~OptionContext();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool Parse(ArgumentList* al);
|
bool Parse(ArgumentList& al);
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -118,8 +134,25 @@ namespace YYCC::ArgParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool Parse(ArgumentList& al) override {} // todo
|
virtual bool Parse(ArgumentList& al) override {
|
||||||
virtual void Reset() override {}// todo
|
// try get corresponding value
|
||||||
|
yycc_u8string strval;
|
||||||
|
al.Next();
|
||||||
|
if (al.IsEOF() || !al.IsValue(&strval)) {
|
||||||
|
al.Prev();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// try parsing value
|
||||||
|
if (!YYCC::ParserHelper::TryParse<_Ty>(strval, m_Data)) return false;
|
||||||
|
// check constraint
|
||||||
|
if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(m_Data))
|
||||||
|
return false;
|
||||||
|
// okey
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
virtual void Reset() override {
|
||||||
|
std::memset(&m_Data, 0, sizeof(m_Data));
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
_Ty m_Data;
|
_Ty m_Data;
|
||||||
|
@ -138,7 +171,10 @@ namespace YYCC::ArgParser {
|
||||||
bool Get() const { return m_Data; }
|
bool Get() const { return m_Data; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool Parse(ArgumentList& al) override { m_Data = true; }
|
virtual bool Parse(ArgumentList& al) override {
|
||||||
|
m_Data = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
virtual void Reset() override { m_Data = false; }
|
virtual void Reset() override { m_Data = false; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -151,7 +187,7 @@ namespace YYCC::ArgParser {
|
||||||
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, const yycc_char8_t* argument_example = nullptr,
|
||||||
bool is_optional = false,
|
bool is_optional = false,
|
||||||
Constraints::Constraint<yycc_u8string_view> constraint = Constraints::Constraint<yycc_u8string_view> {}) :
|
Constraints::Constraint<yycc_u8string> constraint = Constraints::Constraint<yycc_u8string> {}) :
|
||||||
AbstractArgument(long_name, short_name, description, argument_example, is_optional), m_Data(), m_Constraint(constraint) {}
|
AbstractArgument(long_name, short_name, description, argument_example, is_optional), m_Data(), m_Constraint(constraint) {}
|
||||||
virtual ~StringArgument() {}
|
virtual ~StringArgument() {}
|
||||||
|
|
||||||
|
@ -162,12 +198,26 @@ namespace YYCC::ArgParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool Parse(ArgumentList& al) override {} // todo
|
virtual bool Parse(ArgumentList& al) override {
|
||||||
virtual void Reset() override {}// todo
|
// try get corresponding value
|
||||||
|
al.Next();
|
||||||
|
if (al.IsEOF() || !al.IsValue(&m_Data)) {
|
||||||
|
al.Prev();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// check constraint
|
||||||
|
if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(m_Data))
|
||||||
|
return false;
|
||||||
|
// okey
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
virtual void Reset() override {
|
||||||
|
m_Data.clear();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
yycc_u8string m_Data;
|
yycc_u8string m_Data;
|
||||||
Constraints::Constraint<yycc_u8string_view> m_Constraint;
|
Constraints::Constraint<yycc_u8string> m_Constraint;
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
|
@ -185,7 +185,7 @@ namespace YYCC::ConfigManager {
|
||||||
*/
|
*/
|
||||||
StringSetting(
|
StringSetting(
|
||||||
const yycc_char8_t* name, const yycc_u8string_view& default_value,
|
const yycc_char8_t* name, const yycc_u8string_view& default_value,
|
||||||
Constraints::Constraint<yycc_u8string_view> constraint = Constraints::Constraint<yycc_u8string_view> {}) :
|
Constraints::Constraint<yycc_u8string> constraint = Constraints::Constraint<yycc_u8string> {}) :
|
||||||
AbstractSetting(name), m_Data(), m_DefaultData(), m_Constraint(constraint) {
|
AbstractSetting(name), m_Data(), m_DefaultData(), m_Constraint(constraint) {
|
||||||
m_Data = default_value;
|
m_Data = default_value;
|
||||||
m_DefaultData = default_value;
|
m_DefaultData = default_value;
|
||||||
|
@ -201,10 +201,11 @@ namespace YYCC::ConfigManager {
|
||||||
*/
|
*/
|
||||||
bool Set(const yycc_u8string_view& new_data) {
|
bool Set(const yycc_u8string_view& new_data) {
|
||||||
// check data validation
|
// check data validation
|
||||||
if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(new_data))
|
yycc_u8string new_data_cache(new_data);
|
||||||
|
if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(new_data_cache))
|
||||||
return false;
|
return false;
|
||||||
// assign data
|
// assign data
|
||||||
m_Data = new_data;
|
m_Data = std::move(new_data_cache);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,7 +245,7 @@ namespace YYCC::ConfigManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
yycc_u8string m_Data, m_DefaultData;
|
yycc_u8string m_Data, m_DefaultData;
|
||||||
Constraints::Constraint<yycc_u8string_view> m_Constraint;
|
Constraints::Constraint<yycc_u8string> m_Constraint;
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
|
@ -73,10 +73,10 @@ namespace YYCC::Constraints {
|
||||||
* Caller must make sure that the string view passed in initializer list is valid until this Constraint life time gone.
|
* Caller must make sure that the string view passed in initializer list is valid until this Constraint life time gone.
|
||||||
* Becasue this generator will not copy your given string view into string.
|
* Becasue this generator will not copy your given string view into string.
|
||||||
*/
|
*/
|
||||||
Constraint<yycc_u8string_view> GetStringEnumerationConstraint(const std::initializer_list<yycc_u8string_view>& il) {
|
inline Constraint<yycc_u8string> GetStringEnumerationConstraint(const std::initializer_list<yycc_u8string_view>& il) {
|
||||||
std::set<yycc_u8string_view> data(il);
|
std::set<yycc_u8string_view> data(il);
|
||||||
return Constraint<yycc_u8string_view> {
|
return Constraint<yycc_u8string> {
|
||||||
[data](const yycc_u8string_view& val) -> bool { return data.find(val) != data.end(); }
|
[data](const yycc_u8string& val) -> bool { return data.find(yycc_u8string_view(val)) != data.end(); }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,3 +14,4 @@
|
||||||
#include "ExceptionHelper.hpp"
|
#include "ExceptionHelper.hpp"
|
||||||
|
|
||||||
#include "ConfigManager.hpp"
|
#include "ConfigManager.hpp"
|
||||||
|
#include "ArgParser.hpp"
|
||||||
|
|
|
@ -391,12 +391,12 @@ namespace YYCCTestbench {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class TestEnum : int8_t {
|
||||||
|
Test1, Test2, Test3
|
||||||
|
};
|
||||||
|
|
||||||
class TestConfigManager {
|
class TestConfigManager {
|
||||||
public:
|
public:
|
||||||
enum class TestEnum : int8_t {
|
|
||||||
Test1, Test2, Test3
|
|
||||||
};
|
|
||||||
|
|
||||||
TestConfigManager() :
|
TestConfigManager() :
|
||||||
m_IntSetting(YYCC_U8("int-setting"), INT32_C(0)),
|
m_IntSetting(YYCC_U8("int-setting"), INT32_C(0)),
|
||||||
m_FloatSetting(YYCC_U8("float-setting"), 0.0f),
|
m_FloatSetting(YYCC_U8("float-setting"), 0.0f),
|
||||||
|
@ -406,8 +406,7 @@ namespace YYCCTestbench {
|
||||||
m_EnumSetting(YYCC_U8("enum-setting"), TestEnum::Test1),
|
m_EnumSetting(YYCC_U8("enum-setting"), TestEnum::Test1),
|
||||||
m_CoreManager(YYCC_U8("test.cfg"), UINT64_C(0), {
|
m_CoreManager(YYCC_U8("test.cfg"), UINT64_C(0), {
|
||||||
&m_IntSetting, &m_FloatSetting, &m_StringSetting, &m_BoolSetting, &m_ClampedFloatSetting, &m_EnumSetting
|
&m_IntSetting, &m_FloatSetting, &m_StringSetting, &m_BoolSetting, &m_ClampedFloatSetting, &m_EnumSetting
|
||||||
})
|
}) {}
|
||||||
{}
|
|
||||||
~TestConfigManager() {}
|
~TestConfigManager() {}
|
||||||
|
|
||||||
void PrintSettings() {
|
void PrintSettings() {
|
||||||
|
@ -452,7 +451,7 @@ namespace YYCCTestbench {
|
||||||
TEST_MACRO(m_StringSetting, YYCC_U8("fuck"));
|
TEST_MACRO(m_StringSetting, YYCC_U8("fuck"));
|
||||||
TEST_MACRO(m_BoolSetting, true);
|
TEST_MACRO(m_BoolSetting, true);
|
||||||
TEST_MACRO(m_ClampedFloatSetting, 0.5f);
|
TEST_MACRO(m_ClampedFloatSetting, 0.5f);
|
||||||
TEST_MACRO(m_EnumSetting, TestConfigManager::TestEnum::Test2);
|
TEST_MACRO(m_EnumSetting, TestEnum::Test2);
|
||||||
|
|
||||||
#undef TEST_MACRO
|
#undef TEST_MACRO
|
||||||
|
|
||||||
|
@ -468,7 +467,7 @@ namespace YYCCTestbench {
|
||||||
Assert(test.m_StringSetting.Get() == YYCC_U8(""), YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
|
Assert(test.m_StringSetting.Get() == YYCC_U8(""), YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
|
||||||
Assert(test.m_BoolSetting.Get() == false, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
|
Assert(test.m_BoolSetting.Get() == false, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
|
||||||
Assert(test.m_ClampedFloatSetting.Get() == 0.0f, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
|
Assert(test.m_ClampedFloatSetting.Get() == 0.0f, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
|
||||||
Assert(test.m_EnumSetting.Get() == TestConfigManager::TestEnum::Test1, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
|
Assert(test.m_EnumSetting.Get() == TestEnum::Test1, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
|
||||||
|
|
||||||
// test load
|
// test load
|
||||||
Assert(test.m_CoreManager.Load(), YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
|
Assert(test.m_CoreManager.Load(), YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
|
||||||
|
@ -478,13 +477,117 @@ namespace YYCCTestbench {
|
||||||
Assert(test.m_StringSetting.Get() == YYCC_U8("fuck"), YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
|
Assert(test.m_StringSetting.Get() == YYCC_U8("fuck"), YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
|
||||||
Assert(test.m_BoolSetting.Get() == true, YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
|
Assert(test.m_BoolSetting.Get() == true, YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
|
||||||
Assert(test.m_ClampedFloatSetting.Get() == 0.5f, YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
|
Assert(test.m_ClampedFloatSetting.Get() == 0.5f, YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
|
||||||
Assert(test.m_EnumSetting.Get() == TestConfigManager::TestEnum::Test2, YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
|
Assert(test.m_EnumSetting.Get() == TestEnum::Test2, YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestArgParser {
|
||||||
|
public:
|
||||||
|
TestArgParser() :
|
||||||
|
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_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,
|
||||||
|
&m_BoolArgument, &m_ClampedFloatArgument
|
||||||
|
}) {}
|
||||||
|
~TestArgParser() {}
|
||||||
|
|
||||||
|
YYCC::ArgParser::NumberArgument<int32_t> m_IntArgument;
|
||||||
|
YYCC::ArgParser::NumberArgument<float> m_FloatArgument;
|
||||||
|
YYCC::ArgParser::StringArgument m_StringArgument;
|
||||||
|
|
||||||
|
YYCC::ArgParser::SwitchArgument m_BoolArgument;
|
||||||
|
YYCC::ArgParser::NumberArgument<float> m_ClampedFloatArgument;
|
||||||
|
|
||||||
|
YYCC::ArgParser::OptionContext m_OptionContext;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void ArgParserTestbench(int argc, char* argv[]) {
|
||||||
|
// test command line getter
|
||||||
|
{
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||||
|
{
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// test option context
|
||||||
|
// init option context
|
||||||
|
TestArgParser test;
|
||||||
|
|
||||||
|
#define PREPARE_DATA(...) char* test_argv[] = { __VA_ARGS__ }; \
|
||||||
|
auto al = YYCC::ArgParser::ArgumentList::CreateFromStd(sizeof(test_argv) / sizeof(char*), test_argv);
|
||||||
|
|
||||||
|
// normal test
|
||||||
|
{
|
||||||
|
PREPARE_DATA("exec", "-i", "114514");
|
||||||
|
Assert(test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
|
||||||
|
Assert(test.m_IntArgument.IsCaptured() && test.m_IntArgument.Get() == UINT32_C(114514), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
|
||||||
|
Assert(!test.m_BoolArgument.Get(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
|
||||||
|
test.m_OptionContext.Reset();
|
||||||
|
}
|
||||||
|
// no argument
|
||||||
|
{
|
||||||
|
PREPARE_DATA("exec");
|
||||||
|
Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
|
||||||
|
test.m_OptionContext.Reset();
|
||||||
|
}
|
||||||
|
// error argument
|
||||||
|
{
|
||||||
|
PREPARE_DATA("exec", "-?", "114514");
|
||||||
|
Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
|
||||||
|
test.m_OptionContext.Reset();
|
||||||
|
}
|
||||||
|
// lost argument
|
||||||
|
{
|
||||||
|
PREPARE_DATA("exec", "-i");
|
||||||
|
Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
|
||||||
|
test.m_OptionContext.Reset();
|
||||||
|
}
|
||||||
|
// extra useless argument
|
||||||
|
{
|
||||||
|
PREPARE_DATA("exec", "-i", "114514" "1919810");
|
||||||
|
Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
|
||||||
|
test.m_OptionContext.Reset();
|
||||||
|
}
|
||||||
|
// invalid clamp argument
|
||||||
|
{
|
||||||
|
PREPARE_DATA("exec", "-i", "114514", "--clamped-float", "114.0");
|
||||||
|
Assert(!test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
|
||||||
|
test.m_OptionContext.Reset();
|
||||||
|
}
|
||||||
|
// full argument
|
||||||
|
{
|
||||||
|
PREPARE_DATA("exec", "-i", "114514", "-f", "2.0", "--string", "fuck", "-b", "--clamped-float", "0.5");
|
||||||
|
Assert(test.m_OptionContext.Parse(al), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
|
||||||
|
Assert(test.m_IntArgument.IsCaptured() && test.m_IntArgument.Get() == UINT32_C(114514), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
|
||||||
|
Assert(test.m_FloatArgument.IsCaptured() && test.m_FloatArgument.Get() == 2.0f, YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
|
||||||
|
Assert(test.m_StringArgument.IsCaptured() && test.m_StringArgument.Get() == YYCC_U8("fuck"), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
|
||||||
|
Assert(test.m_BoolArgument.IsCaptured() && test.m_BoolArgument.Get(), YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
|
||||||
|
Assert(test.m_ClampedFloatArgument.IsCaptured() && test.m_ClampedFloatArgument.Get() == 0.5f, YYCC_U8("YYCC::ArgParser::OptionContext::Parse"));
|
||||||
|
test.m_OptionContext.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef PREPARE_DATA
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** args) {
|
int main(int argc, char* argv[]) {
|
||||||
// common testbench
|
// common testbench
|
||||||
// normal
|
// normal
|
||||||
YYCCTestbench::EncodingTestbench();
|
YYCCTestbench::EncodingTestbench();
|
||||||
|
@ -494,6 +597,7 @@ int main(int argc, char** args) {
|
||||||
YYCCTestbench::FsPathPatch();
|
YYCCTestbench::FsPathPatch();
|
||||||
// advanced
|
// advanced
|
||||||
YYCCTestbench::ConfigManagerTestbench();
|
YYCCTestbench::ConfigManagerTestbench();
|
||||||
|
YYCCTestbench::ArgParserTestbench(argc, argv);
|
||||||
|
|
||||||
// testbench which may terminal app or ordering input
|
// testbench which may terminal app or ordering input
|
||||||
YYCCTestbench::ConsoleTestbench();
|
YYCCTestbench::ConsoleTestbench();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user