| #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; |
| } |
| |