basic help printing for argparse and other improvements
This commit is contained in:
parent
c19d45b21a
commit
d1b69963cb
129
ostd/argparse.hh
129
ostd/argparse.hh
|
@ -24,6 +24,7 @@
|
||||||
#include "ostd/algorithm.hh"
|
#include "ostd/algorithm.hh"
|
||||||
#include "ostd/format.hh"
|
#include "ostd/format.hh"
|
||||||
#include "ostd/string.hh"
|
#include "ostd/string.hh"
|
||||||
|
#include "ostd/io.hh"
|
||||||
|
|
||||||
namespace ostd {
|
namespace ostd {
|
||||||
|
|
||||||
|
@ -44,7 +45,9 @@ enum class arg_type {
|
||||||
enum class arg_value {
|
enum class arg_value {
|
||||||
NONE = 0,
|
NONE = 0,
|
||||||
REQUIRED,
|
REQUIRED,
|
||||||
OPTIONAL
|
OPTIONAL,
|
||||||
|
ALL,
|
||||||
|
REST
|
||||||
};
|
};
|
||||||
|
|
||||||
struct arg_parser;
|
struct arg_parser;
|
||||||
|
@ -59,7 +62,12 @@ struct arg_argument {
|
||||||
virtual bool is_arg(string_range name) const = 0;
|
virtual bool is_arg(string_range name) const = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
arg_argument() {}
|
arg_argument(arg_value req = arg_value::NONE, int nargs = 1):
|
||||||
|
p_valreq(req), p_nargs(nargs)
|
||||||
|
{}
|
||||||
|
|
||||||
|
arg_value p_valreq;
|
||||||
|
int p_nargs;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct arg_optional: arg_argument {
|
struct arg_optional: arg_argument {
|
||||||
|
@ -88,14 +96,17 @@ struct arg_optional: arg_argument {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
arg_optional(char short_name, string_range long_name, arg_value req):
|
arg_optional() = delete;
|
||||||
p_lname(long_name), p_valreq(req), p_sname(short_name)
|
arg_optional(
|
||||||
|
char short_name, string_range long_name,
|
||||||
|
arg_value req, int nargs = 1
|
||||||
|
):
|
||||||
|
arg_argument(req, nargs), p_lname(long_name), p_sname(short_name)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::optional<std::string> p_value;
|
std::optional<std::string> p_value;
|
||||||
std::string p_lname;
|
std::string p_lname;
|
||||||
arg_value p_valreq;
|
|
||||||
char p_sname;
|
char p_sname;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -109,6 +120,18 @@ struct arg_positional: arg_argument {
|
||||||
bool is_arg(string_range) const {
|
bool is_arg(string_range) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
arg_positional() = delete;
|
||||||
|
arg_positional(
|
||||||
|
string_range name, arg_value req = arg_value::REQUIRED, int nargs = 1
|
||||||
|
):
|
||||||
|
arg_argument(req, nargs),
|
||||||
|
p_name(name)
|
||||||
|
{}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string p_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct arg_category: arg_argument {
|
struct arg_category: arg_argument {
|
||||||
|
@ -121,10 +144,30 @@ struct arg_category: arg_argument {
|
||||||
bool is_arg(string_range) const {
|
bool is_arg(string_range) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
arg_category() = delete;
|
||||||
|
arg_category(
|
||||||
|
string_range name
|
||||||
|
):
|
||||||
|
p_name(name)
|
||||||
|
{}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string p_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct arg_parser {
|
struct arg_parser {
|
||||||
arg_parser() {}
|
arg_parser(string_range progname = string_range{}):
|
||||||
|
p_progname(progname)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void parse(int argc, char **argv) {
|
||||||
|
if (p_progname.empty()) {
|
||||||
|
p_progname = argv[0];
|
||||||
|
}
|
||||||
|
parse(iter(&argv[1], &argv[argc]));
|
||||||
|
}
|
||||||
|
|
||||||
template<typename InputRange>
|
template<typename InputRange>
|
||||||
void parse(InputRange args) {
|
void parse(InputRange args) {
|
||||||
|
@ -171,7 +214,76 @@ struct arg_parser {
|
||||||
return static_cast<arg_category &>(*p_opts.emplace_back(p));
|
return static_cast<arg_category &>(*p_opts.emplace_back(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename OutputRange>
|
||||||
|
OutputRange &&print_help(OutputRange &&range) {
|
||||||
|
print_usage_impl(range);
|
||||||
|
print_help_impl(range);
|
||||||
|
return std::forward<OutputRange>(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_help() {
|
||||||
|
print_help(cout.iter());
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
template<typename OR>
|
||||||
|
void print_usage_impl(OR &out) {
|
||||||
|
string_range progname = p_progname;
|
||||||
|
if (progname.empty()) {
|
||||||
|
progname = "program";
|
||||||
|
}
|
||||||
|
format(out, "usage: %s [opts] [args]\n", progname);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename OR>
|
||||||
|
void print_help_impl(OR &out) {
|
||||||
|
bool has_opt = false, has_pos = false;
|
||||||
|
for (auto &p: p_opts) {
|
||||||
|
switch (p->type()) {
|
||||||
|
case arg_type::OPTIONAL:
|
||||||
|
has_opt = true;
|
||||||
|
break;
|
||||||
|
case arg_type::POSITIONAL:
|
||||||
|
has_pos = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_pos) {
|
||||||
|
format(out, "\npositional arguments:\n");
|
||||||
|
for (auto &p: p_opts) {
|
||||||
|
if (p->type() != arg_type::POSITIONAL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto &parg = static_cast<arg_positional &>(*p.get());
|
||||||
|
format(out, " %s\n", parg.p_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_opt) {
|
||||||
|
format(out, "\noptional arguments:\n");
|
||||||
|
for (auto &p: p_opts) {
|
||||||
|
if (p->type() != arg_type::OPTIONAL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto &parg = static_cast<arg_optional &>(*p.get());
|
||||||
|
format(out, " ");
|
||||||
|
if (parg.p_sname) {
|
||||||
|
format(out, "-%c", parg.p_sname);
|
||||||
|
if (!parg.p_lname.empty()) {
|
||||||
|
format(out, ", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!parg.p_lname.empty()) {
|
||||||
|
format(out, "--%s", parg.p_lname);
|
||||||
|
}
|
||||||
|
out.put('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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, '=');
|
string_range val = find(arg, '=');
|
||||||
|
@ -188,7 +300,7 @@ private:
|
||||||
if (needs == arg_value::NONE) {
|
if (needs == arg_value::NONE) {
|
||||||
if (has_val) {
|
if (has_val) {
|
||||||
throw arg_error{format(
|
throw arg_error{format(
|
||||||
appender<std::string>(), "argument '%s' takes no value",
|
appender<std::string>(), "argument '--%s' takes no value",
|
||||||
desc.p_lname
|
desc.p_lname
|
||||||
).get()};
|
).get()};
|
||||||
}
|
}
|
||||||
|
@ -198,7 +310,7 @@ private:
|
||||||
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 '%s' needs a value",
|
appender<std::string>(), "argument '--%s' needs a value",
|
||||||
desc.p_lname
|
desc.p_lname
|
||||||
).get()};
|
).get()};
|
||||||
}
|
}
|
||||||
|
@ -287,6 +399,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::unique_ptr<arg_argument>> p_opts;
|
std::vector<std::unique_ptr<arg_argument>> p_opts;
|
||||||
|
std::string p_progname;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
Loading…
Reference in a new issue