blob: e0e8304c361521b2c606f595596562143d86fae6 [file] [log] [blame]
#include "private/cli/commandmap.hpp"
#include <sstream>
#include <iomanip>
#include <memory>
#include <cstdlib>
#include <boost/io/ios_state.hpp>
using namespace shareddatalayer::cli;
namespace
{
std::string buildCommandNameAlreadyRegisteredError(const std::string& commandName)
{
std::ostringstream os;
os << "command name \"" << commandName << "\" already registered";
return os.str();
}
std::string buildUnknownCommandNameError(const std::string& commandName)
{
std::ostringstream os;
os << "unknown command: \"" << commandName << '\"';
return os.str();
}
std::string buildCategoryOffsetAlreadyRegisteredError(const std::string& commandName, int offset)
{
std::ostringstream os;
os << commandName << ": Offset " << offset << " already registered";
return os.str();
}
std::string getCategoryName(CommandMap::Category category)
{
switch (category)
{
case CommandMap::Category::UTIL:
return "Utility";
default:
return "Unknown";
}
}
}
CommandMap::CommandNameAlreadyRegistered::CommandNameAlreadyRegistered(const std::string& commandName):
Exception(buildCommandNameAlreadyRegisteredError(commandName))
{
}
CommandMap::UnknownCommandName::UnknownCommandName(const std::string& commandName):
Exception(buildUnknownCommandNameError(commandName))
{
}
CommandMap::CategoryOffsetAlreadyRegistered::CategoryOffsetAlreadyRegistered(const std::string& commandName, int offset):
Exception(buildCategoryOffsetAlreadyRegisteredError(commandName, offset))
{
}
struct CommandMap::Info
{
CommandFunction function;
std::string shortHelp;
std::string longHelp;
boost::program_options::options_description options;
Info(const CommandFunction& function,
const std::string& shortHelp,
const std::string& longHelp);
};
CommandMap::Info::Info(const CommandFunction& function,
const std::string& shortHelp,
const std::string& longHelp):
function(function),
shortHelp(shortHelp),
longHelp(longHelp)
{
}
CommandMap::CommandMap()
{
}
CommandMap::~CommandMap()
{
}
boost::program_options::options_description&
CommandMap::registerCommand(const std::string& commandName,
const std::string& shortHelp,
const std::string& longHelp,
const CommandFunction& commandFunction,
Category category,
int categoryOffset)
{
const auto ret(map.insert(std::make_pair(commandName, Info(commandFunction, shortHelp, longHelp))));
if (!ret.second)
throw CommandNameAlreadyRegistered(commandName);
const auto retCat(categoryMap.insert(std::make_pair(CategoryKey(category, categoryOffset), commandName)));
if (!retCat.second)
throw CategoryOffsetAlreadyRegistered(commandName, categoryOffset);
return ret.first->second.options;
}
std::vector<std::string> CommandMap::getCommandNames() const
{
std::vector<std::string> ret;
for (const auto& i : map)
ret.push_back(i.first);
return ret;
}
const boost::program_options::options_description&
CommandMap::getCommandOptions(const std::string& commandName) const
{
const auto i(map.find(commandName));
if (i == map.end())
throw UnknownCommandName(commandName);
return i->second.options;
}
void CommandMap::shortHelp(std::ostream& out) const
{
boost::io::ios_all_saver guard(out);
size_t maxWidth(0U);
Category currentCategory(Category::UNDEFINED);
for (const auto& i : map)
if (maxWidth < i.first.size())
maxWidth = i.first.size();
for (const auto& i : categoryMap)
{
if (currentCategory != i.first.first)
{
currentCategory = i.first.first;
out << std::endl;
out << getCategoryName(currentCategory) << " commands:" << std::endl;
}
out << std::left << std::setw(maxWidth + 2U) << i.second << map.at(i.second).shortHelp << '\n';
}
}
void CommandMap::longHelp(std::ostream& out, const std::string& commandName) const
{
const auto i(map.find(commandName));
if (i == map.end())
throw UnknownCommandName(commandName);
out << i->first;
if (!i->second.options.options().empty())
{
out << " OPTIONS\n\n" << i->second.longHelp << "\n\nOptions:\n";
i->second.options.print(out);
}
else
{
out << "\n\n" << i->second.longHelp << '\n';
}
}
int CommandMap::execute(const std::string& commandName,
std::ostream& out,
std::ostream& err,
const boost::program_options::variables_map& params,
size_t count)
{
const auto i(map.find(commandName));
if (i == map.end())
{
err << "unknown command: \"" << commandName << '\"' << std::endl;
return EXIT_FAILURE;
}
const auto& function(i->second.function);
int ret(EXIT_SUCCESS);
for (size_t i = 0U; i < count; ++i)
if ((ret = function(out, err, params)) != EXIT_SUCCESS)
break;
return ret;
}
CommandMap& CommandMap::getCommandMap() noexcept
{
static CommandMap instance;
return instance;
}