mirror of
https://github.com/hyprwm/hyprutils.git
synced 2025-12-20 21:00:05 +01:00
cli: add CArgumentParser
This commit is contained in:
parent
31f29957df
commit
21c62325c4
7 changed files with 492 additions and 0 deletions
36
include/hyprutils/cli/ArgumentParser.hpp
Normal file
36
include/hyprutils/cli/ArgumentParser.hpp
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <span>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <optional>
|
||||||
|
#include <expected>
|
||||||
|
#include "../memory/UniquePtr.hpp"
|
||||||
|
|
||||||
|
namespace Hyprutils::CLI {
|
||||||
|
class CArgumentParserImpl;
|
||||||
|
|
||||||
|
class CArgumentParser {
|
||||||
|
public:
|
||||||
|
CArgumentParser(const std::span<const char*>& args);
|
||||||
|
~CArgumentParser() = default;
|
||||||
|
|
||||||
|
std::expected<void, std::string> registerBoolOption(std::string&& name, std::string&& abbrev, std::string&& description);
|
||||||
|
std::expected<void, std::string> registerIntOption(std::string&& name, std::string&& abbrev, std::string&& description);
|
||||||
|
std::expected<void, std::string> registerFloatOption(std::string&& name, std::string&& abbrev, std::string&& description);
|
||||||
|
std::expected<void, std::string> registerStringOption(std::string&& name, std::string&& abbrev, std::string&& description);
|
||||||
|
|
||||||
|
std::optional<bool> getBool(const char* name);
|
||||||
|
std::optional<int> getInt(const char* name);
|
||||||
|
std::optional<float> getFloat(const char* name);
|
||||||
|
std::optional<std::string_view> getString(const char* name);
|
||||||
|
|
||||||
|
// commence the parsing after registering
|
||||||
|
std::expected<void, std::string> parse();
|
||||||
|
|
||||||
|
std::string getDescription(const std::string_view& header, std::optional<size_t> maxWidth = {});
|
||||||
|
|
||||||
|
private:
|
||||||
|
Memory::CUniquePointer<CArgumentParserImpl> m_impl;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -9,5 +9,6 @@ namespace Hyprutils {
|
||||||
std::string_view trim(const std::string_view& in);
|
std::string_view trim(const std::string_view& in);
|
||||||
bool isNumber(const std::string& str, bool allowfloat = false);
|
bool isNumber(const std::string& str, bool allowfloat = false);
|
||||||
void replaceInString(std::string& string, const std::string& what, const std::string& to);
|
void replaceInString(std::string& string, const std::string& what, const std::string& to);
|
||||||
|
bool truthy(const std::string_view& in);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
287
src/cli/ArgumentParser.cpp
Normal file
287
src/cli/ArgumentParser.cpp
Normal file
|
|
@ -0,0 +1,287 @@
|
||||||
|
#include "ArgumentParser.hpp"
|
||||||
|
|
||||||
|
#include <format>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <hyprutils/string/String.hpp>
|
||||||
|
#include <hyprutils/memory/Casts.hpp>
|
||||||
|
|
||||||
|
using namespace Hyprutils::CLI;
|
||||||
|
using namespace Hyprutils::Memory;
|
||||||
|
using namespace Hyprutils::String;
|
||||||
|
using namespace Hyprutils;
|
||||||
|
|
||||||
|
CArgumentParser::CArgumentParser(const std::span<const char*>& args) : m_impl(makeUnique<CArgumentParserImpl>(args)) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::expected<void, std::string> CArgumentParser::registerBoolOption(std::string&& name, std::string&& abbrev, std::string&& description) {
|
||||||
|
return m_impl->registerOption(std::move(name), std::move(abbrev), std::move(description), ARG_TYPE_BOOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::expected<void, std::string> CArgumentParser::registerIntOption(std::string&& name, std::string&& abbrev, std::string&& description) {
|
||||||
|
return m_impl->registerOption(std::move(name), std::move(abbrev), std::move(description), ARG_TYPE_INT);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::expected<void, std::string> CArgumentParser::registerFloatOption(std::string&& name, std::string&& abbrev, std::string&& description) {
|
||||||
|
return m_impl->registerOption(std::move(name), std::move(abbrev), std::move(description), ARG_TYPE_FLOAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::expected<void, std::string> CArgumentParser::registerStringOption(std::string&& name, std::string&& abbrev, std::string&& description) {
|
||||||
|
return m_impl->registerOption(std::move(name), std::move(abbrev), std::move(description), ARG_TYPE_STR);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<bool> CArgumentParser::getBool(const char* name) {
|
||||||
|
auto ref = m_impl->getValue(name);
|
||||||
|
|
||||||
|
if (ref == m_impl->m_values.end())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
if (const auto pval = std::get_if<bool>(&ref->val); pval)
|
||||||
|
return *pval;
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<int> CArgumentParser::getInt(const char* name) {
|
||||||
|
auto ref = m_impl->getValue(name);
|
||||||
|
|
||||||
|
if (ref == m_impl->m_values.end())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
if (const auto pval = std::get_if<int>(&ref->val); pval)
|
||||||
|
return *pval;
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<float> CArgumentParser::getFloat(const char* name) {
|
||||||
|
auto ref = m_impl->getValue(name);
|
||||||
|
|
||||||
|
if (ref == m_impl->m_values.end())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
if (const auto pval = std::get_if<float>(&ref->val); pval)
|
||||||
|
return *pval;
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string_view> CArgumentParser::getString(const char* name) {
|
||||||
|
auto ref = m_impl->getValue(name);
|
||||||
|
|
||||||
|
if (ref == m_impl->m_values.end())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
if (const auto pval = std::get_if<std::string>(&ref->val); pval)
|
||||||
|
return *pval;
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CArgumentParser::getDescription(const std::string_view& header, std::optional<size_t> maxWidth) {
|
||||||
|
return m_impl->getDescription(header, maxWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::expected<void, std::string> CArgumentParser::parse() {
|
||||||
|
return m_impl->parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
CArgumentParserImpl::CArgumentParserImpl(const std::span<const char*>& args) {
|
||||||
|
m_argv.reserve(args.size());
|
||||||
|
for (const auto& a : args) {
|
||||||
|
m_argv.emplace_back(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<SArgumentKey>::iterator CArgumentParserImpl::getValue(const std::string_view& sv) {
|
||||||
|
auto it = std::ranges::find_if(m_values, [&sv](const auto& e) { return e.full == sv || e.abbrev == sv; });
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::expected<void, std::string> CArgumentParserImpl::registerOption(std::string&& name, std::string&& abbrev, std::string&& description, eArgumentType type) {
|
||||||
|
if (getValue(name) != m_values.end() || getValue(abbrev) != m_values.end())
|
||||||
|
return std::unexpected("Value already exists");
|
||||||
|
|
||||||
|
m_values.emplace_back(SArgumentKey{
|
||||||
|
.full = std::move(name),
|
||||||
|
.abbrev = std::move(abbrev),
|
||||||
|
.desc = std::move(description),
|
||||||
|
.argType = type,
|
||||||
|
.val = std::monostate{},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::expected<void, std::string> CArgumentParserImpl::parse() {
|
||||||
|
// walk the args
|
||||||
|
for (size_t i = 1; i < m_argv.size(); ++i) {
|
||||||
|
auto val = m_values.end();
|
||||||
|
const auto& arg = m_argv.at(i);
|
||||||
|
|
||||||
|
if (arg.starts_with("--"))
|
||||||
|
val = getValue(std::string_view{arg}.substr(2));
|
||||||
|
else if (arg.starts_with('-'))
|
||||||
|
val = getValue(std::string_view{arg}.substr(1));
|
||||||
|
else
|
||||||
|
return std::unexpected(std::format("Invalid element found while parsing: {}", arg));
|
||||||
|
|
||||||
|
if (val == m_values.end())
|
||||||
|
return std::unexpected(std::format("Invalid argument found while parsing: {}", arg));
|
||||||
|
|
||||||
|
switch (val->argType) {
|
||||||
|
case ARG_TYPE_BOOL: {
|
||||||
|
val->val = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ARG_TYPE_INT: {
|
||||||
|
if (i + 1 >= m_argv.size())
|
||||||
|
return std::unexpected(std::format("Failed parsing argument {}, no value supplied", arg));
|
||||||
|
const auto& next = std::string{m_argv.at(++i)};
|
||||||
|
if (!isNumber(next))
|
||||||
|
return std::unexpected(std::format("Failed parsing argument {}, value {} is not an int", arg, next));
|
||||||
|
try {
|
||||||
|
val->val = sc<int>(std::stoi(next));
|
||||||
|
} catch (...) { return std::unexpected(std::format("Failed parsing argument {}, value {} is not an int", arg, next)); }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ARG_TYPE_FLOAT: {
|
||||||
|
if (i + 1 >= m_argv.size())
|
||||||
|
return std::unexpected(std::format("Failed parsing argument {}, no value supplied", arg));
|
||||||
|
const auto& next = std::string{m_argv.at(++i)};
|
||||||
|
if (!isNumber(next, true))
|
||||||
|
return std::unexpected(std::format("Failed parsing argument {}, value {} is not a float", arg, next));
|
||||||
|
try {
|
||||||
|
val->val = sc<float>(std::stof(next));
|
||||||
|
} catch (...) { return std::unexpected(std::format("Failed parsing argument {}, value {} is not a float", arg, next)); }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ARG_TYPE_STR: {
|
||||||
|
if (i + 1 >= m_argv.size())
|
||||||
|
return std::unexpected(std::format("Failed parsing argument {}, no value supplied", arg));
|
||||||
|
val->val = std::string{m_argv.at(++i)};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ARG_TYPE_END: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CArgumentParserImpl::getDescription(const std::string_view& header, std::optional<size_t> maxWidth) {
|
||||||
|
|
||||||
|
const size_t MAX_COLS = maxWidth.value_or(80);
|
||||||
|
const std::string PAD_STR = " ";
|
||||||
|
|
||||||
|
constexpr const std::array<const char*, ARG_TYPE_END> TYPE_STRS = {
|
||||||
|
"", // bool
|
||||||
|
"[int]", // int
|
||||||
|
"[float]", // float
|
||||||
|
"[str]", // str
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
auto wrap = [](const std::string_view& str, size_t maxW) -> std::vector<std::string_view> {
|
||||||
|
std::vector<std::string_view> result;
|
||||||
|
|
||||||
|
// walk word by word
|
||||||
|
size_t nextSpacePos = 0, lastBreakPos = 0;
|
||||||
|
while (true) {
|
||||||
|
size_t lastSpacePos = nextSpacePos;
|
||||||
|
nextSpacePos = str.find(' ', nextSpacePos + 1);
|
||||||
|
|
||||||
|
if (nextSpacePos == std::string::npos)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (nextSpacePos - lastBreakPos > maxW) {
|
||||||
|
if (lastSpacePos - lastBreakPos <= maxW) {
|
||||||
|
// break
|
||||||
|
result.emplace_back(str.substr(lastBreakPos, lastSpacePos - lastBreakPos));
|
||||||
|
lastBreakPos = lastSpacePos + 1;
|
||||||
|
} else {
|
||||||
|
while (lastSpacePos - lastBreakPos > maxW) {
|
||||||
|
// break
|
||||||
|
result.emplace_back(str.substr(lastBreakPos, maxW));
|
||||||
|
lastBreakPos += maxW;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.emplace_back(str.substr(lastBreakPos));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto pad = [&PAD_STR](size_t len) -> std::string_view {
|
||||||
|
if (len >= PAD_STR.size())
|
||||||
|
return PAD_STR;
|
||||||
|
return std::string_view{PAD_STR}.substr(0, len);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string rolling;
|
||||||
|
rolling += std::format("┏ {}\n", header);
|
||||||
|
rolling += "┣";
|
||||||
|
for (size_t i = 0; i < MAX_COLS; ++i) {
|
||||||
|
rolling += "━";
|
||||||
|
}
|
||||||
|
rolling += "┓\n";
|
||||||
|
|
||||||
|
// get max widths
|
||||||
|
size_t maxArgWidth = 0, maxShortWidth = 0;
|
||||||
|
for (const auto& v : m_values) {
|
||||||
|
maxShortWidth = std::max(maxShortWidth, v.abbrev.size() + 4 + std::string_view{TYPE_STRS[v.argType]}.length());
|
||||||
|
maxArgWidth = std::max(maxArgWidth, v.full.size() + 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the table
|
||||||
|
for (const auto& v : m_values) {
|
||||||
|
size_t lenUsed = 0;
|
||||||
|
rolling += "┣ --" + v.full;
|
||||||
|
lenUsed += 3 + v.full.size();
|
||||||
|
rolling += pad(maxArgWidth - lenUsed);
|
||||||
|
lenUsed = maxArgWidth;
|
||||||
|
|
||||||
|
rolling += " -" + v.abbrev;
|
||||||
|
lenUsed += 2 + v.abbrev.size();
|
||||||
|
rolling += " ";
|
||||||
|
rolling += TYPE_STRS[v.argType];
|
||||||
|
lenUsed += std::string_view{TYPE_STRS[v.argType]}.length() + 1;
|
||||||
|
rolling += pad(maxArgWidth + maxShortWidth - lenUsed);
|
||||||
|
lenUsed = maxArgWidth + maxShortWidth;
|
||||||
|
|
||||||
|
rolling += " | ";
|
||||||
|
lenUsed += 3;
|
||||||
|
|
||||||
|
const auto ROWS = wrap(v.desc, MAX_COLS - lenUsed);
|
||||||
|
|
||||||
|
const auto LEN_START_DESC = lenUsed;
|
||||||
|
|
||||||
|
rolling += ROWS[0];
|
||||||
|
lenUsed += ROWS[0].size();
|
||||||
|
rolling += pad(MAX_COLS - lenUsed);
|
||||||
|
rolling += "┃\n";
|
||||||
|
|
||||||
|
for (size_t i = 1; i < ROWS.size(); ++i) {
|
||||||
|
lenUsed = LEN_START_DESC;
|
||||||
|
rolling += "┣";
|
||||||
|
rolling += pad(LEN_START_DESC);
|
||||||
|
rolling += ROWS[i];
|
||||||
|
lenUsed += ROWS[i].size();
|
||||||
|
rolling += pad(MAX_COLS - lenUsed);
|
||||||
|
rolling += "┃\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rolling += "┗";
|
||||||
|
for (size_t i = 0; i < MAX_COLS; ++i) {
|
||||||
|
rolling += "━";
|
||||||
|
}
|
||||||
|
rolling += "┛\n";
|
||||||
|
|
||||||
|
return rolling;
|
||||||
|
}
|
||||||
39
src/cli/ArgumentParser.hpp
Normal file
39
src/cli/ArgumentParser.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
#include <hyprutils/cli/ArgumentParser.hpp>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <variant>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Hyprutils::CLI {
|
||||||
|
enum eArgumentType : uint8_t {
|
||||||
|
ARG_TYPE_BOOL = 0,
|
||||||
|
ARG_TYPE_INT,
|
||||||
|
ARG_TYPE_FLOAT,
|
||||||
|
ARG_TYPE_STR,
|
||||||
|
ARG_TYPE_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SArgumentKey {
|
||||||
|
using Value = std::variant<std::monostate, bool, int, float, std::string>;
|
||||||
|
|
||||||
|
std::string full, abbrev, desc;
|
||||||
|
eArgumentType argType = ARG_TYPE_BOOL;
|
||||||
|
|
||||||
|
Value val;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CArgumentParserImpl {
|
||||||
|
public:
|
||||||
|
CArgumentParserImpl(const std::span<const char*>& args);
|
||||||
|
~CArgumentParserImpl() = default;
|
||||||
|
|
||||||
|
std::string getDescription(const std::string_view& header, std::optional<size_t> maxWidth = {});
|
||||||
|
std::expected<void, std::string> parse();
|
||||||
|
std::vector<SArgumentKey>::iterator getValue(const std::string_view& sv);
|
||||||
|
std::expected<void, std::string> registerOption(std::string&& name, std::string&& abbrev, std::string&& description, eArgumentType type);
|
||||||
|
|
||||||
|
std::vector<SArgumentKey> m_values;
|
||||||
|
|
||||||
|
std::vector<std::string_view> m_argv;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -88,3 +88,16 @@ void Hyprutils::String::replaceInString(std::string& string, const std::string&
|
||||||
pos += to.length();
|
pos += to.length();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Hyprutils::String::truthy(const std::string_view& in) {
|
||||||
|
if (in == "1")
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (in == "0")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string lower = std::string{in};
|
||||||
|
std::ranges::transform(lower, lower.begin(), ::tolower);
|
||||||
|
|
||||||
|
return lower.starts_with("true") || lower.starts_with("yes") || lower.starts_with("on");
|
||||||
|
}
|
||||||
|
|
|
||||||
103
tests/cli/ArgumentParser.cpp
Normal file
103
tests/cli/ArgumentParser.cpp
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
#include <cli/ArgumentParser.hpp>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <print>
|
||||||
|
|
||||||
|
using namespace Hyprutils::CLI;
|
||||||
|
using namespace Hyprutils;
|
||||||
|
|
||||||
|
constexpr const char* DESC_TEST = R"#(┏ My description
|
||||||
|
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
┣ --hello -h | Says hello ┃
|
||||||
|
┣ --hello2 -e | Says hello 2 ┃
|
||||||
|
┣ --value -v [float] | Sets a valueeeeeee ┃
|
||||||
|
┣ --longlonglonglongintopt -l [int] | Long long ┃
|
||||||
|
┣ maaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa┃
|
||||||
|
┣ aaaaaaaaaaan maaan man maaan man maaan ┃
|
||||||
|
┣ man maaan man ┃
|
||||||
|
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
||||||
|
)#";
|
||||||
|
|
||||||
|
TEST(CLI, ArgumentParser) {
|
||||||
|
std::vector<const char*> argv = {"app", "--hello", "--value", "0.2"};
|
||||||
|
|
||||||
|
CArgumentParser parser(argv);
|
||||||
|
|
||||||
|
EXPECT_TRUE(parser.registerBoolOption("hello", "h", "Says hello"));
|
||||||
|
EXPECT_TRUE(parser.registerBoolOption("hello2", "e", "Says hello 2"));
|
||||||
|
EXPECT_TRUE(parser.registerFloatOption("value", "v", "Sets a valueeeeeee"));
|
||||||
|
EXPECT_TRUE(parser.registerIntOption("longlonglonglongintopt", "l", "Long long maaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaan maaan man maaan man maaan man maaan man"));
|
||||||
|
|
||||||
|
auto result = parser.parse();
|
||||||
|
|
||||||
|
EXPECT_TRUE(result.has_value());
|
||||||
|
|
||||||
|
std::println("{}", parser.getDescription("My description"));
|
||||||
|
|
||||||
|
if (!result.has_value())
|
||||||
|
std::println("Error: {}", result.error());
|
||||||
|
|
||||||
|
EXPECT_EQ(parser.getBool("hello").value_or(false), true);
|
||||||
|
EXPECT_EQ(parser.getBool("hello2").value_or(false), false);
|
||||||
|
EXPECT_EQ(parser.getFloat("value").value_or(0.F), 0.2F);
|
||||||
|
|
||||||
|
EXPECT_EQ(parser.getDescription("My description"), DESC_TEST);
|
||||||
|
|
||||||
|
CArgumentParser parser2(argv);
|
||||||
|
|
||||||
|
EXPECT_TRUE(parser2.registerBoolOption("hello2", "e", "Says hello 2"));
|
||||||
|
EXPECT_TRUE(parser2.registerFloatOption("value", "v", "Sets a valueeeeeee"));
|
||||||
|
EXPECT_TRUE(parser2.registerIntOption("longlonglonglongintopt", "l", "Long long maaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaan maaan man maaan man maaan man maaan man"));
|
||||||
|
|
||||||
|
auto result2 = parser2.parse();
|
||||||
|
|
||||||
|
EXPECT_TRUE(!result2.has_value());
|
||||||
|
|
||||||
|
std::vector<const char*> argv3 = {"app", "--hello", "--value"};
|
||||||
|
|
||||||
|
CArgumentParser parser3(argv3);
|
||||||
|
|
||||||
|
EXPECT_TRUE(parser3.registerBoolOption("hello2", "e", "Says hello 2"));
|
||||||
|
EXPECT_TRUE(parser3.registerFloatOption("value", "v", "Sets a valueeeeeee"));
|
||||||
|
EXPECT_TRUE(parser3.registerIntOption("longlonglonglongintopt", "l", "Long long maaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaan maaan man maaan man maaan man maaan man"));
|
||||||
|
|
||||||
|
auto result3 = parser3.parse();
|
||||||
|
|
||||||
|
EXPECT_TRUE(!result3.has_value());
|
||||||
|
|
||||||
|
std::vector<const char*> argv4 = {"app", "--value", "hi", "-w", "2"};
|
||||||
|
|
||||||
|
CArgumentParser parser4(argv4);
|
||||||
|
|
||||||
|
EXPECT_TRUE(parser4.registerStringOption("value", "v", "Sets a valueeeeeee"));
|
||||||
|
EXPECT_TRUE(parser4.registerIntOption("value2", "w", "Sets a valueeeeeee 2"));
|
||||||
|
|
||||||
|
auto result4 = parser4.parse();
|
||||||
|
|
||||||
|
EXPECT_TRUE(result4.has_value());
|
||||||
|
|
||||||
|
EXPECT_EQ(parser4.getString("value").value_or(""), "hi");
|
||||||
|
EXPECT_EQ(parser4.getInt("value2").value_or(0), 2);
|
||||||
|
|
||||||
|
std::vector<const char*> argv5 = {
|
||||||
|
"app",
|
||||||
|
"e",
|
||||||
|
};
|
||||||
|
|
||||||
|
CArgumentParser parser5(argv5);
|
||||||
|
|
||||||
|
EXPECT_TRUE(parser5.registerStringOption("value", "v", "Sets a valueeeeeee"));
|
||||||
|
EXPECT_TRUE(parser5.registerStringOption("value2", "w", "Sets a valueeeeeee 2"));
|
||||||
|
|
||||||
|
auto result5 = parser5.parse();
|
||||||
|
|
||||||
|
EXPECT_TRUE(!result5.has_value());
|
||||||
|
|
||||||
|
CArgumentParser parser6(argv5);
|
||||||
|
|
||||||
|
EXPECT_TRUE(parser6.registerStringOption("aa", "v", "Sets a valueeeeeee"));
|
||||||
|
EXPECT_TRUE(!parser6.registerStringOption("aa", "w", "Sets a valueeeeeee 2"));
|
||||||
|
EXPECT_TRUE(parser6.registerStringOption("bb", "b", "Sets a valueeeeeee"));
|
||||||
|
EXPECT_TRUE(!parser6.registerStringOption("cc", "b", "Sets a valueeeeeee 2"));
|
||||||
|
}
|
||||||
|
|
@ -31,4 +31,17 @@ TEST(String, string) {
|
||||||
EXPECT_EQ(isNumber("-1.0", true), true);
|
EXPECT_EQ(isNumber("-1.0", true), true);
|
||||||
EXPECT_EQ(isNumber("-1..0", true), false);
|
EXPECT_EQ(isNumber("-1..0", true), false);
|
||||||
EXPECT_EQ(isNumber("-10.0000000001", true), true);
|
EXPECT_EQ(isNumber("-10.0000000001", true), true);
|
||||||
|
|
||||||
|
EXPECT_EQ(truthy("frgeujgeruibger"), false);
|
||||||
|
EXPECT_EQ(truthy("false"), false);
|
||||||
|
EXPECT_EQ(truthy("0"), false);
|
||||||
|
EXPECT_EQ(truthy("yees"), false);
|
||||||
|
EXPECT_EQ(truthy("naa"), false);
|
||||||
|
EXPECT_EQ(truthy("-1"), false);
|
||||||
|
EXPECT_EQ(truthy("true"), true);
|
||||||
|
EXPECT_EQ(truthy("true eeee ee"), true);
|
||||||
|
EXPECT_EQ(truthy("yesss"), true);
|
||||||
|
EXPECT_EQ(truthy("1"), true);
|
||||||
|
EXPECT_EQ(truthy("on"), true);
|
||||||
|
EXPECT_EQ(truthy("onn"), true);
|
||||||
}
|
}
|
||||||
Loading…
Add table
Reference in a new issue