Skip to content

Commit e1c5ff5

Browse files
committed
Added tests for matching the variou configurations of options and fixed few issues.
1 parent 99e23eb commit e1c5ff5

File tree

2 files changed

+150
-75
lines changed

2 files changed

+150
-75
lines changed

include/cppcommandline.h

Lines changed: 87 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ class Option
153153
{
154154
setDefault(defaultValue);
155155
d->type = getType<T>();
156-
defaulted = true;
156+
d->defaulted = true;
157157
}
158158
return *this;
159159
}
@@ -170,80 +170,37 @@ class Option
170170

171171
std::vector<std::string>::const_iterator match(std::vector<std::string>::const_iterator argument, std::vector<std::string>::const_iterator end)
172172
{
173-
std::vector<std::string>::const_iterator returnPosition = argument;
174173
KeyValue keyValue = getKeyValue(*argument);
175174

176-
if(isLongNameArgument(keyValue.key) || isShortNameArgument(keyValue.key))
175+
if(keyValue.key.empty())
176+
{
177+
if(isPositional())
178+
{
179+
if(setValue(*argument))
180+
++argument;
181+
}
182+
}
183+
else if(keyValue.key == longName() || keyValue.key == shortName())
177184
{
178-
++argument;
179-
180185
if(d->type == Type::Bool)
181-
*d->valueBinding.b = true;
182-
else
183186
{
184-
if(keyValue.value.empty())
185-
{
186-
if(argument == end)
187-
throw(std::logic_error("Missing value for option '" + longName() + "'"));
188-
else
189-
{
190-
keyValue.value = *argument;
191-
++argument;
192-
}
193-
}
194-
195-
class ValueParseError : public std::logic_error
196-
{
197-
public:
198-
ValueParseError() :
199-
std::logic_error("...")
200-
{
201-
202-
}
203-
};
187+
if(setValue(*argument))
188+
++argument;
189+
}
190+
else if(keyValue.value.empty())
191+
{
192+
std::vector<std::string>::const_iterator it = argument+1;
204193

205-
try
206-
{
207-
switch(d->type)
208-
{
209-
case Type::Double:
210-
if(isDouble(*argument))
211-
*d->valueBinding.d = std::stod(*argument);
212-
else
213-
throw(ValueParseError());
214-
break;
215-
case Type::Integer:
216-
if(isInteger(*argument))
217-
*d->valueBinding.i = std::stoi(*argument);
218-
else
219-
throw(ValueParseError());
220-
break;
221-
case Type::LongLong:
222-
if(isLongLong(*argument))
223-
*d->valueBinding.l = std::stol(*argument);
224-
else
225-
throw(ValueParseError());
226-
break;
227-
case Type::String:
228-
*d->valueBinding.s = *argument;
229-
break;
230-
case Type::Undefined:
231-
throw(std::logic_error("Bind value is undefined"));
232-
break;
233-
case Type::Bool:
234-
throw(std::logic_error("Bool option should not be handled here"));
235-
break;
236-
}
237-
238-
returnPosition = argument;
239-
}
240-
catch(ValueParseError&)
241-
{
242-
}
194+
if(it == end)
195+
throw(std::logic_error("Missing value for option '" + longName() + "'"));
196+
else if(setValue(*it))
197+
argument += 2;
243198
}
199+
else if(setValue(keyValue.value))
200+
++argument;
244201
}
245202

246-
return returnPosition;
203+
return argument;
247204
}
248205

249206
private:
@@ -299,6 +256,61 @@ class Option
299256
template<typename T> void setValueBinding(T*);
300257
template<typename T> Type getType() const;
301258

