225 lines
5.8 KiB
C++
225 lines
5.8 KiB
C++
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <cctype>
|
|
#include <exception>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <map>
|
|
#include <optional>
|
|
#include <ostream>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace CliArguments {
|
|
|
|
enum Type {
|
|
String,
|
|
Int,
|
|
Bool
|
|
};
|
|
|
|
struct Argument {
|
|
std::vector<std::string> aliases;
|
|
std::string description;
|
|
Type type = String;
|
|
short order = 0;
|
|
bool required = false;
|
|
bool direct = false;
|
|
};
|
|
|
|
class CliArguments {
|
|
public:
|
|
|
|
CliArguments(int argc, char** argv, std::map<std::string, Argument> arguments)
|
|
: argc_(argc), argv_(argv), arguments_(arguments) {
|
|
registerArgs(argc, argv, arguments);
|
|
}
|
|
|
|
std::string getString(const std::string& argName) const {
|
|
auto value = argumentsValue_.find(argName);
|
|
if (value == argumentsValue_.end()) {
|
|
throw std::logic_error("trying to access non existant argument '" + argName + "'");
|
|
}
|
|
return value->second;
|
|
}
|
|
|
|
int getInt(const std::string& argName) const {
|
|
auto valueStr = getString(argName);
|
|
try {
|
|
return std::stoi(valueStr);
|
|
} catch (std::exception& e) {
|
|
throw std::logic_error("trying to access argument '" + argName + "' (value: '" + valueStr + "')" + " as integer");
|
|
}
|
|
}
|
|
|
|
int isInt(const std::string& argName) const {
|
|
auto valueStr = getString(argName);
|
|
for (const auto& ch : valueStr) {
|
|
if (!isdigit(ch)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool getBool(const std::string& argName) const {
|
|
auto valueStr = getString(argName);
|
|
if (valueStr != "true" && valueStr != "false") {
|
|
throw std::logic_error("trying to access argument '" + argName + "' (value: '" + valueStr + "')" + " as bool");
|
|
}
|
|
return valueStr == "true";
|
|
}
|
|
|
|
int isBool(const std::string& argName) const {
|
|
auto valueStr = getString(argName);
|
|
return valueStr == "true" || valueStr == "false";
|
|
}
|
|
|
|
bool exists(const std::string& argName) const {
|
|
auto value = argumentsValue_.find(argName);
|
|
return value != argumentsValue_.end();
|
|
}
|
|
|
|
void printDebug() const {
|
|
for (const auto& [key, arg] : arguments_) {
|
|
std::string value = "-";
|
|
if (argumentsValue_.find(key) != argumentsValue_.end()) {
|
|
value = argumentsValue_.find(key)->second;
|
|
}
|
|
std::cout << key << ": " << value << std::endl;
|
|
}
|
|
}
|
|
|
|
void printHelp() const {
|
|
int longestArgLength = 0;
|
|
for (const auto& [key, arg] : arguments_) {
|
|
if (key.length() > longestArgLength) {
|
|
longestArgLength = key.length();
|
|
}
|
|
}
|
|
std::cout << "usage: sway-wallpaper [options]";
|
|
for (const auto& arg : directArguments_) {
|
|
std::cout << " <" << arg << ">";
|
|
}
|
|
std::cout << std::endl << std::endl;
|
|
std::cout << "required:" << std::endl;
|
|
|
|
for (const auto& [key, arg] : arguments_) {
|
|
if (!arg.direct) {
|
|
continue;
|
|
}
|
|
std::cout << std::left << std::setw(longestArgLength + 10) << (" " + key) << arg.description << std::endl;
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
std::cout << "options:" << std::endl;
|
|
|
|
for (const auto& [key, arg] : arguments_) {
|
|
if (arg.direct) {
|
|
continue;
|
|
}
|
|
std::string argWithAliases = " ";
|
|
for (const auto& alias : arg.aliases) {
|
|
argWithAliases += "-" + alias + ", ";
|
|
}
|
|
argWithAliases += "--" + key;
|
|
|
|
std::cout << std::left << std::setw(longestArgLength + 10) << argWithAliases;
|
|
std::cout << arg.description << std::endl;
|
|
}
|
|
}
|
|
|
|
bool isValid() const {
|
|
return error_ == "";
|
|
}
|
|
|
|
const std::string& error() const {
|
|
return error_;
|
|
}
|
|
|
|
private:
|
|
|
|
void registerArgs(int argc, char** argv, std::map<std::string, Argument> arguments) {
|
|
for (auto& [key, arg] : arguments) {
|
|
if (arg.direct) {
|
|
directArguments_.push_back(key);
|
|
}
|
|
for (auto& alias : arg.aliases) {
|
|
aliases_[alias] = key;
|
|
}
|
|
}
|
|
std::ranges::sort(directArguments_, [&](const std::string& s1, const std::string& s2) {
|
|
return arguments_[s1].order < arguments_[s2].order;
|
|
});
|
|
|
|
int currentDirectFieldIndex = 0;
|
|
|
|
for (int i = 1; i < argc; ++i) {
|
|
std::string arg = argv[i];
|
|
|
|
if (arg.starts_with("-")) {
|
|
auto argName = extractArgName(arg);
|
|
if (!argName.has_value()) {
|
|
setError("'" + arg + "' is not a valid argument");
|
|
return;
|
|
}
|
|
if (arguments_[argName.value()].type == Bool) {
|
|
argumentsValue_[argName.value()] = "true";
|
|
return;
|
|
}
|
|
++i;
|
|
if (i >= argc) {
|
|
setError("Missing value for argument '" + arg + "'");
|
|
return;
|
|
}
|
|
std::string argValue = std::string(argv[i]);
|
|
argumentsValue_[argName.value()] = argValue;
|
|
} else if (currentDirectFieldIndex >= directArguments_.size()) {
|
|
setError("Too many argument, unknown '" + arg + "'");
|
|
return;
|
|
} else {
|
|
std::string& argName = directArguments_[currentDirectFieldIndex];
|
|
argumentsValue_[argName] = arg;
|
|
++currentDirectFieldIndex;
|
|
}
|
|
}
|
|
|
|
if (currentDirectFieldIndex != directArguments_.size()) {
|
|
setError("Missing required arguments");
|
|
}
|
|
}
|
|
|
|
std::optional<std::string> extractArgName(const std::string& arg) const {
|
|
if (arg.length() > 2 && arg.starts_with("--")) {
|
|
std::string argName = arg.substr(2);
|
|
if (arguments_.find(argName) == arguments_.end()) {
|
|
return std::nullopt;
|
|
}
|
|
return argName;
|
|
} else if (arg.length() == 2 && arg.starts_with("-")) {
|
|
std::string argAlias = arg.substr(1);
|
|
const auto& alias = aliases_.find(argAlias);
|
|
if (alias == aliases_.end()) {
|
|
return std::nullopt;
|
|
}
|
|
std::string argName = alias->second;
|
|
return argName;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
void setError(std::string error) {
|
|
error_ = error;
|
|
}
|
|
|
|
std::string error_;
|
|
int argc_;
|
|
char** argv_;
|
|
std::vector<std::string> directArguments_;
|
|
std::map<std::string, Argument> arguments_;
|
|
std::map<std::string, std::string> argumentsValue_;
|
|
std::map<std::string, std::string> aliases_;
|
|
};
|
|
}
|