arguments_impl.hpp 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. #pragma once
  2. #include "options.hpp"
  3. #include <utility>
  4. namespace program {
  5. template <typename Impl>
  6. template <typename T>
  7. Arguments<Impl>::Option::operator T() const {
  8. return (*this) ? self->options.at(name) : T();
  9. }
  10. template <typename Impl>
  11. Arguments<Impl>::Option::operator bool() const {
  12. return primed() && self->options.count(name);
  13. }
  14. template <typename Impl>
  15. template <typename T>
  16. auto Arguments<Impl>::Option::operator=(T &&value) {
  17. return WithDefault<Option, T>{*this, std::forward<T>(value)};
  18. }
  19. inline std::string join(std::string const &tok,
  20. std::vector<std::string> const &data) {
  21. std::string accum = data.empty() ? "" : data.front();
  22. for (size_t i = 1; i < data.size(); ++i) {
  23. accum += tok;
  24. accum += data[i];
  25. }
  26. return accum;
  27. }
  28. template <typename Impl>
  29. bool Arguments<Impl>::Option::primed() const {
  30. if (self->primed_) { return true; }
  31. std::vector<std::string> aliases{"--" + name};
  32. if (abbrev) {
  33. aliases.emplace_back(std::string{'-', abbrev});
  34. }
  35. for (auto &alias : aliases) {
  36. self->option_name_mapping.emplace(alias, name);
  37. }
  38. self->option_descriptions.emplace(join("/", aliases), description);
  39. return false;
  40. }
  41. }
  42. namespace program {
  43. template <typename Impl>
  44. template <typename T>
  45. Arguments<Impl>::Positional::operator T() const {
  46. return (*this) ? self->arguments.at(index) : T();
  47. }
  48. template <typename Impl>
  49. Arguments<Impl>::Positional::operator bool() const {
  50. return primed(); // TODO(samjaffe): Existance check?
  51. }
  52. template <typename Impl>
  53. template <typename T>
  54. auto Arguments<Impl>::Positional::operator=(T &&value) {
  55. is_optional = true;
  56. return WithDefault<Positional, T>{*this, std::forward<T>(value)};
  57. }
  58. template <typename Impl>
  59. bool Arguments<Impl>::Positional::primed() const {
  60. if (self->primed_) { return true; }
  61. if (is_optional) {
  62. self->optional_from = std::min(self->optional_from, index);
  63. } else if (self->optional_from < index) {
  64. throw std::logic_error{
  65. "Cannot have required positional named '" + name +
  66. "' after optional positionals"};
  67. }
  68. self->argument_names.emplace(index, name);
  69. self->positional_descriptions.emplace(name, description);
  70. return false;
  71. }
  72. }
  73. #define arg_equals(str) !strncmp(argv[i], str, sizeof(str))
  74. namespace program {
  75. template <typename Impl>
  76. Arguments<Impl>::Arguments(int argc, char const * const * const argv) {
  77. Impl generator;
  78. *this = static_cast<Arguments const&>(generator);
  79. if (argument_names.rbegin()->first != argument_names.size() - 1) {
  80. throw std::logic_error{"Jagged arguments are not supported"};
  81. }
  82. primed_ = true;
  83. program = argv[0];
  84. for (size_t i = 1; i < argc; ++i) {
  85. if (arg_equals("--help")) {
  86. usage();
  87. std::exit(0);
  88. } else if (arg_equals("--")) {
  89. arguments.insert(arguments.end(), &argv[i+1], &argv[argc]);
  90. i = argc;
  91. } else if (argv[i][0] == '-') {
  92. // TODO(samjaffe): Arity
  93. options.emplace(option_name_mapping.at(argv[i]), argv[i+1]);
  94. ++i;
  95. } else {
  96. arguments.emplace_back(argv[i]);
  97. }
  98. }
  99. }
  100. template <typename Impl>
  101. void Arguments<Impl>::usage() const {
  102. std::cout << program << " [options...]";
  103. for (auto &[index, name] : argument_names) {
  104. std::cout << " " << (index == optional_from ? "[" : "") << name;
  105. }
  106. if (optional_from != std::numeric_limits<size_t>::max()) {
  107. std::cout << "]";
  108. }
  109. std::cout << "\nPositional Arguments:\n";
  110. for (auto &[name, desc] : positional_descriptions) {
  111. std::cout << " " << name << ": " << desc << "\n";
  112. }
  113. std::cout << "Options:\n";
  114. for (auto &[opt, desc] : option_descriptions) {
  115. std::cout << " " << opt << ": " << desc << "\n";
  116. }
  117. }
  118. template <typename Impl>
  119. auto Arguments<Impl>::option(std::string const &name, std::string const &description) {
  120. return Option{this, name, 0, description};
  121. }
  122. template <typename Impl>
  123. auto Arguments<Impl>::argument(size_t index, std::string const &name,
  124. std::string const &description) {
  125. return Positional{this, index, false, name, description};
  126. }
  127. }