Skip to content

Commit

Permalink
Add subcommand handling
Browse files Browse the repository at this point in the history
  • Loading branch information
attah committed Jan 27, 2024
1 parent 99ffad9 commit af3fd5f
Show file tree
Hide file tree
Showing 3 changed files with 298 additions and 138 deletions.
131 changes: 110 additions & 21 deletions lib/argget.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,20 +234,20 @@ class PosArg : public ArgBase

class ArgGet
{
friend class SubArgGet;

public:
ArgGet() = delete;
ArgGet(const ArgGet&) = delete;
ArgGet& operator=(const ArgGet&) = delete;
ArgGet() = default;
ArgGet(const ArgGet&) = default;
ArgGet& operator=(const ArgGet&) = default;

ArgGet(std::initializer_list<SwitchArgBase*> argDefs,
std::initializer_list<PosArg*> posArgDefs = {})
ArgGet(std::list<SwitchArgBase*> argDefs, std::list<PosArg*> posArgDefs = {})
: _argDefs(argDefs), _posArgDefs(posArgDefs)
{}

bool get_args(int argc, char** argv)
{
std::list<std::string> argList;
_errMsg = std::stringstream();

if(argc < 1)
{
Expand Down Expand Up @@ -278,17 +278,17 @@ class ArgGet
{
if(argList.size() == 0)
{
_errMsg << "Missing value for " << argDef->docName();
_errMsg = "Missing value for " + argDef->docName();
}
else if(argDef->errorHint() != "")
{
_errMsg << argDef->errorHint()
<< " (" << argList.front() << ")";
_errMsg = argDef->errorHint()
+ " (" + argList.front() + ")";
}
else
{
_errMsg << "Bad value for " << argDef->docName()
<< " (" << argList.front() << ")";
_errMsg = "Bad value for " + argDef->docName()
+ " (" + argList.front() + ")";
}
return false;
}
Expand All @@ -297,15 +297,15 @@ class ArgGet

if(argList.size() > _posArgDefs.size())
{ // Cannot consume all - fail early.
_errMsg << "Unknown argument: " << argList.front();
_errMsg = "Unknown argument: " + argList.front();
return false;
}

for(PosArg* posArg : _posArgDefs)
{
if(!posArg->parse(argList))
{
_errMsg << "Missing positional argument " << (posArg->docName());
_errMsg = "Missing positional argument " + (posArg->docName());
return false;
}
}
Expand All @@ -316,17 +316,33 @@ class ArgGet
}
else
{
_errMsg << "Unknown argument: " << argList.front();
_errMsg = "Unknown argument: " + argList.front();
return false;
}
}

std::string argHelp()
{
std::string help = "Usage: " + _name + _argHelp();
return help;
}

std::string errmsg()
{
return _errMsg;
}

protected:

std::string _argHelp() const
{
std::stringstream help;
size_t w = 0;

help << "Usage: " << _name << " [options]";
if(!_argDefs.empty())
{
help << " [options]";
}
for(PosArg* posArg : _posArgDefs)
{
help << " " << posArg->docName();
Expand All @@ -345,21 +361,94 @@ class ArgGet
return help.str();
}

std::string name()
private:
std::string _name;
std::list<SwitchArgBase*> _argDefs;
std::list<PosArg*> _posArgDefs;
std::string _errMsg;

};

class SubArgGet
{
public:
SubArgGet() = delete;
SubArgGet(const SubArgGet&) = delete;
SubArgGet& operator=(const SubArgGet&) = delete;

SubArgGet(std::map<std::string, std::pair<std::list<SwitchArgBase*>, std::list<PosArg*>>> map)
{
return _name;
for(const auto& [subCommand, args] : map)
{
_subCommands[subCommand] = ArgGet(args.first, args.second);
}
}

bool get_args(int argc, char** argv)
{
std::list<std::string> argList;

if(argc < 1)
{
throw(std::logic_error("No program name"));
}
_name = argv[0];

for(int i = 1; i < argc; i++)
{
argList.push_back(argv[i]);
}

if(argc < 2)
{
_errMsg = "No sub-command given";
return false;
}
_subCommand = argv[1];
if(_subCommands.find(_subCommand) == _subCommands.end())
{
_errMsg = "Invalid sub-command";
return false;
}

ArgGet& argGet = _subCommands[_subCommand];
if(!argGet.get_args(argc-1, &argv[1]))
{
_errMsg = argGet.errmsg();
return false;
}

return true;
}

std::string argHelp()
{
std::stringstream help;

help << "Usage: " << _name << " <sub-command> [options]" << std::endl << std::endl;
for(const auto& [subCommand, argGet] : _subCommands)
{
help << _name << " " << subCommand << argGet._argHelp() << std::endl;
}
return help.str();
}

std::string errmsg()
{
return _errMsg.str();
return _errMsg;
}

std::string subCommand()
{
return _subCommand;
}

private:
std::string _name;
std::list<SwitchArgBase*> _argDefs;
std::list<PosArg*> _posArgDefs;
std::stringstream _errMsg;
std::string _subCommand;
std::map<std::string, ArgGet> _subCommands;
std::string _errMsg;

};

#endif //ARGGET_H
47 changes: 47 additions & 0 deletions tests/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1150,6 +1150,53 @@ TEST(argget)

}

TEST(argget_subcommand)
{
bool b = false;
SwitchArg<bool> boolopt(b, {"-b", "--bool"}, "A bool option");

std::string s;
SwitchArg<string> stringopt(s, {"-s", "--string"}, "A string option");

std::string a1;
PosArg arg1(a1, "arg1");

SubArgGet get({{"boolify", {{&boolopt}, {}}},
{"stringify", {{&stringopt}, {}}},
{"posify", {{}, {&arg1}}}});
char* argv[3] = {(char*)"myprog",
(char*)"boolify",
(char*)"-b"};

ASSERT(get.get_args(3, argv));
ASSERT(boolopt.isSet());
ASSERT(b);

char* argv1[4] = {(char*)"myprog",
(char*)"stringify",
(char*)"--string",
(char*)"mystring"};

ASSERT(get.get_args(4, argv1));
ASSERT(stringopt.isSet());
ASSERT(s=="mystring");

char* argv2[3] = {(char*)"myprog",
(char*)"posify",
(char*)"myposarg"};

ASSERT(get.get_args(3, argv2));
ASSERT(arg1.isSet());
ASSERT(a1=="myposarg");

char* argv3[4] = {(char*)"myprog",
(char*)"stringify",
(char*)"-b"};

ASSERT_FALSE(get.get_args(3, argv3));

}

TEST(flip_logic)
{
PrintParameters params;
Expand Down
Loading

0 comments on commit af3fd5f

Please sign in to comment.