argparse.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // diy argument parser because NIH syndrome or something | ||
| 2 | #include <type_traits> | ||
| 3 | #include "autoarray.h" | ||
| 4 | #include "libstr.h" | ||
| 5 | #include "errors.h" | ||
| 6 | |||
| 7 | // T is a lambda with 0 or 1 arguments | ||
| 8 | template<typename T> | ||
| 9 | struct opt { | ||
| 10 | 42 | constexpr opt(char shortname, const char* longname, T&& cb) | |
| 11 | 42 | : shortname(shortname), longname(longname), callback(cb) {} | |
| 12 | char shortname; | ||
| 13 | const char* longname; | ||
| 14 | T& callback; | ||
| 15 | }; | ||
| 16 | |||
| 17 | extern int pass; | ||
| 18 | |||
| 19 | template<typename... T> | ||
| 20 | 3 | autoarray<const char*> parse_opts(int argc, const char* argv[], opt<T>... opts) { | |
| 21 | 3 | autoarray<const char*> positional; | |
| 22 | 3 | int i; | |
| 23 | 13 | for(i = 1; i < argc; i++) { | |
| 24 | 7 | if(argv[i][0] != '-' || argv[i] == STR "-") { | |
| 25 | 4 | positional.append(argv[i]); | |
| 26 | 4 | continue; | |
| 27 | } | ||
| 28 | 3 | if(argv[i] == STR "--") { | |
| 29 | ✗ | i++; | |
| 30 | ✗ | break; // treat rest of the args as positional | |
| 31 | } | ||
| 32 | |||
| 33 | 3 | bool found = false; | |
| 34 | 3 | string helpname; | |
| 35 | 3 | const char* inline_arg = nullptr; | |
| 36 | 9 | auto call = [&](auto cb) { | |
| 37 | 3 | found = true; | |
| 38 | if constexpr (std::is_invocable_v<decltype(cb)>) { | ||
| 39 | ✗ | if(inline_arg) throw_err_null(pass, err_cli_no_arg, helpname.data()); | |
| 40 | ✗ | else cb(); | |
| 41 | } else { | ||
| 42 | 3 | const char* arg = inline_arg ? inline_arg : argv[++i]; | |
| 43 | 3 | if(!arg || !*arg) throw_err_null(pass, err_cli_missing_arg, helpname.data()); | |
| 44 | 3 | else cb(arg); | |
| 45 | } | ||
| 46 | }; | ||
| 47 | |||
| 48 | 3 | if(argv[i][1] == '-') { | |
| 49 | // long option | ||
| 50 | 1 | const char* option = argv[i]+2; | |
| 51 | 1 | inline_arg = strchr(option, '='); | |
| 52 | 1 | string optname{}; | |
| 53 | 1 | if(inline_arg) { | |
| 54 | ✗ | optname.assign(option, inline_arg - option); | |
| 55 | ✗ | inline_arg++; | |
| 56 | } | ||
| 57 | 1 | else optname.assign(option); | |
| 58 | |||
| 59 | 1 | helpname = STR "--" + optname; | |
| 60 | // i love this language | ||
| 61 | 29 | ([&] { | |
| 62 | 14 | if(opts.longname && optname == opts.longname) call(opts.callback); | |
| 63 | 1 | }(), ...); | |
| 64 | 1 | } else { | |
| 65 | // short option | ||
| 66 | 2 | char optname = argv[i][1]; | |
| 67 | 2 | if(argv[i][2] != 0) inline_arg = argv[i]+2; | |
| 68 | 2 | helpname = STR "-" + optname; | |
| 69 | 58 | ([&] { | |
| 70 | 28 | if(opts.shortname && optname == opts.shortname) call(opts.callback); | |
| 71 | 2 | }(), ...); | |
| 72 | } | ||
| 73 | 3 | if(!found) { | |
| 74 | ✗ | throw_err_null(pass, err_cli_unknown_opt, helpname.data()); | |
| 75 | } | ||
| 76 | } | ||
| 77 | // rest are positional | ||
| 78 | 3 | for(; i < argc; i++) positional.append(argv[i]); | |
| 79 | 3 | return positional; | |
| 80 | ✗ | } | |
| 81 |