cli.h 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. //
  2. // cli.h
  3. // tax-manager
  4. //
  5. // Created by Sam Jaffe on 10/8/20.
  6. // Copyright © 2020 Sam Jaffe. All rights reserved.
  7. //
  8. #pragma once
  9. #include <functional>
  10. #include <iostream>
  11. #include <string>
  12. #include <tuple>
  13. #include <unordered_map>
  14. #include <vector>
  15. #include "lambda_cast.h"
  16. namespace cli {
  17. template <typename T> struct is_string : std::false_type {};
  18. template <typename T>
  19. struct is_string<T*> : std::is_same<std::remove_cv_t<T>, char> {};
  20. template <typename T>
  21. struct is_string<T[]> : std::is_same<std::remove_cv_t<T>, char> {};
  22. template <typename...Ts>
  23. struct is_string<std::basic_string<Ts...>> : std::true_type {};
  24. template <typename T, typename = void> struct is_container : std::false_type {};
  25. template <typename T>
  26. struct is_container<T, std::enable_if_t<!std::is_void_v<typename T::value_type>>> : std::true_type {};
  27. template <typename T>
  28. static constexpr bool const is_string_v = is_string<T>::value;
  29. template <typename T>
  30. static constexpr bool const is_container_v = is_container<T>::value;
  31. class cli {
  32. public:
  33. using args_t = std::vector<std::string>;
  34. using callback = std::function<void(args_t const &)>;
  35. private:
  36. std::istream &in_;
  37. std::string prompt_;
  38. std::unordered_map<std::string, size_t> arguments_;
  39. std::unordered_map<std::string, callback> callbacks_;
  40. bool eof_;
  41. public:
  42. cli(std::string const & prompt, std::istream &in = std::cin);
  43. cli(std::istream &in = std::cin);
  44. template <typename... Args>
  45. cli & register_callback(std::string const & handle,
  46. std::function<void(Args...)> cb);
  47. template <typename F>
  48. cli & register_callback(std::string const & handle, F && cb) {
  49. return register_callback(handle, lambdas::FFL(cb));
  50. }
  51. template <typename T>
  52. cli & register_query(std::string const & handle, std::function<T()> cb);
  53. template <typename F>
  54. cli & register_query(std::string const & handle, F && cb) {
  55. return register_query(handle, lambdas::FFL(cb));
  56. }
  57. void run() const;
  58. template <typename T, size_t I>
  59. static auto from_string(std::string const & value) {
  60. using E = std::tuple_element_t<I, T>;
  61. using V = std::remove_cv_t<std::remove_reference_t<E>>;
  62. return from_string<V>(value);
  63. }
  64. private:
  65. bool active() const;
  66. template <typename T> static T from_string(std::string const &);
  67. };
  68. template <typename... Args, size_t... Is>
  69. void cli_invoke(std::function<void(Args...)> cb, cli::args_t const & args,
  70. std::index_sequence<Is...>) {
  71. using tuple = std::tuple<Args...>;
  72. cb(cli::from_string<tuple, Is>(args[Is])...);
  73. }
  74. template <typename... Args>
  75. cli & cli::register_callback(std::string const & handle,
  76. std::function<void(Args...)> cb) {
  77. arguments_.emplace(handle, sizeof...(Args));
  78. callbacks_.emplace(handle, [=](args_t const & args) {
  79. if (sizeof...(Args) > args.size()) {
  80. std::cerr << "Missing Args in command '" << handle << "', " <<
  81. sizeof...(Args) << " required, but " << args.size() << " input\n";
  82. return;
  83. }
  84. cli_invoke(cb, args, std::make_index_sequence<sizeof...(Args)>());
  85. });
  86. return *this;
  87. }
  88. template <typename T> void cli_print(T const & value) {
  89. if constexpr (is_container_v<T> && !is_string_v<T>) {
  90. using ::cli::cli_print;
  91. for (auto & elem : value) {
  92. cli_print(elem);
  93. }
  94. } else {
  95. std::cout << " " << value << "\n";
  96. }
  97. }
  98. template <typename T>
  99. cli & cli::register_query(std::string const & handle, std::function<T()> cb) {
  100. arguments_.emplace(handle, 0);
  101. callbacks_.emplace(handle, [=](args_t const & args) {
  102. using ::cli::cli_print;
  103. cli_print(cb());
  104. });
  105. return *this;
  106. }
  107. }