forked from OctaForge/libostd
argparse parsing cleanup and nicer api
This commit is contained in:
parent
ff4620d3f5
commit
d1ad559461
14
build.cc
14
build.cc
|
@ -148,31 +148,31 @@ int main(int argc, char **argv) {
|
|||
|
||||
auto &help = ap.add_help("print this message and exit");
|
||||
|
||||
ap.add_optional(0, "no-examples", ostd::arg_value::NONE)
|
||||
ap.add_optional("--no-examples", ostd::arg_value::NONE)
|
||||
.help("do not build examples")
|
||||
.action(ostd::arg_store_false(build_examples));
|
||||
|
||||
ap.add_optional(0, "no-test-suite", ostd::arg_value::NONE)
|
||||
ap.add_optional("--no-test-suite", ostd::arg_value::NONE)
|
||||
.help("do not build test suite")
|
||||
.action(ostd::arg_store_false(build_testsuite));
|
||||
|
||||
ap.add_optional(0, "no-static", ostd::arg_value::NONE)
|
||||
ap.add_optional("--no-static", ostd::arg_value::NONE)
|
||||
.help("do not build static libostd")
|
||||
.action(ostd::arg_store_false(build_static));
|
||||
|
||||
ap.add_optional(0, "shared", ostd::arg_value::NONE)
|
||||
ap.add_optional("--shared", ostd::arg_value::NONE)
|
||||
.help("build shared libostd")
|
||||
.action(ostd::arg_store_true(build_shared));
|
||||
|
||||
ap.add_optional(0, "config", ostd::arg_value::REQUIRED)
|
||||
ap.add_optional("--config", ostd::arg_value::REQUIRED)
|
||||
.help("build configuration (debug/release)")
|
||||
.action(ostd::arg_store_str(build_cfg));
|
||||
|
||||
ap.add_optional('v', "verbose", ostd::arg_value::NONE)
|
||||
ap.add_optional("-v", "--verbose", ostd::arg_value::NONE)
|
||||
.help("print entire commands")
|
||||
.action(ostd::arg_store_true(verbose));
|
||||
|
||||
ap.add_optional(0, "clean", ostd::arg_value::NONE)
|
||||
ap.add_optional("--clean", ostd::arg_value::NONE)
|
||||
.help("remove generated files and exit")
|
||||
.action(ostd::arg_store_true(clean));
|
||||
|
||||
|
|
154
ostd/argparse.hh
154
ostd/argparse.hh
|
@ -16,6 +16,7 @@
|
|||
#ifndef OSTD_ARGPARSE_HH
|
||||
#define OSTD_ARGPARSE_HH
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
@ -101,11 +102,10 @@ struct arg_optional: arg_argument {
|
|||
}
|
||||
|
||||
bool is_arg(string_range name) const {
|
||||
if (starts_with(name, "--")) {
|
||||
return name.slice(2) == ostd::citer(p_lname);
|
||||
for (auto const &nm: p_names) {
|
||||
if (name == iter(nm)) {
|
||||
return true;
|
||||
}
|
||||
if (name[0] == '-') {
|
||||
return name[1] == p_sname;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -134,17 +134,39 @@ struct arg_optional: arg_argument {
|
|||
return *this;
|
||||
}
|
||||
|
||||
arg_optional &add_name(string_range name) {
|
||||
p_names.emplace_back(name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
protected:
|
||||
arg_optional() = delete;
|
||||
|
||||
arg_optional(string_range name, arg_value req, int nargs = 1):
|
||||
arg_argument(req, nargs)
|
||||
{
|
||||
p_names.emplace_back(name);
|
||||
}
|
||||
arg_optional(string_range name, int nargs):
|
||||
arg_argument(nargs)
|
||||
{
|
||||
p_names.emplace_back(name);
|
||||
}
|
||||
|
||||
arg_optional(
|
||||
char short_name, string_range long_name,
|
||||
arg_value req, int nargs = 1
|
||||
string_range name1, string_range name2, arg_value req, int nargs = 1
|
||||
):
|
||||
arg_argument(req, nargs), p_lname(long_name), p_sname(short_name)
|
||||
{}
|
||||
arg_optional(char short_name, string_range long_name, int nargs):
|
||||
arg_argument(nargs), p_lname(long_name), p_sname(short_name)
|
||||
{}
|
||||
arg_argument(req, nargs)
|
||||
{
|
||||
p_names.emplace_back(name1);
|
||||
p_names.emplace_back(name2);
|
||||
}
|
||||
arg_optional(string_range name1, string_range name2, int nargs):
|
||||
arg_argument(nargs)
|
||||
{
|
||||
p_names.emplace_back(name1);
|
||||
p_names.emplace_back(name2);
|
||||
}
|
||||
|
||||
void set_values(iterator_range<string_range const *> vals) {
|
||||
if (p_action) {
|
||||
|
@ -156,8 +178,7 @@ protected:
|
|||
|
||||
private:
|
||||
std::function<void(iterator_range<string_range const *>)> p_action;
|
||||
std::string p_lname;
|
||||
char p_sname;
|
||||
std::vector<std::string> p_names;
|
||||
bool p_used = false;
|
||||
};
|
||||
|
||||
|
@ -300,7 +321,7 @@ struct arg_parser: arg_description_container {
|
|||
|
||||
template<typename OutputRange>
|
||||
arg_optional &add_help(OutputRange out, string_range msg) {
|
||||
auto &opt = add_optional('h', "help", arg_value::NONE);
|
||||
auto &opt = add_optional("-h", "--help", arg_value::NONE);
|
||||
opt.help(msg);
|
||||
opt.action([this, out = std::move(out)](auto) mutable {
|
||||
this->print_help(out);
|
||||
|
@ -351,15 +372,10 @@ private:
|
|||
case arg_type::OPTIONAL: {
|
||||
auto &opt = static_cast<arg_optional &>(*p);
|
||||
std::size_t nl = 0;
|
||||
if (opt.p_sname) {
|
||||
nl += 2;
|
||||
}
|
||||
if (!opt.p_lname.empty()) {
|
||||
if (opt.p_sname) {
|
||||
nl += 2;
|
||||
}
|
||||
nl += opt.p_lname.size() + 2;
|
||||
for (auto &s: opt.p_names) {
|
||||
nl += s.size();
|
||||
}
|
||||
nl += 2 * (opt.p_names.size() - 1);
|
||||
opt_namel = std::max(opt_namel, nl);
|
||||
break;
|
||||
}
|
||||
|
@ -405,17 +421,13 @@ private:
|
|||
auto &parg = static_cast<arg_optional &>(*p.get());
|
||||
format(out, " ");
|
||||
std::size_t nd = 0;
|
||||
if (parg.p_sname) {
|
||||
nd += 2;
|
||||
format(out, "-%c", parg.p_sname);
|
||||
if (!parg.p_lname.empty()) {
|
||||
for (auto &s: parg.p_names) {
|
||||
if (nd) {
|
||||
nd += 2;
|
||||
format(out, ", ");
|
||||
}
|
||||
}
|
||||
if (!parg.p_lname.empty()) {
|
||||
nd += parg.p_lname.size() + 2;
|
||||
format(out, "--%s", parg.p_lname);
|
||||
nd += s.size();
|
||||
format(out, "%s", s);
|
||||
}
|
||||
if (parg.p_helpstr.empty()) {
|
||||
out.put('\n');
|
||||
|
@ -432,83 +444,51 @@ private:
|
|||
|
||||
template<typename R>
|
||||
void parse_long(string_range arg, R &args) {
|
||||
string_range val = find(arg, '=');
|
||||
bool has_val = !val.empty(), arg_val = false;
|
||||
if (has_val) {
|
||||
arg = arg.slice(0, arg.size() - val.size());
|
||||
val.pop_front();
|
||||
}
|
||||
|
||||
auto &desc = find_arg<arg_optional>(arg);
|
||||
|
||||
args.pop_front();
|
||||
auto needs = desc.needs_value();
|
||||
if (needs == arg_value::NONE) {
|
||||
if (has_val) {
|
||||
throw arg_error{format(
|
||||
appender<std::string>(), "argument '--%s' takes no value",
|
||||
desc.p_lname
|
||||
).get()};
|
||||
}
|
||||
desc.set_values(nullptr);
|
||||
return;
|
||||
}
|
||||
if (!has_val) {
|
||||
if (args.empty()) {
|
||||
if (needs == arg_value::REQUIRED) {
|
||||
throw arg_error{format(
|
||||
appender<std::string>(), "argument '--%s' needs a value",
|
||||
desc.p_lname
|
||||
).get()};
|
||||
}
|
||||
desc.set_values(nullptr);
|
||||
return;
|
||||
}
|
||||
string_range tval = args.front();
|
||||
if ((needs != arg_value::OPTIONAL) || !find_arg_ptr(tval)) {
|
||||
val = tval;
|
||||
has_val = arg_val = true;
|
||||
}
|
||||
}
|
||||
if (has_val) {
|
||||
desc.set_values(iter({ val }));
|
||||
if (arg_val) {
|
||||
args.pop_front();
|
||||
}
|
||||
} else {
|
||||
desc.set_values(nullptr);
|
||||
std::optional<string_range> val;
|
||||
if (auto sv = find(arg, '='); !sv.empty()) {
|
||||
arg = arg.slice(0, arg.size() - sv.size());
|
||||
sv.pop_front();
|
||||
val = sv;
|
||||
}
|
||||
parse_arg(arg, std::move(val), args);
|
||||
}
|
||||
|
||||
template<typename R>
|
||||
void parse_short(string_range arg, R &args) {
|
||||
string_range val;
|
||||
bool has_val = (arg.size() > 2), arg_val = false;
|
||||
if (has_val) {
|
||||
std::optional<string_range> val;
|
||||
if (arg.size() > 2) {
|
||||
val = arg.slice(2);
|
||||
arg = arg.slice(0, 2);
|
||||
}
|
||||
parse_arg(arg, std::move(val), args);
|
||||
}
|
||||
|
||||
template<typename R>
|
||||
void parse_arg(
|
||||
string_range arg, std::optional<string_range> val, R &args
|
||||
) {
|
||||
bool arg_val = false;
|
||||
std::string argname{arg};
|
||||
auto &desc = find_arg<arg_optional>(arg);
|
||||
|
||||
args.pop_front();
|
||||
auto needs = desc.needs_value();
|
||||
if (needs == arg_value::NONE) {
|
||||
if (has_val) {
|
||||
if (val) {
|
||||
throw arg_error{format(
|
||||
appender<std::string>(), "argument '-%c' takes no value",
|
||||
desc.p_sname
|
||||
appender<std::string>(), "argument '%s' takes no value",
|
||||
argname
|
||||
).get()};
|
||||
}
|
||||
desc.set_values(nullptr);
|
||||
return;
|
||||
}
|
||||
if (!has_val) {
|
||||
if (!val) {
|
||||
if (args.empty()) {
|
||||
if (needs == arg_value::REQUIRED) {
|
||||
throw arg_error{format(
|
||||
appender<std::string>(), "argument '-%c' needs a value",
|
||||
desc.p_sname
|
||||
appender<std::string>(), "argument '%s' needs a value",
|
||||
argname
|
||||
).get()};
|
||||
}
|
||||
desc.set_values(nullptr);
|
||||
|
@ -517,11 +497,11 @@ private:
|
|||
string_range tval = args.front();
|
||||
if ((needs != arg_value::OPTIONAL) || !find_arg_ptr(tval)) {
|
||||
val = tval;
|
||||
has_val = arg_val = true;
|
||||
arg_val = true;
|
||||
}
|
||||
}
|
||||
if (has_val) {
|
||||
desc.set_values(iter({ val }));
|
||||
if (val) {
|
||||
desc.set_values(iter({ *val }));
|
||||
if (arg_val) {
|
||||
args.pop_front();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue