Skip to content

Commit 99e23eb

Browse files
committed
Added parsing of the options (missing tests).
1 parent 3e8b33e commit 99e23eb

File tree

3 files changed

+160
-11
lines changed

3 files changed

+160
-11
lines changed

include/cppcommandline.h

Lines changed: 129 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <stdexcept>
66
#include <regex>
77
#include <memory>
8+
#include <iostream>
89

910
namespace cppcommandline
1011
{
@@ -40,6 +41,17 @@ class Option
4041
d.reset(option.d.release()); return *this;
4142
}
4243

44+
bool operator==(const Option &other) const
45+
{
46+
return d->defaultStringValue == other.d->defaultStringValue
47+
&& d->description == other.d->description
48+
&& d->longName == other.d->longName
49+
&& d->required == other.d->required
50+
&& d->shortName == other.d->shortName
51+
&& d->type == other.d->type;
52+
//TODO: Compare the union values.
53+
}
54+
4355
bool isPositional() const
4456
{
4557
return d->longName.empty();
@@ -116,7 +128,7 @@ class Option
116128

117129
Option &required()
118130
{
119-
if(d->type == Type::Undefined)
131+
if(!d->defaulted)
120132
d->required = true;
121133
else
122134
throw std::logic_error("The option " + getName() + " has default value and cannot be set as required.");
@@ -141,6 +153,7 @@ class Option
141153
{
142154
setDefault(defaultValue);
143155
d->type = getType<T>();
156+
defaulted = true;
144157
}
145158
return *this;
146159
}
@@ -155,6 +168,84 @@ class Option
155168
setValueBinding(&value);
156169
}
157170

171+
std::vector<std::string>::const_iterator match(std::vector<std::string>::const_iterator argument, std::vector<std::string>::const_iterator end)
172+
{
173+
std::vector<std::string>::const_iterator returnPosition = argument;
174+
KeyValue keyValue = getKeyValue(*argument);
175+
176+
if(isLongNameArgument(keyValue.key) || isShortNameArgument(keyValue.key))
177+
{
178+
++argument;
179+
180+
if(d->type == Type::Bool)
181+
*d->valueBinding.b = true;
182+
else
183+
{
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+
};
204+
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+
}
243+
}
244+
}
245+
246+
return returnPosition;
247+
}
248+
158249
private:
159250
enum class Type
160251
{
@@ -199,6 +290,7 @@ class Option
199290
Option::ValueBinding valueBinding;
200291
Option::Type type = Type::Undefined;
201292
bool required = false;
293+
bool defaulted = false;
202294
};
203295

204296
template<typename T> T *getBoundValue() const;
@@ -304,11 +396,11 @@ template<> void Option::setDefault(int defaultValue) { d->defaultValue.i = defau
304396
template<> void Option::setDefault(long long defaultValue) { d->defaultValue.l = defaultValue; }
305397
template<> void Option::setDefault(double defaultValue) { d->defaultValue.d = defaultValue; }
306398
template<> void Option::setDefault(bool defaultValue) { d->defaultValue.b = defaultValue; }
307-
template<> void Option::setValueBinding(std::string *binding) { d->valueBinding.s = binding; }
308-
template<> void Option::setValueBinding(int *binding) { d->valueBinding.i = binding; }
309-
template<> void Option::setValueBinding(long long *binding) { d->valueBinding.l = binding; }
310-
template<> void Option::setValueBinding(double *binding) { d->valueBinding.d = binding; }
311-
template<> void Option::setValueBinding(bool *binding) { d->valueBinding.b = binding; }
399+
template<> void Option::setValueBinding(std::string *binding) { d->valueBinding.s = binding; if(d->defaulted) *binding = d->defaultStringValue; }
400+
template<> void Option::setValueBinding(int *binding) { d->valueBinding.i = binding; if(d->defaulted) *binding = d->defaultValue.i; }
401+
template<> void Option::setValueBinding(long long *binding) { d->valueBinding.l = binding; if(d->defaulted) *binding = d->defaultValue.l; }
402+
template<> void Option::setValueBinding(double *binding) { d->valueBinding.d = binding; if(d->defaulted) *binding = d->defaultValue.d; }
403+
template<> void Option::setValueBinding(bool *binding) { d->valueBinding.b = binding; if(d->defaulted) *binding = d->defaultValue.b; }
312404
template<> Option::Type Option::getType<std::string>() const { return Type::String; }
313405
template<> Option::Type Option::getType<int>() const { return Type::Integer; }
314406
template<> Option::Type Option::getType<long long>() const { return Type::LongLong; }
@@ -332,18 +424,44 @@ class Parser
332424

333425
void parse(int argc, char **argv)
334426
{
335-
mArgc = argc;
336-
mArgv = argv;
427+
mArgs = std::vector<std::string>();
428+
429+
for(int i = 0; i < argc; i++)
430+
mArgs.emplace_back(std::string(argv[i]));
337431

338-
for(int i = 0; i < mArgc; ++i)
432+
std::vector<Option*> options;
433+
434+
for(Option &option : mOptions)
435+
options.emplace_back(&option);
436+
437+
for(auto arg = mArgs.cbegin(); arg != mArgs.cend();)
339438
{
439+
auto start = arg;
440+
441+
for(auto option = options.begin(); option != options.end(); ++option)
442+
{
443+
auto next = (*option)->match(arg, mArgs.cend());
444+
445+
if(next != arg)
446+
{
447+
options.erase(option);
448+
arg = next;
449+
}
450+
}
340451

452+
if(arg == start)
453+
throw(std::logic_error("No option set for argument '" + *arg + "'"));
454+
}
455+
456+
for(Option *option : options)
457+
{
458+
if(option->isRequired())
459+
throw(std::logic_error("Option '" + option->longName() + "' was set as required but did not match any arguments"));
341460
}
342461
}
343462

344463
private:
345-
int mArgc = 0;
346-
char **mArgv = nullptr;
464+
std::vector<std::string> mArgs;
347465
std::vector<Option> mOptions;
348466
};
349467

test/cppcommandlinetest.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,35 @@ void CppCommandLineTest::boundValue()
132132
}
133133
}
134134

135+
void CppCommandLineTest::match()
136+
{
137+
{
138+
SCENARIO("Option match")
139+
std::vector<std::string> arguments{"longName", "value"};
140+
std::string value;
141+
cppcommandline::Option option("longName");
142+
option.bindTo(value);
143+
QCOMPARE(arguments.cend(), option.match(arguments.cbegin(), arguments.cend()));
144+
}
145+
}
146+
147+
void CppCommandLineTest::ParserOption()
148+
{
149+
SCENARIO("Parser returns a default constructed option")
150+
cppcommandline::Parser parser;
151+
QCOMPARE(cppcommandline::Option(), parser.option());
152+
}
135153

154+
void CppCommandLineTest::ParserOptionLongName()
155+
{
156+
SCENARIO("Parser returns a default constructed option")
157+
cppcommandline::Parser parser;
158+
QCOMPARE(cppcommandline::Option("longName"), parser.option("longName"));
159+
}
136160

161+
void CppCommandLineTest::parse()
162+
{
137163

164+
}
138165

139166
QTEST_APPLESS_MAIN(CppCommandLineTest)

test/cppcommandlinetest.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,8 @@ private slots:
1616
void required();
1717
void defaultValue();
1818
void boundValue();
19+
void match();
20+
void ParserOption();
21+
void ParserOptionLongName();
22+
void parse();
1923
};

0 commit comments

Comments
 (0)