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