Commit eb0aae66 authored by Dominik Charousset's avatar Dominik Charousset

Never throw exceptions from message::extract_opts

parent c66efff2
...@@ -338,13 +338,10 @@ private: ...@@ -338,13 +338,10 @@ private:
struct message::cli_res { struct message::cli_res {
/// Stores the remaining (unmatched) arguments. /// Stores the remaining (unmatched) arguments.
message remainder; message remainder;
/// Stores the names of all active options. /// Stores the names of all active options.
std::set<std::string> opts; std::set<std::string> opts;
/// Stores the automatically generated help text. /// Stores the automatically generated help text.
std::string helptext; std::string helptext;
/// Stores errors during option parsing. /// Stores errors during option parsing.
std::string error; std::string error;
}; };
......
...@@ -145,13 +145,16 @@ message message::extract(message_handler handler) const { ...@@ -145,13 +145,16 @@ message message::extract(message_handler handler) const {
message::cli_res message::extract_opts(std::vector<cli_arg> xs, message::cli_res message::extract_opts(std::vector<cli_arg> xs,
help_factory f) const { help_factory f) const {
std::string helpstr;
auto make_error = [&](std::string err) -> cli_res {
return {*this, std::set<std::string>{}, std::move(helpstr), std::move(err)};
};
// add default help item if user did not specify any help option // add default help item if user did not specify any help option
auto pred = [](const cli_arg& arg) { auto pred = [](const cli_arg& arg) -> bool {
std::vector<std::string> s; std::vector<std::string> s;
split(s, arg.name, is_any_of(","), token_compress_on); split(s, arg.name, is_any_of(","), token_compress_on);
if (s.empty()) { if (s.empty())
throw std::invalid_argument("invalid option name: " + arg.name); return false;
}
auto has_short_help = [](const std::string& opt) { auto has_short_help = [](const std::string& opt) {
return opt.find_first_of("h?") != std::string::npos; return opt.find_first_of("h?") != std::string::npos;
}; };
...@@ -167,12 +170,12 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs, ...@@ -167,12 +170,12 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs,
std::vector<std::string> s; std::vector<std::string> s;
split(s, cliarg.name, is_any_of(","), token_compress_on); split(s, cliarg.name, is_any_of(","), token_compress_on);
if (s.empty()) { if (s.empty()) {
throw std::invalid_argument("invalid option name: " + cliarg.name); return make_error("invalid option name: " + cliarg.name);
} }
longs["--" + s.front()] = &cliarg; longs["--" + s.front()] = &cliarg;
for (size_t i = 1; i < s.size(); ++i) { for (size_t i = 1; i < s.size(); ++i) {
if (s[i].size() != 1) { if (s[i].size() != 1) {
throw std::invalid_argument("invalid short option name: " + s[i]); return make_error("invalid short option name: " + s[i]);
} }
shorts["-" + s[i]] = &cliarg; shorts["-" + s[i]] = &cliarg;
} }
...@@ -198,6 +201,23 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs, ...@@ -198,6 +201,23 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs,
ht += " arg"; ht += " arg";
} }
} }
if (f) {
helpstr = f(xs);
} else {
auto op = [](size_t tmp, const cli_arg& arg) {
return std::max(tmp, arg.helptext.size());
};
auto name_width = std::accumulate(xs.begin(), xs.end(), size_t{0}, op);
std::ostringstream oss;
oss << std::left;
oss << "Allowed options:" << std::endl;
for (auto& ca : xs) {
oss << " ";
oss.width(static_cast<std::streamsize>(name_width));
oss << ca.helptext << " : " << ca.text << std::endl;
}
helpstr = oss.str();
}
std::set<std::string> opts; std::set<std::string> opts;
auto insert_opt_name = [&](const cli_arg* ptr) { auto insert_opt_name = [&](const cli_arg* ptr) {
auto separator = ptr->name.find(','); auto separator = ptr->name.find(',');
...@@ -207,6 +227,8 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs, ...@@ -207,6 +227,8 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs,
opts.insert(ptr->name.substr(0, separator)); opts.insert(ptr->name.substr(0, separator));
} }
}; };
// we can't `return make_error(...)` from inside `extract`, hence we
// store any occurred error in a temporary variable returned at the end
std::string error; std::string error;
auto res = extract({ auto res = extract({
[&](const std::string& arg) -> optional<skip_message_t> { [&](const std::string& arg) -> optional<skip_message_t> {
...@@ -220,7 +242,7 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs, ...@@ -220,7 +242,7 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs,
if (arg.size() > 2) { if (arg.size() > 2) {
// this short opt comes with a value (no space), e.g., -x2 // this short opt comes with a value (no space), e.g., -x2
if (! i->second->fun(arg.substr(2))) { if (! i->second->fun(arg.substr(2))) {
error = "invalid value for option " + i->second->name + ": " + arg; error = "invalid value for " + i->second->name + ": " + arg;
return none; return none;
} }
insert_opt_name(i->second); insert_opt_name(i->second);
...@@ -241,7 +263,7 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs, ...@@ -241,7 +263,7 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs,
return none; return none;
} }
if (! j->second->fun(arg.substr(eq_pos + 1))) { if (! j->second->fun(arg.substr(eq_pos + 1))) {
error = "invalid value for option " + j->second->name + ": " + arg; error = "invalid value for " + j->second->name + ": " + arg;
return none; return none;
} }
insert_opt_name(j->second); insert_opt_name(j->second);
...@@ -278,25 +300,7 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs, ...@@ -278,25 +300,7 @@ message::cli_res message::extract_opts(std::vector<cli_arg> xs,
return none; return none;
} }
}); });
std::string helptext; return {res, std::move(opts), std::move(helpstr), std::move(error)};
if (f) {
helptext = f(xs);
} else {
auto op = [](size_t tmp, const cli_arg& arg) {
return std::max(tmp, arg.helptext.size());
};
auto name_width = std::accumulate(xs.begin(), xs.end(), size_t{0}, op);
std::ostringstream oss;
oss << std::left;
oss << "Allowed options:" << std::endl;
for (auto& ca : xs) {
oss << " ";
oss.width(static_cast<std::streamsize>(name_width));
oss << ca.helptext << " : " << ca.text << std::endl;
}
helptext = oss.str();
}
return {res, std::move(opts), std::move(helptext), std::move(error)};
} }
message::cli_arg::cli_arg(std::string nstr, std::string tstr) message::cli_arg::cli_arg(std::string nstr, std::string tstr)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment