9 #if __linux__ || __APPLE__
10 #include <sys/ioctl.h>
24 return m_unknown_options;
29 const Option* current_option =
nullptr;
31 m_unknown_options.clear();
33 auto all_options = get_all_options();
35 std::string current_flag;
36 std::vector<std::string> current_parameters;
42 for (
int i = 1; i < argc; ++i)
44 std::string arg(argv[i]);
47 bool opt_found =
false;
48 for (
auto opt : all_options)
50 if (std::find(opt->flags.begin(), opt->flags.end(), arg) != opt->flags.end())
54 if (current_option !=
nullptr)
59 "the option with flags {}is missing a required parameter!",
60 utils::join(
" ", std::vector<std::string>(current_option->flags.begin(), current_option->flags.end())));
64 args.set_option(current_flag, current_option->flags, current_parameters);
70 current_parameters = opt->parameters;
81 if (current_option !=
nullptr)
83 current_parameters[param_pos++] = arg;
87 m_unknown_options.push_back(arg);
92 if (current_option !=
nullptr && param_pos >= current_option->parameters.size())
94 args.set_option(current_flag, current_option->flags, current_parameters);
95 current_option =
nullptr;
96 current_parameters.clear();
102 if (current_option !=
nullptr)
104 if (param_pos < current_option->parameters.size() && current_option->parameters[param_pos] ==
A_REQUIRED_PARAMETER)
107 "the option with flags {}is missing at least one required parameter!",
108 utils::join(
" ", std::vector<std::string>(current_option->flags.begin(), current_option->flags.end())));
111 args.set_option(current_flag, current_option->flags, current_parameters);
119 for (
auto opt : get_all_options())
121 if (std::find(opt->flags.begin(), opt->flags.end(), flag) != opt->flags.end())
129 bool ProgramOptions::add(
const std::string& flag,
const std::string& description,
const std::initializer_list<std::string>& parameters)
131 return add({flag}, description, parameters);
134 bool ProgramOptions::add(
const std::initializer_list<std::string>& flags,
const std::string& description,
const std::initializer_list<std::string>& parameters)
136 if (flags.size() == 0)
138 log_error(
"core",
"can't add option with empty flags (description: '{}')!",
utils::trim(description));
142 if (description.empty())
144 std::string flags_string =
"";
145 for (
const auto& flag : flags)
147 flags_string += flag +
" ";
150 log_error(
"core",
"can't add option with flags ({}). The description must not be empty!",
utils::trim(flags_string));
155 for (
auto opt : get_all_options())
157 if (opt->description == description)
159 std::string flags_string =
"";
160 for (
const auto& flag : flags)
162 flags_string += flag +
" ";
165 log_error(
"core",
"can't add option with flags ({}). An option with description '{}' is already registered.",
utils::trim(flags_string), description);
170 for (
const auto& flag : flags)
174 log_error(
"core",
"the flag '{}' is already registered.", flag);
180 bool found_not_required =
false;
181 for (
const auto& param : parameters)
185 if (found_not_required)
187 log_error(
"core",
"a required parameter can't follow a non-required parameter.");
193 found_not_required =
true;
199 opt.description = description;
200 opt.parameters = parameters;
203 m_options.push_back(opt);
211 for (
auto other_opt : other_options.get_all_options())
213 for (
auto opt : get_all_options())
215 if (opt->description == other_opt->description)
217 log_error(
"core",
"an option with description '{}' is already registered.", opt->description);
221 for (
const auto& flag : other_opt->flags)
223 if (std::find(opt->flags.begin(), opt->flags.end(), flag) != opt->flags.end())
225 log_error(
"core",
"the flag '{}' is already registered.", flag);
233 m_suboptions[category].push_back(other_options);
239 for (
auto it = m_options.begin(); it != m_options.end(); ++it)
241 if (it->flags.find(flag) != it->flags.end())
243 it->flags.erase(flag);
244 if (it->flags.empty())
251 for (
auto& sub : m_suboptions)
253 for (
auto& opt : sub.second)
255 auto success = opt.remove(flag);
267 size_t line_width = 80;
269 #if __linux__ || __APPLE__
271 ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
278 line_width = w.ws_col;
282 return get_options_string_internal(get_flag_length() + 10, line_width);
287 std::vector<std::tuple<std::set<std::string>, std::string>> options;
288 for (
auto opt : get_all_options())
290 options.push_back(std::make_tuple(opt->flags, opt->description));
295 std::vector<const ProgramOptions::Option*> ProgramOptions::get_all_options()
const
297 std::vector<const ProgramOptions::Option*> options;
299 std::transform(m_options.begin(), m_options.end(), std::back_inserter(options), [](
auto& opt) { return &opt; });
301 for (
const auto& it : m_suboptions)
303 for (
const auto& opt : it.second)
305 auto insertion = opt.get_all_options();
306 options.insert(options.end(), insertion.begin(), insertion.end());
312 size_t ProgramOptions::get_flag_length()
const
315 for (
const auto& opt : m_options)
318 for (
const auto& f : opt.flags)
320 opt_len += f.size() + 2;
322 for (
size_t j = 0; j < opt.parameters.size(); ++j)
328 if (opt_len > max_len)
334 for (
const auto& it : m_suboptions)
336 for (
const auto& opt : it.second)
338 size_t subopt_len = opt.get_flag_length();
339 if (subopt_len > max_len)
341 max_len = subopt_len;
349 std::string ProgramOptions::get_options_string_internal(
size_t fill_length,
size_t max_line_width)
const
358 for (
size_t i = 0; i < m_options.size(); ++i)
360 auto& opt = m_options[i];
362 std::string flags =
" " +
utils::join(
", ", std::vector<std::string>(opt.flags.begin(), opt.flags.end()));
364 for (
const auto& flag : opt.parameters)
378 for (
size_t j = 0; j < fill_length - flags.size(); ++j)
383 std::string description = opt.description;
384 bool first_description_line =
true;
386 while (!description.empty())
388 if (!first_description_line)
390 for (
size_t j = 0; j < fill_length; ++j)
395 if (description.size() > max_line_width - fill_length)
397 auto index = description.substr(0, max_line_width - fill_length).rfind(
' ');
398 if (index == std::string::npos)
400 index = max_line_width - fill_length;
402 s += description.substr(0, index) +
"\n";
403 description = description.substr(index);
410 first_description_line =
false;
413 if (i != m_options.size() - 1)
424 for (
const auto& it : m_suboptions)
426 if (!it.first.empty())
428 s += it.first +
"\n";
430 for (
const auto& opt : it.second)
432 s += opt.get_options_string_internal(fill_length, max_line_width);
438 while (s.substr(s.length() - 2) !=
"\n\n")
std::string get_options_string() const
bool is_registered(const std::string &flag) const
bool remove(const std::string &flag)
std::vector< std::tuple< std::set< std::string >, std::string > > get_options() const
ProgramArguments parse(int argc, const char *argv[])
bool add(const std::string &flag, const std::string &description, const std::initializer_list< std::string > ¶meters={})
std::vector< std::string > get_unknown_arguments()
ProgramOptions(const std::string &name="")
static const std::string A_REQUIRED_PARAMETER
constant to specify that a parameter is required and does not have a default value.
#define log_error(channel,...)
CORE_API T trim(const T &s, const char *to_remove=" \t\r\n")
CORE_API std::string join(const std::string &joiner, const Iterator &begin, const Iterator &end, const Transform &transform)