basic help printing for argparse and other improvements

master
Daniel Kolesa 2017-05-14 16:24:19 +02:00
parent c19d45b21a
commit d1b69963cb
1 changed files with 121 additions and 8 deletions

View File

@ -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;
}; };
/** @} */ /** @} */