3static bool looksLikeNumber(
const std::string& s) {
4 if (
s.empty())
return false;
6 std::strtod(
s.c_str(), &end);
7 return end && *end ==
'\0';
10Argument::Argument(
const std::string& longName,
const std::string& shortName,
const std::string& helpText,
11 ArgType type,
bool isRequired,
bool allowsMultiple,
12 std::optional<std::string> defaultValue,
bool isPositional)
13 : longName(longName), shortName(shortName), helpText(helpText), type(type),
14 isRequired(isRequired), allowsMultiple(allowsMultiple),
15 defaultValue(defaultValue), isPositional(isPositional) {}
18 validators.push_back(validator);
26 for (
const auto& validator : validators) {
27 if (!validator->validate(value)) {
28 throw std::invalid_argument(validator->errorMessage());
51 return allowsMultiple;
83 isRequired = required;
93 allowsMultiple = multiple;
98 validators.push_back(validator);
103 isPositional = positional;
108 Argument arg =
Argument(longName, shortName, helpText, type, isRequired, allowsMultiple, defaultValue, isPositional);
109 if (!validators.empty()){
110 for (
auto &validator : validators) {
111 arg.addValidator(validator);
117const Argument* ArgParser::findArgumentByLongName(
const std::string& name)
const {
118 auto it = std::find_if(arguments.begin(), arguments.end(),
119 [&](
const Argument& a){ return a.getLongName() == name; });
120 return (it == arguments.end()) ? nullptr : &(*it);
124 return parsedValues.find(name) != parsedValues.end();
128 if (arg.isPositionalArg()) {
129 positional_arguments.push_back(arg);
131 arguments.push_back(arg);
135 size_t positionalIndex = 0;
136 positionalValues.clear();
137 for (
int i = 1; i < argc; ++i) {
138 std::string arg = argv[i];
139 if (arg.rfind(
"--", 0) == 0 || arg.rfind(
"-", 0) == 0) {
140 std::string key = arg.substr(arg[1] ==
'-' ? 2 : 1);
141 auto it = std::find_if(arguments.begin(), arguments.end(), [&](
const Argument& a) {
142 return a.getLongName() == key || a.getShortName() == key;
145 if (it == arguments.end()) {
146 throw std::invalid_argument(
"Unknown option: " + arg);
151 std::vector<std::string> values;
152 while (i + 1 < argc && ((argv[i + 1][0] !=
'-') || looksLikeNumber(argv[i + 1]))) {
153 std::string value = argv[++i];
155 values.push_back(value);
157 if (!values.size()) {
158 throw std::invalid_argument(
"Please put at least one argument for : " + arg);
166 if (i + 1 < argc && ((argv[i + 1][0] !=
'-') || looksLikeNumber(argv[i + 1]))) {
167 std::string value = argv[++i];
171 throw std::invalid_argument(
"Missing value for option: " + key);
175 if (positionalIndex >= positional_arguments.size()) {
176 throw std::invalid_argument(
"Unexpected positional argument: " + arg);
178 Argument& positionalArg = positional_arguments[positionalIndex++];
180 positionalValues.push_back(arg);
184 for (
const auto& arg : positional_arguments) {
185 if (arg.isRequiredArg() && positionalValues.size() < positional_arguments.size()) {
186 throw std::invalid_argument(
"Missing required positional argument: " + arg.getLongName());
190 for (
const auto& arg : arguments) {
191 if (parsedValues.find(arg.getLongName()) == parsedValues.end() && arg.getDefaultValue()) {
192 parsedValues[arg.getLongName()] = {*arg.getDefaultValue()};
198 auto it = parsedValues.find(name);
199 if (it != parsedValues.end() && !it->second.empty()) {
200 return it->second[0];
202 for (
const auto& arg : arguments) {
203 if (arg.getLongName() == name && arg.getDefaultValue()) {
204 return *arg.getDefaultValue();
207 throw std::invalid_argument(
"Value for argument " + name +
" not provided.");
211 auto it = parsedValues.find(name);
212 if (it != parsedValues.end()) {
215 throw std::invalid_argument(
"Values for argument " + name +
" not provided.");
219 return positionalValues;
223 for (
const auto& arg : arguments) {
224 if (arg.isPositionalArg()) {
225 std::cout << arg.getLongName() <<
": " << arg.getHelpText() <<
" (Positional)\n";
227 std::cout <<
"--" << arg.getLongName() <<
", -" << arg.getShortName()
228 <<
": " << arg.getHelpText();
229 if (arg.getDefaultValue()) {
230 std::cout <<
" (default: " << *arg.getDefaultValue() <<
")";
void parse(int argc, char *argv[])
std::vector< std::string > getValues(const std::string &name) const
std::vector< std::string > getPositionalValues() const
std::string getValue(const std::string &name) const
void addArgument(const Argument &arg)
bool exists(const std::string &name) const
ArgumentBuilder & setAllowsMultiple(bool multiple)
ArgumentBuilder & setType(ArgType argType)
ArgumentBuilder & setLongName(const std::string &name)
ArgumentBuilder & setDefaultValue(const std::string &value)
ArgumentBuilder & setHelpText(const std::string &text)
ArgumentBuilder & setPositional(bool positional)
ArgumentBuilder & setRequired(bool required)
ArgumentBuilder & setShortName(const std::string &name)
ArgumentBuilder & addValidator(std::shared_ptr< IValidator > validator)
bool allowsMultipleValues() const
bool isRequiredArg() const
std::string getLongName() const
bool validate(const std::string &value) const
Argument(const std::string &longName, const std::string &shortName, const std::string &helpText, ArgType type, bool isRequired, bool allowsMultiple, std::optional< std::string > defaultValue, bool isPositional)
bool isPositionalArg() const
std::optional< std::string > getDefaultValue() const
std::string getShortName() const
void addValidator(std::shared_ptr< IValidator > validator)
std::string getHelpText() const