Move Command and MultiCommand to libutil

(cherry picked from commit f70434b1fb)
This commit is contained in:
Eelco Dolstra 2018-11-22 16:03:31 +01:00
parent f1b5c76c1a
commit f964f428fe
5 changed files with 116 additions and 117 deletions

View file

@ -200,4 +200,73 @@ void printTable(std::ostream & out, const Table2 & table)
} }
} }
void Command::printHelp(const string & programName, std::ostream & out)
{
Args::printHelp(programName, out);
auto exs = examples();
if (!exs.empty()) {
out << "\n";
out << "Examples:\n";
for (auto & ex : exs)
out << "\n"
<< " " << ex.description << "\n" // FIXME: wrap
<< " $ " << ex.command << "\n";
}
}
MultiCommand::MultiCommand(const std::vector<ref<Command>> & _commands)
{
for (auto & command : _commands)
commands.emplace(command->name(), command);
expectedArgs.push_back(ExpectedArg{"command", 1, true, [=](std::vector<std::string> ss) {
assert(!command);
auto i = commands.find(ss[0]);
if (i == commands.end())
throw UsageError("'%s' is not a recognised command", ss[0]);
command = i->second;
}});
}
void MultiCommand::printHelp(const string & programName, std::ostream & out)
{
if (command) {
command->printHelp(programName + " " + command->name(), out);
return;
}
out << "Usage: " << programName << " <COMMAND> <FLAGS>... <ARGS>...\n";
out << "\n";
out << "Common flags:\n";
printFlags(out);
out << "\n";
out << "Available commands:\n";
Table2 table;
for (auto & command : commands) {
auto descr = command.second->description();
if (!descr.empty())
table.push_back(std::make_pair(command.second->name(), descr));
}
printTable(out, table);
}
bool MultiCommand::processFlag(Strings::iterator & pos, Strings::iterator end)
{
if (Args::processFlag(pos, end)) return true;
if (command && command->processFlag(pos, end)) return true;
return false;
}
bool MultiCommand::processArgs(const Strings & args, bool finish)
{
if (command)
return command->processArgs(args, finish);
else
return Args::processArgs(args, finish);
}
} }

View file

@ -188,6 +188,48 @@ public:
friend class MultiCommand; friend class MultiCommand;
}; };
/* A command is an argument parser that can be executed by calling its
run() method. */
struct Command : virtual Args
{
virtual ~Command() { }
virtual std::string name() = 0;
virtual void prepare() { };
virtual void run() = 0;
struct Example
{
std::string description;
std::string command;
};
typedef std::list<Example> Examples;
virtual Examples examples() { return Examples(); }
void printHelp(const string & programName, std::ostream & out) override;
};
typedef std::map<std::string, ref<Command>> Commands;
/* An argument parser that supports multiple subcommands,
i.e. <command> <subcommand>. */
class MultiCommand : virtual Args
{
public:
Commands commands;
std::shared_ptr<Command> command;
MultiCommand(const std::vector<ref<Command>> & commands);
void printHelp(const string & programName, std::ostream & out) override;
bool processFlag(Strings::iterator & pos, Strings::iterator end) override;
bool processArgs(const Strings & args, bool finish) override;
};
Strings argvToStrings(int argc, char * * argv); Strings argvToStrings(int argc, char * * argv);
/* Helper function for rendering argument labels. */ /* Helper function for rendering argument labels. */

View file

@ -7,80 +7,6 @@ namespace nix {
std::vector<ref<Command>> * RegisterCommand::commands = 0; std::vector<ref<Command>> * RegisterCommand::commands = 0;
void Command::printHelp(const string & programName, std::ostream & out)
{
Args::printHelp(programName, out);
auto exs = examples();
if (!exs.empty()) {
out << "\n";
out << "Examples:\n";
for (auto & ex : exs)
out << "\n"
<< " " << ex.description << "\n" // FIXME: wrap
<< " $ " << ex.command << "\n";
}
}
MultiCommand::MultiCommand(const std::vector<ref<Command>> & _commands)
{
for (auto & command : _commands)
commands.emplace(command->name(), command);
expectedArgs.push_back(ExpectedArg{"command", 1, true, [=](std::vector<std::string> ss) {
assert(!command);
auto i = commands.find(ss[0]);
if (i == commands.end())
throw UsageError("'%s' is not a recognised command", ss[0]);
command = i->second;
}});
}
void MultiCommand::printHelp(const string & programName, std::ostream & out)
{
if (command) {
command->printHelp(programName + " " + command->name(), out);
return;
}
out << "Usage: " << programName << " <COMMAND> <FLAGS>... <ARGS>...\n";
out << "\n";
out << "Common flags:\n";
printFlags(out);
out << "\n";
out << "Available commands:\n";
Table2 table;
for (auto & command : commands) {
auto descr = command.second->description();
if (!descr.empty())
table.push_back(std::make_pair(command.second->name(), descr));
}
printTable(out, table);
#if 0
out << "\n";
out << "For full documentation, run 'man " << programName << "' or 'man " << programName << "-<COMMAND>'.\n";
#endif
}
bool MultiCommand::processFlag(Strings::iterator & pos, Strings::iterator end)
{
if (Args::processFlag(pos, end)) return true;
if (command && command->processFlag(pos, end)) return true;
return false;
}
bool MultiCommand::processArgs(const Strings & args, bool finish)
{
if (command)
return command->processArgs(args, finish);
else
return Args::processArgs(args, finish);
}
StoreCommand::StoreCommand() StoreCommand::StoreCommand()
{ {
} }

View file

@ -11,29 +11,6 @@ struct Value;
class Bindings; class Bindings;
class EvalState; class EvalState;
struct Pos; struct Pos;
/* A command is an argument parser that can be executed by calling its
run() method. */
struct Command : virtual Args
{
virtual ~Command() { }
virtual std::string name() = 0;
virtual void prepare() { };
virtual void run() = 0;
struct Example
{
std::string description;
std::string command;
};
typedef std::list<Example> Examples;
virtual Examples examples() { return Examples(); }
void printHelp(const string & programName, std::ostream & out) override;
};
class Store; class Store;
/* A command that requires a Nix store. */ /* A command that requires a Nix store. */
@ -171,26 +148,6 @@ struct StorePathCommand : public InstallablesCommand
void run(ref<Store> store) override; void run(ref<Store> store) override;
}; };
typedef std::map<std::string, ref<Command>> Commands;
/* An argument parser that supports multiple subcommands,
i.e. <command> <subcommand>. */
class MultiCommand : virtual Args
{
public:
Commands commands;
std::shared_ptr<Command> command;
MultiCommand(const std::vector<ref<Command>> & commands);
void printHelp(const string & programName, std::ostream & out) override;
bool processFlag(Strings::iterator & pos, Strings::iterator end) override;
bool processArgs(const Strings & args, bool finish) override;
};
/* A helper class for registering commands globally. */ /* A helper class for registering commands globally. */
struct RegisterCommand struct RegisterCommand
{ {

View file

@ -107,6 +107,11 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
void printHelp(const string & programName, std::ostream & out) void printHelp(const string & programName, std::ostream & out)
{ {
MultiCommand::printHelp(programName, out); MultiCommand::printHelp(programName, out);
#if 0
out << "\nFor full documentation, run 'man " << programName << "' or 'man " << programName << "-<COMMAND>'.\n";
#endif
std::cout << "\nNote: this program is EXPERIMENTAL and subject to change.\n"; std::cout << "\nNote: this program is EXPERIMENTAL and subject to change.\n";
} }