Cleanup
This commit is contained in:
parent
0884f180f5
commit
4c3c638a05
|
@ -86,7 +86,7 @@ void Args::printHelp(const string & programName, std::ostream & out)
|
||||||
for (auto & exp : expectedArgs) {
|
for (auto & exp : expectedArgs) {
|
||||||
std::cout << renderLabels({exp.label});
|
std::cout << renderLabels({exp.label});
|
||||||
// FIXME: handle arity > 1
|
// FIXME: handle arity > 1
|
||||||
if (exp.arity == 0) std::cout << "...";
|
if (exp.handler.arity == ArityAny) std::cout << "...";
|
||||||
if (exp.optional) std::cout << "?";
|
if (exp.optional) std::cout << "?";
|
||||||
}
|
}
|
||||||
std::cout << "\n";
|
std::cout << "\n";
|
||||||
|
@ -127,8 +127,8 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
||||||
if (flag.handler.arity == ArityAny) break;
|
if (flag.handler.arity == ArityAny) break;
|
||||||
throw UsageError("flag '%s' requires %d argument(s)", name, flag.handler.arity);
|
throw UsageError("flag '%s' requires %d argument(s)", name, flag.handler.arity);
|
||||||
}
|
}
|
||||||
if (auto prefix = needsCompletion(*pos))
|
if (flag.completer)
|
||||||
if (flag.completer)
|
if (auto prefix = needsCompletion(*pos))
|
||||||
flag.completer(n, *prefix);
|
flag.completer(n, *prefix);
|
||||||
args.push_back(*pos++);
|
args.push_back(*pos++);
|
||||||
}
|
}
|
||||||
|
@ -179,12 +179,17 @@ bool Args::processArgs(const Strings & args, bool finish)
|
||||||
|
|
||||||
bool res = false;
|
bool res = false;
|
||||||
|
|
||||||
if ((exp.arity == 0 && finish) ||
|
if ((exp.handler.arity == ArityAny && finish) ||
|
||||||
(exp.arity > 0 && args.size() == exp.arity))
|
(exp.handler.arity != ArityAny && args.size() == exp.handler.arity))
|
||||||
{
|
{
|
||||||
std::vector<std::string> ss;
|
std::vector<std::string> ss;
|
||||||
for (auto & s : args) ss.push_back(s);
|
for (const auto &[n, s] : enumerate(args)) {
|
||||||
exp.handler(std::move(ss));
|
ss.push_back(s);
|
||||||
|
if (exp.completer)
|
||||||
|
if (auto prefix = needsCompletion(s))
|
||||||
|
exp.completer(n, *prefix);
|
||||||
|
}
|
||||||
|
exp.handler.fun(ss);
|
||||||
expectedArgs.pop_front();
|
expectedArgs.pop_front();
|
||||||
res = true;
|
res = true;
|
||||||
}
|
}
|
||||||
|
@ -214,46 +219,17 @@ Args::Flag Args::Flag::mkHashTypeFlag(std::string && longName, HashType * ht)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void completePath(size_t, std::string_view s)
|
void completePath(size_t, std::string_view prefix)
|
||||||
{
|
{
|
||||||
if (auto prefix = needsCompletion(s)) {
|
pathCompletions = true;
|
||||||
pathCompletions = true;
|
glob_t globbuf;
|
||||||
glob_t globbuf;
|
if (glob((std::string(prefix) + "*").c_str(), GLOB_NOESCAPE | GLOB_TILDE, nullptr, &globbuf) == 0) {
|
||||||
if (glob((*prefix + "*").c_str(), GLOB_NOESCAPE | GLOB_TILDE, nullptr, &globbuf) == 0) {
|
for (size_t i = 0; i < globbuf.gl_pathc; ++i)
|
||||||
for (size_t i = 0; i < globbuf.gl_pathc; ++i)
|
completions->insert(globbuf.gl_pathv[i]);
|
||||||
completions->insert(globbuf.gl_pathv[i]);
|
globfree(&globbuf);
|
||||||
globfree(&globbuf);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Args::expectPathArg(const std::string & label, string * dest, bool optional)
|
|
||||||
{
|
|
||||||
expectedArgs.push_back({
|
|
||||||
.label = label,
|
|
||||||
.arity = 1,
|
|
||||||
.optional = optional,
|
|
||||||
.handler = {[=](std::vector<std::string> ss) {
|
|
||||||
completePath(0, ss[0]);
|
|
||||||
*dest = ss[0];
|
|
||||||
}}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Args::expectPathArgs(const std::string & label, std::vector<std::string> * dest)
|
|
||||||
{
|
|
||||||
expectedArgs.push_back({
|
|
||||||
.label = label,
|
|
||||||
.arity = 0,
|
|
||||||
.optional = false,
|
|
||||||
.handler = {[=](std::vector<std::string> ss) {
|
|
||||||
for (auto & s : ss)
|
|
||||||
completePath(0, s);
|
|
||||||
*dest = std::move(ss);
|
|
||||||
}}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Strings argvToStrings(int argc, char * * argv)
|
Strings argvToStrings(int argc, char * * argv)
|
||||||
{
|
{
|
||||||
Strings args;
|
Strings args;
|
||||||
|
@ -301,18 +277,22 @@ void Command::printHelp(const string & programName, std::ostream & out)
|
||||||
MultiCommand::MultiCommand(const Commands & commands)
|
MultiCommand::MultiCommand(const Commands & commands)
|
||||||
: commands(commands)
|
: commands(commands)
|
||||||
{
|
{
|
||||||
expectedArgs.push_back(ExpectedArg{"command", 1, true, [=](std::vector<std::string> ss) {
|
expectArgs({
|
||||||
assert(!command);
|
.label = "command",
|
||||||
if (auto prefix = needsCompletion(ss[0])) {
|
.optional = true,
|
||||||
for (auto & [name, command] : commands)
|
.handler = {[=](std::string s) {
|
||||||
if (hasPrefix(name, *prefix))
|
assert(!command);
|
||||||
completions->insert(name);
|
if (auto prefix = needsCompletion(s)) {
|
||||||
}
|
for (auto & [name, command] : commands)
|
||||||
auto i = commands.find(ss[0]);
|
if (hasPrefix(name, *prefix))
|
||||||
if (i == commands.end())
|
completions->insert(name);
|
||||||
throw UsageError("'%s' is not a recognised command", ss[0]);
|
}
|
||||||
command = {ss[0], i->second()};
|
auto i = commands.find(s);
|
||||||
}});
|
if (i == commands.end())
|
||||||
|
throw UsageError("'%s' is not a recognised command", s);
|
||||||
|
command = {s, i->second()};
|
||||||
|
}}
|
||||||
|
});
|
||||||
|
|
||||||
categories[Command::catDefault] = "Available commands";
|
categories[Command::catDefault] = "Available commands";
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,55 +28,60 @@ protected:
|
||||||
|
|
||||||
static const size_t ArityAny = std::numeric_limits<size_t>::max();
|
static const size_t ArityAny = std::numeric_limits<size_t>::max();
|
||||||
|
|
||||||
|
struct Handler
|
||||||
|
{
|
||||||
|
std::function<void(std::vector<std::string>)> fun;
|
||||||
|
size_t arity;
|
||||||
|
|
||||||
|
Handler() {}
|
||||||
|
|
||||||
|
Handler(std::function<void(std::vector<std::string>)> && fun)
|
||||||
|
: fun(std::move(fun))
|
||||||
|
, arity(ArityAny)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
Handler(std::function<void()> && handler)
|
||||||
|
: fun([handler{std::move(handler)}](std::vector<std::string>) { handler(); })
|
||||||
|
, arity(0)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
Handler(std::function<void(std::string)> && handler)
|
||||||
|
: fun([handler{std::move(handler)}](std::vector<std::string> ss) {
|
||||||
|
handler(std::move(ss[0]));
|
||||||
|
})
|
||||||
|
, arity(1)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
Handler(std::function<void(std::string, std::string)> && handler)
|
||||||
|
: fun([handler{std::move(handler)}](std::vector<std::string> ss) {
|
||||||
|
handler(std::move(ss[0]), std::move(ss[1]));
|
||||||
|
})
|
||||||
|
, arity(2)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
Handler(std::vector<std::string> * dest)
|
||||||
|
: fun([=](std::vector<std::string> ss) { *dest = ss; })
|
||||||
|
, arity(ArityAny)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
Handler(T * dest)
|
||||||
|
: fun([=](std::vector<std::string> ss) { *dest = ss[0]; })
|
||||||
|
, arity(1)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
Handler(T * dest, const T & val)
|
||||||
|
: fun([=](std::vector<std::string> ss) { *dest = val; })
|
||||||
|
, arity(0)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
/* Flags. */
|
/* Flags. */
|
||||||
struct Flag
|
struct Flag
|
||||||
{
|
{
|
||||||
typedef std::shared_ptr<Flag> ptr;
|
typedef std::shared_ptr<Flag> ptr;
|
||||||
|
|
||||||
struct Handler
|
|
||||||
{
|
|
||||||
std::function<void(std::vector<std::string>)> fun;
|
|
||||||
size_t arity;
|
|
||||||
|
|
||||||
Handler() {}
|
|
||||||
|
|
||||||
Handler(std::function<void(std::vector<std::string>)> && fun)
|
|
||||||
: fun(std::move(fun))
|
|
||||||
, arity(ArityAny)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
Handler(std::function<void()> && handler)
|
|
||||||
: fun([handler{std::move(handler)}](std::vector<std::string>) { handler(); })
|
|
||||||
, arity(0)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
Handler(std::function<void(std::string)> && handler)
|
|
||||||
: fun([handler{std::move(handler)}](std::vector<std::string> ss) {
|
|
||||||
handler(std::move(ss[0]));
|
|
||||||
})
|
|
||||||
, arity(1)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
Handler(std::function<void(std::string, std::string)> && handler)
|
|
||||||
: fun([handler{std::move(handler)}](std::vector<std::string> ss) {
|
|
||||||
handler(std::move(ss[0]), std::move(ss[1]));
|
|
||||||
})
|
|
||||||
, arity(2)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
Handler(T * dest)
|
|
||||||
: fun([=](std::vector<std::string> ss) { *dest = ss[0]; })
|
|
||||||
, arity(1)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
Handler(T * dest, const T & val)
|
|
||||||
: fun([=](std::vector<std::string> ss) { *dest = val; })
|
|
||||||
, arity(0)
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string longName;
|
std::string longName;
|
||||||
char shortName = 0;
|
char shortName = 0;
|
||||||
std::string description;
|
std::string description;
|
||||||
|
@ -99,9 +104,9 @@ protected:
|
||||||
struct ExpectedArg
|
struct ExpectedArg
|
||||||
{
|
{
|
||||||
std::string label;
|
std::string label;
|
||||||
size_t arity = 0; // 0 = any
|
|
||||||
bool optional = false;
|
bool optional = false;
|
||||||
std::function<void(std::vector<std::string>)> handler;
|
Handler handler;
|
||||||
|
std::function<void(size_t, std::string_view)> completer;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::list<ExpectedArg> expectedArgs;
|
std::list<ExpectedArg> expectedArgs;
|
||||||
|
@ -175,26 +180,30 @@ public:
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void expectArgs(ExpectedArg && arg)
|
||||||
|
{
|
||||||
|
expectedArgs.emplace_back(std::move(arg));
|
||||||
|
}
|
||||||
|
|
||||||
/* Expect a string argument. */
|
/* Expect a string argument. */
|
||||||
void expectArg(const std::string & label, string * dest, bool optional = false)
|
void expectArg(const std::string & label, string * dest, bool optional = false)
|
||||||
{
|
{
|
||||||
expectedArgs.push_back(ExpectedArg{label, 1, optional, [=](std::vector<std::string> ss) {
|
expectArgs({
|
||||||
*dest = ss[0];
|
.label = label,
|
||||||
}});
|
.optional = true,
|
||||||
|
.handler = {dest}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void expectPathArg(const std::string & label, string * dest, bool optional = false);
|
|
||||||
|
|
||||||
/* Expect 0 or more arguments. */
|
/* Expect 0 or more arguments. */
|
||||||
void expectArgs(const std::string & label, std::vector<std::string> * dest)
|
void expectArgs(const std::string & label, std::vector<std::string> * dest)
|
||||||
{
|
{
|
||||||
expectedArgs.push_back(ExpectedArg{label, 0, false, [=](std::vector<std::string> ss) {
|
expectArgs({
|
||||||
*dest = std::move(ss);
|
.label = label,
|
||||||
}});
|
.handler = {dest}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void expectPathArgs(const std::string & label, std::vector<std::string> * dest);
|
|
||||||
|
|
||||||
friend class MultiCommand;
|
friend class MultiCommand;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -266,6 +275,6 @@ extern bool pathCompletions;
|
||||||
|
|
||||||
std::optional<std::string> needsCompletion(std::string_view s);
|
std::optional<std::string> needsCompletion(std::string_view s);
|
||||||
|
|
||||||
void completePath(size_t, std::string_view s);
|
void completePath(size_t, std::string_view prefix);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,11 @@ struct CmdCatStore : StoreCommand, MixCat
|
||||||
{
|
{
|
||||||
CmdCatStore()
|
CmdCatStore()
|
||||||
{
|
{
|
||||||
expectPathArg("path", &path);
|
expectArgs({
|
||||||
|
.label = "path",
|
||||||
|
.handler = {&path},
|
||||||
|
.completer = completePath
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string description() override
|
std::string description() override
|
||||||
|
@ -47,7 +51,11 @@ struct CmdCatNar : StoreCommand, MixCat
|
||||||
|
|
||||||
CmdCatNar()
|
CmdCatNar()
|
||||||
{
|
{
|
||||||
expectPathArg("nar", &narPath);
|
expectArgs({
|
||||||
|
.label = "nar",
|
||||||
|
.handler = {&narPath},
|
||||||
|
.completer = completePath
|
||||||
|
});
|
||||||
expectArg("path", &path);
|
expectArg("path", &path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,10 +73,7 @@ struct InstallablesCommand : virtual Args, SourceExprCommand
|
||||||
{
|
{
|
||||||
std::vector<std::shared_ptr<Installable>> installables;
|
std::vector<std::shared_ptr<Installable>> installables;
|
||||||
|
|
||||||
InstallablesCommand()
|
InstallablesCommand();
|
||||||
{
|
|
||||||
expectArgs("installables", &_installables);
|
|
||||||
}
|
|
||||||
|
|
||||||
void prepare() override;
|
void prepare() override;
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,11 @@ struct CmdHash : Command
|
||||||
.labels({"modulus"})
|
.labels({"modulus"})
|
||||||
.dest(&modulus);
|
.dest(&modulus);
|
||||||
#endif
|
#endif
|
||||||
expectPathArgs("paths", &paths);
|
expectArgs({
|
||||||
|
.label = "paths",
|
||||||
|
.handler = {&paths},
|
||||||
|
.completer = completePath
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string description() override
|
std::string description() override
|
||||||
|
|
|
@ -571,6 +571,14 @@ StorePathSet toDerivations(ref<Store> store,
|
||||||
return drvPaths;
|
return drvPaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InstallablesCommand::InstallablesCommand()
|
||||||
|
{
|
||||||
|
expectArgs({
|
||||||
|
.label = "installables",
|
||||||
|
.handler = {&_installables},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void InstallablesCommand::prepare()
|
void InstallablesCommand::prepare()
|
||||||
{
|
{
|
||||||
if (_installables.empty() && useDefaultInstallables())
|
if (_installables.empty() && useDefaultInstallables())
|
||||||
|
|
|
@ -85,7 +85,11 @@ struct CmdLsStore : StoreCommand, MixLs
|
||||||
{
|
{
|
||||||
CmdLsStore()
|
CmdLsStore()
|
||||||
{
|
{
|
||||||
expectPathArg("path", &path);
|
expectArgs({
|
||||||
|
.label = "path",
|
||||||
|
.handler = {&path},
|
||||||
|
.completer = completePath
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Examples examples() override
|
Examples examples() override
|
||||||
|
@ -117,7 +121,11 @@ struct CmdLsNar : Command, MixLs
|
||||||
|
|
||||||
CmdLsNar()
|
CmdLsNar()
|
||||||
{
|
{
|
||||||
expectPathArg("nar", &narPath);
|
expectArgs({
|
||||||
|
.label = "nar",
|
||||||
|
.handler = {&narPath},
|
||||||
|
.completer = completePath
|
||||||
|
});
|
||||||
expectArg("path", &path);
|
expectArg("path", &path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -767,7 +767,11 @@ struct CmdRepl : StoreCommand, MixEvalArgs
|
||||||
|
|
||||||
CmdRepl()
|
CmdRepl()
|
||||||
{
|
{
|
||||||
expectPathArgs("files", &files);
|
expectArgs({
|
||||||
|
.label = "files",
|
||||||
|
.handler = {&files},
|
||||||
|
.completer = completePath
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string description() override
|
std::string description() override
|
||||||
|
|
|
@ -149,7 +149,11 @@ struct CmdRun : InstallableCommand, RunCommon
|
||||||
|
|
||||||
CmdRun()
|
CmdRun()
|
||||||
{
|
{
|
||||||
expectPathArgs("args", &args);
|
expectArgs({
|
||||||
|
.label = "args",
|
||||||
|
.handler = {&args},
|
||||||
|
.completer = completePath
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string description() override
|
std::string description() override
|
||||||
|
|
Loading…
Reference in a new issue