259+
bool setValue(std::string value)
260+
{
261+
bool result = true;
262+
263+
class ValueParseError : public std::logic_error
264+
{
265+
public:
266+
ValueParseError() :
267+
std::logic_error("...")
268+
{
269+
270+
}
271+
};
272+
273+
try
274+
{
275+
switch(d->type)
276+
{
277+
case Type::Bool:
278+
*d->valueBinding.b = true;
279+
break;
280+
case Type::Double:
281+
if(isDouble(value))
282+
*d->valueBinding.d = std::stod(value);
283+
else
284+
throw(ValueParseError());
285+
break;
286+
case Type::Integer:
287+
if(isInteger(value))
288+
*d->valueBinding.i = std::stoi(value);
289+
else
290+
throw(ValueParseError());
291+
break;
292+
case Type::LongLong:
293+
if(isLongLong(value))
294+
*d->valueBinding.l = std::stoll(value);
295+
else
296+
throw(ValueParseError());
297+
break;
298+
case Type::String:
299+
*d->valueBinding.s = value;
300+
break;
301+
case Type::Undefined:
302+
throw(std::logic_error("Bind value undefined for option '" + longName() + "'"));
303+
break;
304+
}
305+
}
306+
catch(ValueParseError&)
307+
{
308+
result = false;
309+
}
310+
311+
return result;
312+
}
313+
302314
std::string getName() const
303315
{
304316
return d->longName.empty() ? "[Positional]" : "'" + d->longName + "'";
@@ -324,12 +336,14 @@ class Option
324336
KeyValue getKeyValue(std::string argument) const
325337
{
326338
KeyValue keyValue;
327-
if(std::regex_match(argument, std::regex("^(--|-)[a-zA-Z\\d]=.*")))
339+
std::cmatch m;
340+
if(std::regex_match(argument.c_str(), m, std::regex("^(--|-)([a-zA-Z\\d]+)((=(.*))|)")))
328341
{
329-
size_t pos = argument.find_first_of('=') + 1;
330-
keyValue.key = argument.substr(0, pos);
331-
keyValue.value = argument.substr(pos, argument.size() - pos);
342+
keyValue.key = m.size() > 2 ? std::string(m[2]) : "";
343+
keyValue.value = m.size() > 4 ? std::string(m[5]) : "";
332344
}
345+
else
346+
keyValue.value = argument;
333347
return keyValue;
334348
}
335349

@@ -355,17 +369,17 @@ class Option
355369

356370
bool isLongLong(std::string argument) const
357371
{
358-
return isNumber(argument) && (std::stoll(argument) < std::numeric_limits<int>::min() || std::stoll(argument) > std::numeric_limits<int>::max());
372+
return isNumber(argument);
359373
}
360374

361375
bool isDouble(std::string argument) const
362376
{
363-
return std::regex_match(argument, std::regex("^(-|\\d)\\d+(.|,)\\d+$"));
377+
return std::regex_match(argument, std::regex("^(-|)\\d+\\.\\d+$"));
364378
}
365379

366380
bool isNumber(std::string argument) const
367381
{
368-
return std::regex_match(argument, std::regex("^(-|\\d)\\d+$"));
382+
return std::regex_match(argument, std::regex("^(-|)\\d+$"));
369383
}
370384

371385
bool isLongName(std::string longName) const

test/cppcommandlinetest.cpp

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,73 @@ void CppCommandLineTest::boundValue()
135135
void CppCommandLineTest::match()
136136
{
137137
{
138-
SCENARIO("Option match")
139-
std::vector<std::string> arguments{"longName", "value"};
138+
SCENARIO("Option match separate option and value")
139+
std::vector<std::string> arguments{"--longName", "value"};
140140
std::string value;
141141
cppcommandline::Option option("longName");
142142
option.bindTo(value);
143143
QCOMPARE(arguments.cend(), option.match(arguments.cbegin(), arguments.cend()));
144+
QCOMPARE(std::string("value"), value);
145+
}
146+
147+
{
148+
SCENARIO("Option match joined option and value with =")
149+
std::vector<std::string> arguments{"--longName=value"};
150+
std::string value;
151+
cppcommandline::Option option("longName");
152+
option.bindTo(value);
153+
QCOMPARE(arguments.cend(), option.match(arguments.cbegin(), arguments.cend()));
154+
QCOMPARE(std::string("value"), value);
155+
}
156+
157+
{
158+
SCENARIO("Option match positional boolean argument")
159+
std::vector<std::string> arguments{"positional"};
160+
bool value;
161+
cppcommandline::Option option;
162+
option.bindTo(value);
163+
QCOMPARE(arguments.cend(), option.match(arguments.cbegin(), arguments.cend()));
164+
QCOMPARE(true, value);
165+
}
166+
167+
{
168+
SCENARIO("Option match positional argument with value")
169+
std::vector<std::string> arguments{"value"};
170+
std::string value;
171+
cppcommandline::Option option;
172+
option.bindTo(value);
173+
QCOMPARE(arguments.cend(), option.match(arguments.cbegin(), arguments.cend()));
174+
QCOMPARE(std::string("value"), value);
175+
}
176+
177+
{
178+
SCENARIO("Option match `int` argument")
179+
std::vector<std::string> arguments{"10"};
180+
int value = 0;
181+
cppcommandline::Option option;
182+
option.bindTo(value);
183+
QCOMPARE(arguments.cend(), option.match(arguments.cbegin(), arguments.cend()));
184+
QCOMPARE(10, value);
185+
}
186+
187+
{
188+
SCENARIO("Option match `double` argument")
189+
std::vector<std::string> arguments{"5.5"};
190+
double value = 0;
191+
cppcommandline::Option option;
192+
option.bindTo(value);
193+
QCOMPARE(arguments.cend(), option.match(arguments.cbegin(), arguments.cend()));
194+
QCOMPARE(double(5.5), value);
195+
}
196+
197+
{
198+
SCENARIO("Option match `long long` argument")
199+
std::vector<std::string> arguments{"999999999999"};
200+
qint64 value = 0;
201+
cppcommandline::Option option;
202+
option.bindTo(value);
203+
QCOMPARE(arguments.cend(), option.match(arguments.cbegin(), arguments.cend()));
204+
QCOMPARE(qint64(999999999999), value);
144205
}
145206
}
146207

0 commit comments

Comments
 (0)