// // cli.h // tax-manager // // Created by Sam Jaffe on 10/8/20. // Copyright © 2020 Sam Jaffe. All rights reserved. // #pragma once #include #include #include #include #include #include #include #include #include "lambda_cast.h" #include "types.h" namespace cli { class cli { public: using args_t = std::vector; using callback = std::function; private: string_utils::EscapedTokenizer tokenize_{" ", {'"', "\\\""}}; std::istream *in_; std::string prompt_; std::unordered_map arguments_; std::unordered_map callbacks_; mutable bool eof_; public: explicit cli(std::string const & prompt, std::istream &in = std::cin); explicit cli(std::istream &in = std::cin); template cli & register_callback(std::string const & handle, std::function cb); template cli & register_callback(std::string const & handle, F && cb) { return register_callback(handle, lambdas::FFL(cb)); } template cli & register_query(std::string const & handle, std::function cb); template cli & register_query(std::string const & handle, F && cb) { return register_query(handle, lambdas::FFL(cb)); } void run() const { in_ ? run_stream() : run_interactive(); } private: void run_interactive() const; void run_stream() const; void run(std::string &command) const; bool active() const; cli(cli const &) = delete; cli(cli &&) = delete; cli &operator=(cli const &) = delete; cli &operator=(cli &&) = delete; }; template void cli_invoke(std::function cb, cli::args_t const & args, std::index_sequence) { std::tuple...> object; if (string_utils::cast(object, args)) { cb(std::move(std::get(object))...); } } template cli & cli::register_callback(std::string const & handle, std::function cb) { constexpr size_t N = sizeof...(Args); arguments_.emplace(handle, N); callbacks_.emplace(handle, [=](args_t args) { if (N > args.size()) { std::cerr << "Missing Args in command '" << handle << "', " << N << " required, but " << args.size() << " input\n"; return; } args.resize(N); cli_invoke(cb, args, std::make_index_sequence()); }); return *this; } template void cli_print(T const & value) { if constexpr (detail::is_container_v && !detail::is_string_v) { using ::cli::cli_print; for (auto & elem : value) { cli_print(elem); } } else if constexpr (detail::is_map_value_type_v) { std::cout << " " << value.first << ": " << value.second << "\n"; } else { std::cout << " " << value << "\n"; } } template cli & cli::register_query(std::string const & handle, std::function cb) { arguments_.emplace(handle, 0); callbacks_.emplace(handle, [=](args_t const & args) { using ::cli::cli_print; cli_print(cb()); }); return *this; } }