#include "argparse.hpp" #include #include #include #include #include ArgParser::ArgParser(std::string program_name) : program_name_(std::move(program_name)) {} void ArgParser::addString(const std::string &name, const std::string &default_value, const std::string &help, bool required, const std::string &short_name) { order_.push_back(name); meta_[name] = {OptionType::kString, help, required, short_name}; string_values_[name] = default_value; provided_[name] = false; if (!short_name.empty()) { short_to_long_[short_name] = name; } } void ArgParser::addInt(const std::string &name, int default_value, const std::string &help, bool required, const std::string &short_name) { order_.push_back(name); meta_[name] = {OptionType::kInt, help, required, short_name}; int_values_[name] = default_value; provided_[name] = false; if (!short_name.empty()) { short_to_long_[short_name] = name; } } void ArgParser::addFlag(const std::string &name, const std::string &help, const std::string &short_name) { order_.push_back(name); meta_[name] = {OptionType::kFlag, help, false, short_name}; flag_values_[name] = false; provided_[name] = false; if (!short_name.empty()) { short_to_long_[short_name] = name; } } bool ArgParser::parse(int argc, char **argv, std::string *error) { for (int i = 1; i < argc; ++i) { std::string token(argv[i]); if (token == "--help" || token == "-h") { if (error) { *error = "help"; } return false; } if (token.rfind("--", 0) != 0 && token.rfind("-", 0) == 0) { std::string short_key = token.substr(1); std::string short_value; size_t short_eq = short_key.find('='); if (short_eq != std::string::npos) { short_value = short_key.substr(short_eq + 1); short_key = short_key.substr(0, short_eq); } auto sk = short_to_long_.find(short_key); if (sk == short_to_long_.end()) { if (error) { *error = "Unknown option: -" + short_key; } return false; } auto m = meta_.find(sk->second); if (m == meta_.end()) { if (error) { *error = "Unknown option: -" + short_key; } return false; } if (m->second.type == OptionType::kFlag) { if (short_eq != std::string::npos) { if (error) { *error = "Flag does not take a value: -" + short_key; } return false; } flag_values_[sk->second] = true; provided_[sk->second] = true; } else if (m->second.type == OptionType::kString) { if (short_eq == std::string::npos) { if (i + 1 >= argc) { if (error) { *error = "Missing value for -" + short_key; } return false; } short_value = argv[++i]; } string_values_[sk->second] = short_value; provided_[sk->second] = true; } else if (m->second.type == OptionType::kInt) { long parsed; if (short_eq == std::string::npos) { if (i + 1 >= argc) { if (error) { *error = "Missing value for -" + short_key; } return false; } short_value = argv[++i]; } errno = 0; char *endp = nullptr; parsed = std::strtol(short_value.c_str(), &endp, 0); if (errno != 0 || endp == short_value.c_str() || *endp != '\0' || parsed < INT_MIN || parsed > INT_MAX) { if (error) { *error = "Invalid integer for -" + short_key + ": " + short_value; } return false; } int_values_[sk->second] = static_cast(parsed); provided_[sk->second] = true; } continue; } if (token.rfind("--", 0) != 0) { if (error) { *error = "Unexpected positional argument: " + token; } return false; } std::string key; std::string value; size_t eq = token.find('='); if (eq == std::string::npos) { key = token.substr(2); } else { key = token.substr(2, eq - 2); value = token.substr(eq + 1); } auto m = meta_.find(key); if (m == meta_.end()) { if (error) { *error = "Unknown option: --" + key; } return false; } if (m->second.type == OptionType::kFlag) { if (eq != std::string::npos) { if (error) { *error = "Flag does not take a value: --" + key; } return false; } flag_values_[key] = true; provided_[key] = true; continue; } if (eq == std::string::npos) { if (i + 1 >= argc) { if (error) { *error = "Missing value for --" + key; } return false; } value = argv[++i]; } if (m->second.type == OptionType::kString) { string_values_[key] = value; provided_[key] = true; } else if (m->second.type == OptionType::kInt) { errno = 0; char *endp = nullptr; long parsed = std::strtol(value.c_str(), &endp, 0); if (errno != 0 || endp == value.c_str() || *endp != '\0' || parsed < INT_MIN || parsed > INT_MAX) { if (error) { *error = "Invalid integer for --" + key + ": " + value; } return false; } int_values_[key] = static_cast(parsed); provided_[key] = true; } } for (const auto &key : order_) { auto m = meta_.find(key); if (m != meta_.end() && m->second.required && !has(key)) { if (error) { *error = "Missing required option: --" + key; } return false; } } return true; } bool ArgParser::has(const std::string &name) const { auto p = provided_.find(name); return p != provided_.end() && p->second; } std::string ArgParser::getString(const std::string &name) const { auto it = string_values_.find(name); if (it == string_values_.end()) { return std::string(); } return it->second; } int ArgParser::getInt(const std::string &name) const { auto it = int_values_.find(name); if (it == int_values_.end()) { return 0; } return it->second; } bool ArgParser::getFlag(const std::string &name) const { auto it = flag_values_.find(name); if (it == flag_values_.end()) { return false; } return it->second; } std::string ArgParser::helpText() const { std::ostringstream oss; oss << "Usage: " << program_name_ << " [options]\n\n"; oss << "Options:\n"; oss << " -h, --help Show this help\n"; for (const auto &key : order_) { auto m = meta_.find(key); if (m == meta_.end()) { continue; } oss << " "; if (!m->second.short_name.empty()) { oss << "-" << m->second.short_name << ", "; } else { oss << " "; } oss << "--" << key; if (m->second.type != OptionType::kFlag) { oss << " "; } if (m->second.required) { oss << " (required)"; } oss << "\n"; oss << " " << m->second.help; if (m->second.type == OptionType::kString) { auto s = string_values_.find(key); if (s != string_values_.end()) { oss << " [default: '" << s->second << "']"; } } else { if (m->second.type == OptionType::kInt) { auto iv = int_values_.find(key); if (iv != int_values_.end()) { oss << " [default: " << iv->second << "]"; } } } oss << "\n"; } return oss.str(); }