cmCommandLineArgument.h 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #pragma once
  4. #include "cmStringAlgorithms.h"
  5. #include "cmSystemTools.h"
  6. template <typename FunctionSignature>
  7. struct cmCommandLineArgument
  8. {
  9. enum class Values
  10. {
  11. Zero,
  12. One,
  13. Two,
  14. ZeroOrOne,
  15. OneOrMore
  16. };
  17. enum class RequiresSeparator
  18. {
  19. Yes,
  20. No
  21. };
  22. enum class ParseMode
  23. {
  24. Valid,
  25. Invalid,
  26. SyntaxError,
  27. ValueError
  28. };
  29. std::string InvalidSyntaxMessage;
  30. std::string InvalidValueMessage;
  31. std::string Name;
  32. Values Type;
  33. RequiresSeparator SeparatorNeeded;
  34. std::function<FunctionSignature> StoreCall;
  35. template <typename FunctionType>
  36. cmCommandLineArgument(std::string n, Values t, FunctionType&& func)
  37. : InvalidSyntaxMessage(cmStrCat(" is invalid syntax for ", n))
  38. , InvalidValueMessage(cmStrCat("Invalid value used with ", n))
  39. , Name(std::move(n))
  40. , Type(t)
  41. , SeparatorNeeded(RequiresSeparator::Yes)
  42. , StoreCall(std::forward<FunctionType>(func))
  43. {
  44. }
  45. template <typename FunctionType>
  46. cmCommandLineArgument(std::string n, Values t, RequiresSeparator s,
  47. FunctionType&& func)
  48. : InvalidSyntaxMessage(cmStrCat(" is invalid syntax for ", n))
  49. , InvalidValueMessage(cmStrCat("Invalid value used with ", n))
  50. , Name(std::move(n))
  51. , Type(t)
  52. , SeparatorNeeded(s)
  53. , StoreCall(std::forward<FunctionType>(func))
  54. {
  55. }
  56. template <typename FunctionType>
  57. cmCommandLineArgument(std::string n, std::string failedMsg, Values t,
  58. FunctionType&& func)
  59. : InvalidSyntaxMessage(cmStrCat(" is invalid syntax for ", n))
  60. , InvalidValueMessage(std::move(failedMsg))
  61. , Name(std::move(n))
  62. , Type(t)
  63. , SeparatorNeeded(RequiresSeparator::Yes)
  64. , StoreCall(std::forward<FunctionType>(func))
  65. {
  66. }
  67. template <typename FunctionType>
  68. cmCommandLineArgument(std::string n, std::string failedMsg, Values t,
  69. RequiresSeparator s, FunctionType&& func)
  70. : InvalidSyntaxMessage(cmStrCat(" is invalid syntax for ", n))
  71. , InvalidValueMessage(std::move(failedMsg))
  72. , Name(std::move(n))
  73. , Type(t)
  74. , SeparatorNeeded(s)
  75. , StoreCall(std::forward<FunctionType>(func))
  76. {
  77. }
  78. bool matches(std::string const& input) const
  79. {
  80. bool matched = false;
  81. if (this->Type == Values::Zero) {
  82. matched = (input == this->Name);
  83. } else if (this->SeparatorNeeded == RequiresSeparator::No) {
  84. matched = cmHasPrefix(input, this->Name);
  85. } else if (cmHasPrefix(input, this->Name)) {
  86. if (input.size() == this->Name.size()) {
  87. matched = true;
  88. } else {
  89. matched =
  90. (input[this->Name.size()] == '=' || input[this->Name.size()] == ' ');
  91. }
  92. }
  93. return matched;
  94. }
  95. template <typename T, typename... CallState>
  96. bool parse(std::string const& input, T& index,
  97. std::vector<std::string> const& allArgs,
  98. CallState&&... state) const
  99. {
  100. ParseMode parseState = ParseMode::Valid;
  101. if (this->Type == Values::Zero) {
  102. if (input.size() == this->Name.size()) {
  103. parseState =
  104. this->StoreCall(std::string{}, std::forward<CallState>(state)...)
  105. ? ParseMode::Valid
  106. : ParseMode::Invalid;
  107. } else {
  108. parseState = ParseMode::SyntaxError;
  109. }
  110. } else if (this->Type == Values::One || this->Type == Values::ZeroOrOne) {
  111. if (input.size() == this->Name.size()) {
  112. auto nextValueIndex = index + 1;
  113. if (nextValueIndex >= allArgs.size() ||
  114. allArgs[nextValueIndex][0] == '-') {
  115. if (this->Type == Values::ZeroOrOne) {
  116. parseState =
  117. this->StoreCall(std::string{}, std::forward<CallState>(state)...)
  118. ? ParseMode::Valid
  119. : ParseMode::Invalid;
  120. } else {
  121. parseState = ParseMode::ValueError;
  122. }
  123. } else {
  124. parseState = this->StoreCall(allArgs[nextValueIndex],
  125. std::forward<CallState>(state)...)
  126. ? ParseMode::Valid
  127. : ParseMode::Invalid;
  128. index = nextValueIndex;
  129. }
  130. } else {
  131. auto value = this->extract_single_value(input, parseState);
  132. if (parseState == ParseMode::Valid) {
  133. parseState =
  134. this->StoreCall(value, std::forward<CallState>(state)...)
  135. ? ParseMode::Valid
  136. : ParseMode::Invalid;
  137. }
  138. }
  139. } else if (this->Type == Values::Two) {
  140. if (input.size() == this->Name.size()) {
  141. if (index + 2 >= allArgs.size() || allArgs[index + 1][0] == '-' ||
  142. allArgs[index + 2][0] == '-') {
  143. parseState = ParseMode::ValueError;
  144. } else {
  145. index += 2;
  146. parseState =
  147. this->StoreCall(cmStrCat(allArgs[index - 1], ";", allArgs[index]),
  148. std::forward<CallState>(state)...)
  149. ? ParseMode::Valid
  150. : ParseMode::Invalid;
  151. }
  152. }
  153. } else if (this->Type == Values::OneOrMore) {
  154. if (input.size() == this->Name.size()) {
  155. auto nextValueIndex = index + 1;
  156. if (nextValueIndex >= allArgs.size() ||
  157. allArgs[nextValueIndex][0] == '-') {
  158. parseState = ParseMode::ValueError;
  159. } else {
  160. std::string buffer = allArgs[nextValueIndex++];
  161. while (nextValueIndex < allArgs.size() &&
  162. allArgs[nextValueIndex][0] != '-') {
  163. buffer = cmStrCat(buffer, ";", allArgs[nextValueIndex++]);
  164. }
  165. parseState =
  166. this->StoreCall(buffer, std::forward<CallState>(state)...)
  167. ? ParseMode::Valid
  168. : ParseMode::Invalid;
  169. index = (nextValueIndex - 1);
  170. }
  171. } else {
  172. auto value = this->extract_single_value(input, parseState);
  173. if (parseState == ParseMode::Valid) {
  174. parseState =
  175. this->StoreCall(value, std::forward<CallState>(state)...)
  176. ? ParseMode::Valid
  177. : ParseMode::Invalid;
  178. }
  179. }
  180. }
  181. if (parseState == ParseMode::SyntaxError) {
  182. cmSystemTools::Error(
  183. cmStrCat("'", input, "'", this->InvalidSyntaxMessage));
  184. } else if (parseState == ParseMode::ValueError) {
  185. cmSystemTools::Error(this->InvalidValueMessage);
  186. }
  187. return (parseState == ParseMode::Valid);
  188. }
  189. private:
  190. std::string extract_single_value(std::string const& input,
  191. ParseMode& parseState) const
  192. {
  193. // parse the string to get the value
  194. auto possible_value = cm::string_view(input).substr(this->Name.size());
  195. if (possible_value.empty()) {
  196. parseState = ParseMode::ValueError;
  197. } else if (possible_value[0] == '=') {
  198. possible_value.remove_prefix(1);
  199. if (possible_value.empty()) {
  200. parseState = ParseMode::ValueError;
  201. }
  202. }
  203. if (parseState == ParseMode::Valid && possible_value[0] == ' ') {
  204. possible_value.remove_prefix(1);
  205. }
  206. return std::string(possible_value);
  207. }
  208. };