#include "private/cli/commandmap.hpp" #include #include #include #include #include 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 CommandMap::getCommandNames() const { std::vector 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; }