include/poac/option/help.hpp (92 lines of code) (raw):
#ifndef POAC_OPTION_HELP_HPP
#define POAC_OPTION_HELP_HPP
#include <iostream>
#include <iomanip>
#include <vector>
#include <string>
#include <cstdlib>
#include "../core/exception.hpp"
#include "../core/inference.hpp"
#include "../io/cli.hpp"
// Forward-declaration
namespace poac::core::infer {
enum class op_type_e : int;
template <typename S, typename OpTypeE, typename VS, typename = std::enable_if_t<std::is_rvalue_reference_v<VS&&>>>
auto _apply(S&& func, const OpTypeE& cmd, VS&& arg);
template <typename S, typename VS, typename = std::enable_if_t<std::is_rvalue_reference_v<VS&&>>>
std::string apply(S&& func, const S& cmd, VS&& arg);
extern const std::unordered_map<std::string, op_type_e> subcmd_map;
extern const std::unordered_map<std::string, op_type_e> option_map;
}
// TODO: help文を,コンパイル時に一つの文字列として変換する.
// TODO: optionではなく,helpコマンドとすれば,順序は,init helpを許されなくなるので明快になる.
// TODO: さらに,versionを,poacの部分に埋め込めば(もう一段階抽象化後),optionを管理する必要がなくなる.
namespace poac::option {
namespace _help {
void echo_option(const std::string& arg) {
namespace exception = core::exception;
try {
std::cout << "Usage: poac " << arg << " "
<< core::infer::apply(std::string("options"), arg, std::vector<std::string>())
<< std::endl;
}
catch (const exception::invalid_first_arg& e) {
throw exception::invalid_second_arg("--help");
}
}
template<typename T, typename U>
void show(const T& key, const U& value) {
// Eliminate -h and -v
// It assumes two characters because the regular expression is slow.
if (key.size() != 2) {
std::cout << io::cli::blue << io::cli::bold
<< " " << std::setw(9) << std::left << key << " "
<< io::cli::reset;
std::cout << io::cli::yellow
<< _apply(std::string("summary"), value, std::vector<std::string>())
<< io::cli::reset
<< std::endl;
}
}
void exec_help() {
std::cout << "Usage: poac <command> [<args>]" << std::endl << std::endl;
std::cout << io::cli::bold
<< "Available subcommands:"
<< io::cli::reset
<< std::endl;
for (const auto&[name, value] : core::infer::subcmd_map)
show(name, value);
std::cout << io::cli::bold
<< "Available options:"
<< io::cli::reset
<< std::endl;
for (const auto&[name, value] : core::infer::option_map)
show(name, value);
std::cout << std::endl
<< "See `poac <command> --help` for information on a specific command.\n"
"For full documentation, see: https://github.com/poacpm/poac#readme\n";
}
template<typename VS, typename=std::enable_if_t<std::is_rvalue_reference_v<VS&&>>>
int _main(VS&& vs) {
namespace exception = core::exception;
if (vs.size() == 0) {
exec_help();
return EXIT_SUCCESS;
}
else if (vs.size() == 1) {
echo_option(vs[0]);
return EXIT_SUCCESS;
}
else {
throw exception::invalid_second_arg("--help");
}
// show only --help's option
}
}
struct help {
static const std::string summary() {
return "Display help for a command";
}
static const std::string options() {
return "<subcommad or option>";
}
template<typename VS, typename=std::enable_if_t<std::is_rvalue_reference_v<VS&&>>>
int operator()(VS&& argv) {
return _help::_main(std::move(argv));
}
};
} // end namespace
#endif // !POAC_OPTION_HELP_HPP