#pragma once #include #include #include #include #include #include #include #include #include #include #include namespace CliArguments { enum Type { String, Int, Bool }; struct Argument { std::vector 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 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 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 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 directArguments_; std::map arguments_; std::map argumentsValue_; std::map aliases_; }; }