cli: faithful argparse port (single flag table, HelpFormatter, textwrap, abbreviations, usage on error)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,62 @@
|
||||
#include "../src/cli/cli.h"
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "../src/cli/arg_parser.h"
|
||||
#include "../src/cli/help_formatter.h"
|
||||
#include "../src/cli/text_wrap.h"
|
||||
using namespace detz::cli;
|
||||
static Options opts(std::vector<const char*> a) {
|
||||
std::vector<char*> v{const_cast<char*>("detz")};
|
||||
for (auto x : a) v.push_back(const_cast<char*>(x));
|
||||
return Parser().parseOpts(static_cast<int>(v.size()), v.data());
|
||||
}
|
||||
static bool errHas(const Options& o, const std::string& m) { return o.error.find("tai: error: " + m) != std::string::npos && o.error.rfind("usage: tai", 0) == 0; }
|
||||
static void argparseCases() {
|
||||
assert(opts({"--vers"}).version);
|
||||
assert(errHas(opts({"--ins"}), "ambiguous option: --ins could match --install, --install-telegram"));
|
||||
assert(errHas(opts({"--profile"}), "argument --profile: expected one argument"));
|
||||
assert(errHas(opts({"--yes=1"}), "argument --yes: ignored explicit argument '1'"));
|
||||
assert(errHas(opts({"--cap-max-turns", "abc"}), "argument --cap-max-turns: invalid int value: 'abc'"));
|
||||
assert(errHas(opts({"--debug", "zzz"}), "argument --debug: invalid choice: 'zzz' (choose from file, stdout, stderr)"));
|
||||
assert(errHas(opts({"--bogus"}), "unrecognized arguments: --bogus"));
|
||||
assert(opts({"-h", "--bogus"}).help && opts({"-h", "--bogus"}).error.empty());
|
||||
assert(errHas(opts({"--cap-permission-mode", "zzz", "-h"}), "argument --cap-permission-mode: invalid choice"));
|
||||
assert(opts({"--debug"}).debug.value() == "file");
|
||||
assert(opts({"--debug=stderr"}).debug.value() == "stderr");
|
||||
assert(opts({"--profile=x", "hello", "world"}).prompt == (std::vector<std::string>{"hello", "world"}));
|
||||
assert(opts({"--cap", "--yes", "x"}).cap.value() == (std::vector<std::string>{"--yes", "x"}));
|
||||
assert(!opts({"--cap", "--yes"}).yes);
|
||||
assert(opts({"--mplex"}).mplex.has_value() && opts({"--mplex"}).mplex->empty());
|
||||
assert(opts({"--", "--yes"}).prompt == (std::vector<std::string>{"--yes"}));
|
||||
assert(opts({"--cap-max-turns", "-1"}).cap_max_turns.value() == -1);
|
||||
assert(opts({"--mcp-port", " 1_0 "}).mcp_port.value() == 10);
|
||||
assert(errHas(opts({"--model", "x"}), "unrecognized arguments: --model x") || errHas(opts({"--model", "x"}), "unrecognized arguments: --model"));
|
||||
long long n = 0;
|
||||
assert(ArgParser::pyInt("\xd9\xa3", n) && n == 3);
|
||||
assert(!ArgParser::pyInt("3x", n) && !ArgParser::pyInt("_3", n) && !ArgParser::pyInt("", n));
|
||||
assert(ArgParser::pyRepr("it's") == "\"it's\"" && ArgParser::pyRepr("a'\"") == "'a\\'\"'");
|
||||
}
|
||||
static void helpCases() {
|
||||
// Usage, help and parser share one table: every option string of the table is in --help.
|
||||
setenv("COLUMNS", "80", 1);
|
||||
const std::string help = Parser::helpText();
|
||||
for (const auto& f : FlagTable::all())
|
||||
for (const auto& n : f.names) assert(help.find(n) != std::string::npos);
|
||||
assert(help.rfind("usage: tai [-h] [--profile PROFILE]", 0) == 0);
|
||||
// width 80: the wrapped help column starts at 24
|
||||
assert(help.find(" --debug [SINK] extensive debug logging to <home>/debug.log (file),\n") != std::string::npos);
|
||||
setenv("COLUMNS", "40", 1);
|
||||
assert(HelpFormatter(HelpFormatter::terminalColumns()).formatUsage().find("\n [--yes] [--yolo] [--auto]\n") != std::string::npos);
|
||||
// textwrap: breaks after hyphens inside words, breaks over-long words
|
||||
assert(TextWrap::wrap("per-record context", 10) == (std::vector<std::string>{"per-record", "context"}));
|
||||
assert(TextWrap::wrap("abcdefghij", 4) == (std::vector<std::string>{"abcd", "efgh", "ij"}));
|
||||
unsetenv("COLUMNS");
|
||||
}
|
||||
int main() {
|
||||
argparseCases();
|
||||
helpCases();
|
||||
detz::cli::Parser p;
|
||||
// golden --help contains every flag
|
||||
std::string h = detz::cli::Parser::helpText();
|
||||
|
||||
Reference in New Issue
Block a user