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_flags({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 return add_flags(flags, description, parameters);
139 bool ProgramOptions::add_flags(
const std::vector<std::string>& flags,
const std::string& description,
const std::vector<std::string>& parameters)
141 if (flags.size() == 0)
143 log_error(
"core",
"can't add option with empty flags (description: '{}')!",
utils::trim(description));
147 if (description.empty())
149 std::string flags_string =
"";
150 for (
const auto& flag : flags)
152 flags_string += flag +
" ";
155 log_error(
"core",
"can't add option with flags ({}). The description must not be empty!",
utils::trim(flags_string));
160 for (
auto opt : get_all_options())
162 if (opt->description == description)
164 std::string flags_string =
"";
165 for (
const auto& flag : flags)
167 flags_string += flag +
" ";
170 log_error(
"core",
"can't add option with flags ({}). An option with description '{}' is already registered.",
utils::trim(flags_string), description);
175 for (
const auto& flag : flags)
179 log_error(
"core",
"the flag '{}' is already registered.", flag);
185 bool found_not_required =
false;
186 for (
const auto& param : parameters)
190 if (found_not_required)
192 log_error(
"core",
"a required parameter can't follow a non-required parameter.");
198 found_not_required =
true;
204 opt.description = description;
205 opt.parameters = parameters;
206 opt.flags = std::set<std::string>(flags.begin(), flags.end());
208 m_options.push_back(opt);
216 for (
auto other_opt : other_options.get_all_options())
218 for (
auto opt : get_all_options())
220 if (opt->description == other_opt->description)
222 log_error(
"core",
"an option with description '{}' is already registered.", opt->description);
226 for (
const auto& flag : other_opt->flags)
228 if (std::find(opt->flags.begin(), opt->flags.end(), flag) != opt->flags.end())
230 log_error(
"core",
"the flag '{}' is already registered.", flag);
238 m_suboptions[category].push_back(other_options);
244 for (
auto it = m_options.begin(); it != m_options.end(); ++it)
246 if (it->flags.find(flag) != it->flags.end())
248 it->flags.erase(flag);
249 if (it->flags.empty())
256 for (
auto& sub : m_suboptions)
258 for (
auto& opt : sub.second)
260 auto success = opt.remove(flag);
272 size_t line_width = 80;
274 #if __linux__ || __APPLE__
276 ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
283 line_width = w.ws_col;
287 return get_options_string_internal(get_flag_length() + 10, line_width);
292 std::vector<std::tuple<std::set<std::string>, std::string>> options;
293 for (
auto opt : get_all_options())
295 options.push_back(std::make_tuple(opt->flags, opt->description));
300 std::vector<const ProgramOptions::Option*> ProgramOptions::get_all_options()
const
302 std::vector<const ProgramOptions::Option*> options;
304 std::transform(m_options.begin(), m_options.end(), std::back_inserter(options), [](
auto& opt) { return &opt; });
306 for (
const auto& it : m_suboptions)
308 for (
const auto& opt : it.second)
310 auto insertion = opt.get_all_options();
311 options.insert(options.end(), insertion.begin(), insertion.end());
317 size_t ProgramOptions::get_flag_length()
const
320 for (
const auto& opt : m_options)
323 for (
const auto& f : opt.flags)
325 opt_len += f.size() + 2;
327 for (
size_t j = 0; j < opt.parameters.size(); ++j)
333 if (opt_len > max_len)
339 for (
const auto& it : m_suboptions)
341 for (
const auto& opt : it.second)
343 size_t subopt_len = opt.get_flag_length();
344 if (subopt_len > max_len)
346 max_len = subopt_len;
354 std::string ProgramOptions::get_options_string_internal(
size_t fill_length,
size_t max_line_width)
const
363 for (
size_t i = 0; i < m_options.size(); ++i)
365 auto& opt = m_options[i];
367 std::string flags =
" " +
utils::join(
", ", std::vector<std::string>(opt.flags.begin(), opt.flags.end()));
369 for (
const auto& flag : opt.parameters)
383 for (
size_t j = 0; j < fill_length - flags.size(); ++j)
388 std::string description = opt.description;
389 bool first_description_line =
true;
391 while (!description.empty())
393 if (!first_description_line)
395 for (
size_t j = 0; j < fill_length; ++j)
400 if (description.size() > max_line_width - fill_length)
402 auto index = description.substr(0, max_line_width - fill_length).rfind(
' ');
403 if (
index == std::string::npos)
405 index = max_line_width - fill_length;
407 s += description.substr(0,
index) +
"\n";
408 description = description.substr(
index);
415 first_description_line =
false;
418 if (i != m_options.size() - 1)
429 for (
const auto& it : m_suboptions)
431 if (!it.first.empty())
433 s += it.first +
"\n";
435 for (
const auto& opt : it.second)
437 s += opt.get_options_string_internal(fill_length, max_line_width);
443 while (s.substr(s.length() - 2) !=
"\n\n")
void set_option(const std::string &flag, const std::string ¶meter)
std::string get_options_string() const
bool add_flags(const std::vector< std::string > &flags, const std::string &description, const std::vector< std::string > ¶meters={})
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,...)
std::string join(const std::string &joiner, const Iterator &begin, const Iterator &end, const Transform &transform)
T trim(const T &s, const char *to_remove=" \t\r\n")