ArgsParser.h 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. /*
  2. * Copyright (c) 2020, Sergey Bugaev <bugaevc@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <AK/ByteString.h>
  8. #include <AK/Concepts.h>
  9. #include <AK/Function.h>
  10. #include <AK/Vector.h>
  11. #include <LibMain/Main.h>
  12. #include <stdio.h>
  13. namespace Core {
  14. class ArgsParser {
  15. public:
  16. ArgsParser();
  17. enum class Required {
  18. Yes,
  19. No
  20. };
  21. enum class FailureBehavior {
  22. PrintUsageAndExit,
  23. PrintUsage,
  24. Exit,
  25. Ignore,
  26. };
  27. enum class OptionArgumentMode {
  28. None,
  29. Optional,
  30. Required,
  31. };
  32. /// When an option is hidden.
  33. /// If the hide mode is not None, then it's always hidden from the usage/synopsis.
  34. enum class OptionHideMode {
  35. None,
  36. Markdown,
  37. CommandLineAndMarkdown,
  38. };
  39. struct Option {
  40. OptionArgumentMode argument_mode { OptionArgumentMode::Required };
  41. char const* help_string { nullptr };
  42. char const* long_name { nullptr };
  43. char short_name { 0 };
  44. char const* value_name { nullptr };
  45. Function<ErrorOr<bool>(StringView)> accept_value;
  46. OptionHideMode hide_mode { OptionHideMode::None };
  47. ByteString name_for_display() const
  48. {
  49. if (long_name)
  50. return ByteString::formatted("--{}", long_name);
  51. return ByteString::formatted("-{:c}", short_name);
  52. }
  53. };
  54. struct Arg {
  55. char const* help_string { nullptr };
  56. char const* name { nullptr };
  57. int min_values { 0 };
  58. int max_values { 1 };
  59. Function<ErrorOr<bool>(StringView)> accept_value;
  60. };
  61. bool parse(Span<StringView> arguments, FailureBehavior failure_behavior = FailureBehavior::PrintUsageAndExit);
  62. bool parse(Main::Arguments const& arguments, FailureBehavior failure_behavior = FailureBehavior::PrintUsageAndExit)
  63. {
  64. return parse(arguments.strings, failure_behavior);
  65. }
  66. // *Without* trailing newline!
  67. void set_general_help(char const* help_string) { m_general_help = help_string; }
  68. void set_stop_on_first_non_option(bool stop_on_first_non_option) { m_stop_on_first_non_option = stop_on_first_non_option; }
  69. void print_usage(FILE*, StringView argv0);
  70. void print_usage_terminal(FILE*, StringView argv0);
  71. void print_usage_markdown(FILE*, StringView argv0);
  72. void print_version(FILE*);
  73. void add_option(Option&&);
  74. void add_ignored(char const* long_name, char short_name, OptionHideMode hide_mode = OptionHideMode::None);
  75. void add_option(bool& value, char const* help_string, char const* long_name, char short_name, OptionHideMode hide_mode = OptionHideMode::None);
  76. /// If the option is present, set the enum to have the given `new_value`.
  77. template<Enum T>
  78. void add_option(T& value, T new_value, char const* help_string, char const* long_name, char short_name, OptionHideMode hide_mode = OptionHideMode::None)
  79. {
  80. add_option({ .argument_mode = Core::ArgsParser::OptionArgumentMode::None,
  81. .help_string = help_string,
  82. .long_name = long_name,
  83. .short_name = short_name,
  84. .accept_value = [&](StringView) {
  85. value = new_value;
  86. return true;
  87. },
  88. .hide_mode = hide_mode });
  89. }
  90. template<Integral I>
  91. void add_option(I& value, char const* help_string, char const* long_name, char short_name, char const* value_name, OptionHideMode hide_mode = OptionHideMode::None)
  92. {
  93. Option option {
  94. OptionArgumentMode::Required,
  95. help_string,
  96. long_name,
  97. short_name,
  98. value_name,
  99. [&value](StringView view) -> ErrorOr<bool> {
  100. Optional<I> opt = view.to_number<I>();
  101. value = opt.value_or(0);
  102. return opt.has_value();
  103. },
  104. hide_mode,
  105. };
  106. add_option(move(option));
  107. }
  108. template<Integral I>
  109. void add_option(Optional<I>& value, char const* help_string, char const* long_name, char short_name, char const* value_name, OptionHideMode hide_mode = OptionHideMode::None)
  110. {
  111. Option option {
  112. OptionArgumentMode::Required,
  113. help_string,
  114. long_name,
  115. short_name,
  116. value_name,
  117. [&value](StringView view) -> ErrorOr<bool> {
  118. value = view.to_number<I>();
  119. return value.has_value();
  120. },
  121. hide_mode,
  122. };
  123. add_option(move(option));
  124. }
  125. template<Integral I>
  126. void add_option(Vector<I>& values, char const* help_string, char const* long_name, char short_name, char const* value_name, char separator = ',', OptionHideMode hide_mode = OptionHideMode::None)
  127. {
  128. Option option {
  129. OptionArgumentMode::Required,
  130. help_string,
  131. long_name,
  132. short_name,
  133. value_name,
  134. [&values, separator](StringView s) -> ErrorOr<bool> {
  135. bool parsed_all_values = true;
  136. s.for_each_split_view(separator, SplitBehavior::Nothing, [&](auto value) {
  137. if (auto maybe_value = value.template to_number<I>(); maybe_value.has_value())
  138. values.append(*maybe_value);
  139. else
  140. parsed_all_values = false;
  141. });
  142. return parsed_all_values;
  143. },
  144. hide_mode
  145. };
  146. add_option(move(option));
  147. }
  148. void add_option(ByteString& value, char const* help_string, char const* long_name, char short_name, char const* value_name, OptionHideMode hide_mode = OptionHideMode::None);
  149. void add_option(String& value, char const* help_string, char const* long_name, char short_name, char const* value_name, OptionHideMode hide_mode = OptionHideMode::None);
  150. void add_option(StringView& value, char const* help_string, char const* long_name, char short_name, char const* value_name, OptionHideMode hide_mode = OptionHideMode::None);
  151. void add_option(double& value, char const* help_string, char const* long_name, char short_name, char const* value_name, OptionHideMode hide_mode = OptionHideMode::None);
  152. void add_option(Optional<double>& value, char const* help_string, char const* long_name, char short_name, char const* value_name, OptionHideMode hide_mode = OptionHideMode::None);
  153. // Note: This option is being used when we expect the user to use the same option
  154. // multiple times (e.g. "program --option=example --option=anotherexample ...").
  155. void add_option(Vector<ByteString>& values, char const* help_string, char const* long_name, char short_name, char const* value_name, OptionHideMode hide_mode = OptionHideMode::None);
  156. void add_positional_argument(Arg&&);
  157. void add_positional_argument(ByteString& value, char const* help_string, char const* name, Required required = Required::Yes);
  158. void add_positional_argument(StringView& value, char const* help_string, char const* name, Required required = Required::Yes);
  159. void add_positional_argument(String& value, char const* help_string, char const* name, Required required = Required::Yes);
  160. template<Integral I>
  161. void add_positional_argument(I& value, char const* help_string, char const* name, Required required = Required::Yes)
  162. {
  163. Arg arg {
  164. help_string,
  165. name,
  166. required == Required::Yes ? 1 : 0,
  167. 1,
  168. [&value](StringView view) -> ErrorOr<bool> {
  169. Optional<I> opt = view.to_number<I>();
  170. value = opt.value_or(0);
  171. return opt.has_value();
  172. },
  173. };
  174. add_positional_argument(move(arg));
  175. }
  176. void add_positional_argument(double& value, char const* help_string, char const* name, Required required = Required::Yes);
  177. void add_positional_argument(Vector<ByteString>& value, char const* help_string, char const* name, Required required = Required::Yes);
  178. void add_positional_argument(Vector<StringView>& value, char const* help_string, char const* name, Required required = Required::Yes);
  179. void add_positional_argument(Vector<String>& value, char const* help_string, char const* name, Required required = Required::Yes);
  180. private:
  181. void autocomplete(FILE*, StringView program_name, ReadonlySpan<StringView> remaining_arguments);
  182. Vector<Option> m_options;
  183. Vector<Arg> m_positional_args;
  184. bool m_show_help { false };
  185. bool m_show_version { false };
  186. bool m_perform_autocomplete { false };
  187. char const* m_general_help { nullptr };
  188. bool m_stop_on_first_non_option { false };
  189. };
  190. }