mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-25 23:36:16 +02:00
Improve the error message for “multicommands” commands (#9510)
* Factor out the default `MultiCommand` behavior All the `MultiCommand`s had (nearly) the same behavior when called without a subcommand. Factor out this behavior into the `NixMultiCommand` class. * Display the list of available subcommands when none is specified Whenever a user runs a command that excepts a subcommand, add the list of available subcommands to the error message. * Print the multi-command lists as Markdown lists This takes more screen real estate, but is also much more readable than a comma-separated list
This commit is contained in:
parent
fbc855b3c3
commit
7fff625e39
15 changed files with 55 additions and 89 deletions
|
@ -1,4 +1,5 @@
|
|||
#include "command.hh"
|
||||
#include "markdown.hh"
|
||||
#include "store-api.hh"
|
||||
#include "local-fs-store.hh"
|
||||
#include "derivations.hh"
|
||||
|
@ -34,6 +35,19 @@ nlohmann::json NixMultiCommand::toJSON()
|
|||
return MultiCommand::toJSON();
|
||||
}
|
||||
|
||||
void NixMultiCommand::run()
|
||||
{
|
||||
if (!command) {
|
||||
std::set<std::string> subCommandTextLines;
|
||||
for (auto & [name, _] : commands)
|
||||
subCommandTextLines.insert(fmt("- `%s`", name));
|
||||
std::string markdownError = fmt("`nix %s` requires a sub-command. Available sub-commands:\n\n%s\n",
|
||||
commandName, concatStringsSep("\n", subCommandTextLines));
|
||||
throw UsageError(renderMarkdownToTerminal(markdownError));
|
||||
}
|
||||
command->second->run();
|
||||
}
|
||||
|
||||
StoreCommand::StoreCommand()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -26,9 +26,13 @@ static constexpr Command::Category catNixInstallation = 102;
|
|||
|
||||
static constexpr auto installablesCategory = "Options that change the interpretation of [installables](@docroot@/command-ref/new-cli/nix.md#installables)";
|
||||
|
||||
struct NixMultiCommand : virtual MultiCommand, virtual Command
|
||||
struct NixMultiCommand : MultiCommand, virtual Command
|
||||
{
|
||||
nlohmann::json toJSON() override;
|
||||
|
||||
using MultiCommand::MultiCommand;
|
||||
|
||||
virtual void run() override;
|
||||
};
|
||||
|
||||
// For the overloaded run methods
|
||||
|
|
|
@ -483,7 +483,7 @@ bool Args::processArgs(const Strings & args, bool finish)
|
|||
if (!anyCompleted)
|
||||
exp.handler.fun(ss);
|
||||
|
||||
/* Move the list element to the processedArgs. This is almost the same as
|
||||
/* Move the list element to the processedArgs. This is almost the same as
|
||||
`processedArgs.push_back(expectedArgs.front()); expectedArgs.pop_front()`,
|
||||
except that it will only adjust the next and prev pointers of the list
|
||||
elements, meaning the actual contents don't move in memory. This is
|
||||
|
@ -622,8 +622,9 @@ std::optional<ExperimentalFeature> Command::experimentalFeature ()
|
|||
return { Xp::NixCommand };
|
||||
}
|
||||
|
||||
MultiCommand::MultiCommand(const Commands & commands_)
|
||||
MultiCommand::MultiCommand(std::string_view commandName, const Commands & commands_)
|
||||
: commands(commands_)
|
||||
, commandName(commandName)
|
||||
{
|
||||
expectArgs({
|
||||
.label = "subcommand",
|
||||
|
|
|
@ -223,11 +223,11 @@ protected:
|
|||
std::list<ExpectedArg> expectedArgs;
|
||||
/**
|
||||
* List of processed positional argument forms.
|
||||
*
|
||||
*
|
||||
* All items removed from `expectedArgs` are added here. After all
|
||||
* arguments were processed, this list should be exactly the same as
|
||||
* `expectedArgs` was before.
|
||||
*
|
||||
*
|
||||
* This list is used to extend the lifetime of the argument forms.
|
||||
* If this is not done, some closures that reference the command
|
||||
* itself will segfault.
|
||||
|
@ -356,13 +356,16 @@ public:
|
|||
*/
|
||||
std::optional<std::pair<std::string, ref<Command>>> command;
|
||||
|
||||
MultiCommand(const Commands & commands);
|
||||
MultiCommand(std::string_view commandName, const Commands & commands);
|
||||
|
||||
bool processFlag(Strings::iterator & pos, Strings::iterator end) override;
|
||||
|
||||
bool processArgs(const Strings & args, bool finish) override;
|
||||
|
||||
nlohmann::json toJSON() override;
|
||||
|
||||
protected:
|
||||
std::string commandName = "";
|
||||
};
|
||||
|
||||
Strings argvToStrings(int argc, char * * argv);
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
|
||||
using namespace nix;
|
||||
|
||||
struct CmdConfig : virtual NixMultiCommand
|
||||
struct CmdConfig : NixMultiCommand
|
||||
{
|
||||
CmdConfig() : MultiCommand(RegisterCommand::getCommandsFor({"config"}))
|
||||
CmdConfig() : NixMultiCommand("config", RegisterCommand::getCommandsFor({"config"}))
|
||||
{ }
|
||||
|
||||
std::string description() override
|
||||
|
@ -18,13 +18,6 @@ struct CmdConfig : virtual NixMultiCommand
|
|||
}
|
||||
|
||||
Category category() override { return catUtility; }
|
||||
|
||||
void run() override
|
||||
{
|
||||
if (!command)
|
||||
throw UsageError("'nix config' requires a sub-command.");
|
||||
command->second->run();
|
||||
}
|
||||
};
|
||||
|
||||
struct CmdConfigShow : Command, MixJSON
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
using namespace nix;
|
||||
|
||||
struct CmdDerivation : virtual NixMultiCommand
|
||||
struct CmdDerivation : NixMultiCommand
|
||||
{
|
||||
CmdDerivation() : MultiCommand(RegisterCommand::getCommandsFor({"derivation"}))
|
||||
CmdDerivation() : NixMultiCommand("derivation", RegisterCommand::getCommandsFor({"derivation"}))
|
||||
{ }
|
||||
|
||||
std::string description() override
|
||||
|
@ -13,13 +13,6 @@ struct CmdDerivation : virtual NixMultiCommand
|
|||
}
|
||||
|
||||
Category category() override { return catUtility; }
|
||||
|
||||
void run() override
|
||||
{
|
||||
if (!command)
|
||||
throw UsageError("'nix derivation' requires a sub-command.");
|
||||
command->second->run();
|
||||
}
|
||||
};
|
||||
|
||||
static auto rCmdDerivation = registerCommand<CmdDerivation>("derivation");
|
||||
|
|
|
@ -1399,7 +1399,9 @@ struct CmdFlakePrefetch : FlakeCommand, MixJSON
|
|||
struct CmdFlake : NixMultiCommand
|
||||
{
|
||||
CmdFlake()
|
||||
: MultiCommand({
|
||||
: NixMultiCommand(
|
||||
"flake",
|
||||
{
|
||||
{"update", []() { return make_ref<CmdFlakeUpdate>(); }},
|
||||
{"lock", []() { return make_ref<CmdFlakeLock>(); }},
|
||||
{"metadata", []() { return make_ref<CmdFlakeMetadata>(); }},
|
||||
|
@ -1429,10 +1431,8 @@ struct CmdFlake : NixMultiCommand
|
|||
|
||||
void run() override
|
||||
{
|
||||
if (!command)
|
||||
throw UsageError("'nix flake' requires a sub-command.");
|
||||
experimentalFeatureSettings.require(Xp::Flakes);
|
||||
command->second->run();
|
||||
NixMultiCommand::run();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -130,7 +130,9 @@ struct CmdToBase : Command
|
|||
struct CmdHash : NixMultiCommand
|
||||
{
|
||||
CmdHash()
|
||||
: MultiCommand({
|
||||
: NixMultiCommand(
|
||||
"hash",
|
||||
{
|
||||
{"file", []() { return make_ref<CmdHashBase>(FileIngestionMethod::Flat);; }},
|
||||
{"path", []() { return make_ref<CmdHashBase>(FileIngestionMethod::Recursive); }},
|
||||
{"to-base16", []() { return make_ref<CmdToBase>(HashFormat::Base16); }},
|
||||
|
@ -146,13 +148,6 @@ struct CmdHash : NixMultiCommand
|
|||
}
|
||||
|
||||
Category category() override { return catUtility; }
|
||||
|
||||
void run() override
|
||||
{
|
||||
if (!command)
|
||||
throw UsageError("'nix hash' requires a sub-command.");
|
||||
command->second->run();
|
||||
}
|
||||
};
|
||||
|
||||
static auto rCmdHash = registerCommand<CmdHash>("hash");
|
||||
|
|
|
@ -67,7 +67,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs, virtual RootArgs
|
|||
bool helpRequested = false;
|
||||
bool showVersion = false;
|
||||
|
||||
NixArgs() : MultiCommand(RegisterCommand::getCommandsFor({})), MixCommonArgs("nix")
|
||||
NixArgs() : MultiCommand("", RegisterCommand::getCommandsFor({})), MixCommonArgs("nix")
|
||||
{
|
||||
categories.clear();
|
||||
categories[catHelp] = "Help commands";
|
||||
|
|
|
@ -4,7 +4,7 @@ using namespace nix;
|
|||
|
||||
struct CmdNar : NixMultiCommand
|
||||
{
|
||||
CmdNar() : MultiCommand(RegisterCommand::getCommandsFor({"nar"}))
|
||||
CmdNar() : NixMultiCommand("nar", RegisterCommand::getCommandsFor({"nar"}))
|
||||
{ }
|
||||
|
||||
std::string description() override
|
||||
|
@ -20,13 +20,6 @@ struct CmdNar : NixMultiCommand
|
|||
}
|
||||
|
||||
Category category() override { return catUtility; }
|
||||
|
||||
void run() override
|
||||
{
|
||||
if (!command)
|
||||
throw UsageError("'nix nar' requires a sub-command.");
|
||||
command->second->run();
|
||||
}
|
||||
};
|
||||
|
||||
static auto rCmdNar = registerCommand<CmdNar>("nar");
|
||||
|
|
|
@ -825,7 +825,9 @@ struct CmdProfileWipeHistory : virtual StoreCommand, MixDefaultProfile, MixDryRu
|
|||
struct CmdProfile : NixMultiCommand
|
||||
{
|
||||
CmdProfile()
|
||||
: MultiCommand({
|
||||
: NixMultiCommand(
|
||||
"profile",
|
||||
{
|
||||
{"install", []() { return make_ref<CmdProfileInstall>(); }},
|
||||
{"remove", []() { return make_ref<CmdProfileRemove>(); }},
|
||||
{"upgrade", []() { return make_ref<CmdProfileUpgrade>(); }},
|
||||
|
@ -848,13 +850,6 @@ struct CmdProfile : NixMultiCommand
|
|||
#include "profile.md"
|
||||
;
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
if (!command)
|
||||
throw UsageError("'nix profile' requires a sub-command.");
|
||||
command->second->run();
|
||||
}
|
||||
};
|
||||
|
||||
static auto rCmdProfile = registerCommand<CmdProfile>("profile");
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
|
||||
using namespace nix;
|
||||
|
||||
struct CmdRealisation : virtual NixMultiCommand
|
||||
struct CmdRealisation : NixMultiCommand
|
||||
{
|
||||
CmdRealisation() : MultiCommand(RegisterCommand::getCommandsFor({"realisation"}))
|
||||
CmdRealisation() : NixMultiCommand("realisation", RegisterCommand::getCommandsFor({"realisation"}))
|
||||
{ }
|
||||
|
||||
std::string description() override
|
||||
|
@ -16,13 +16,6 @@ struct CmdRealisation : virtual NixMultiCommand
|
|||
}
|
||||
|
||||
Category category() override { return catUtility; }
|
||||
|
||||
void run() override
|
||||
{
|
||||
if (!command)
|
||||
throw UsageError("'nix realisation' requires a sub-command.");
|
||||
command->second->run();
|
||||
}
|
||||
};
|
||||
|
||||
static auto rCmdRealisation = registerCommand<CmdRealisation>("realisation");
|
||||
|
|
|
@ -196,10 +196,12 @@ struct CmdRegistryPin : RegistryCommand, EvalCommand
|
|||
}
|
||||
};
|
||||
|
||||
struct CmdRegistry : virtual NixMultiCommand
|
||||
struct CmdRegistry : NixMultiCommand
|
||||
{
|
||||
CmdRegistry()
|
||||
: MultiCommand({
|
||||
: NixMultiCommand(
|
||||
"registry",
|
||||
{
|
||||
{"list", []() { return make_ref<CmdRegistryList>(); }},
|
||||
{"add", []() { return make_ref<CmdRegistryAdd>(); }},
|
||||
{"remove", []() { return make_ref<CmdRegistryRemove>(); }},
|
||||
|
@ -221,14 +223,6 @@ struct CmdRegistry : virtual NixMultiCommand
|
|||
}
|
||||
|
||||
Category category() override { return catSecondary; }
|
||||
|
||||
void run() override
|
||||
{
|
||||
experimentalFeatureSettings.require(Xp::Flakes);
|
||||
if (!command)
|
||||
throw UsageError("'nix registry' requires a sub-command.");
|
||||
command->second->run();
|
||||
}
|
||||
};
|
||||
|
||||
static auto rCmdRegistry = registerCommand<CmdRegistry>("registry");
|
||||
|
|
|
@ -205,7 +205,9 @@ struct CmdKeyConvertSecretToPublic : Command
|
|||
struct CmdKey : NixMultiCommand
|
||||
{
|
||||
CmdKey()
|
||||
: MultiCommand({
|
||||
: NixMultiCommand(
|
||||
"key",
|
||||
{
|
||||
{"generate-secret", []() { return make_ref<CmdKeyGenerateSecret>(); }},
|
||||
{"convert-secret-to-public", []() { return make_ref<CmdKeyConvertSecretToPublic>(); }},
|
||||
})
|
||||
|
@ -218,13 +220,6 @@ struct CmdKey : NixMultiCommand
|
|||
}
|
||||
|
||||
Category category() override { return catUtility; }
|
||||
|
||||
void run() override
|
||||
{
|
||||
if (!command)
|
||||
throw UsageError("'nix key' requires a sub-command.");
|
||||
command->second->run();
|
||||
}
|
||||
};
|
||||
|
||||
static auto rCmdKey = registerCommand<CmdKey>("key");
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
using namespace nix;
|
||||
|
||||
struct CmdStore : virtual NixMultiCommand
|
||||
struct CmdStore : NixMultiCommand
|
||||
{
|
||||
CmdStore() : MultiCommand(RegisterCommand::getCommandsFor({"store"}))
|
||||
CmdStore() : NixMultiCommand("store", RegisterCommand::getCommandsFor({"store"}))
|
||||
{ }
|
||||
|
||||
std::string description() override
|
||||
|
@ -13,13 +13,6 @@ struct CmdStore : virtual NixMultiCommand
|
|||
}
|
||||
|
||||
Category category() override { return catUtility; }
|
||||
|
||||
void run() override
|
||||
{
|
||||
if (!command)
|
||||
throw UsageError("'nix store' requires a sub-command.");
|
||||
command->second->run();
|
||||
}
|
||||
};
|
||||
|
||||
static auto rCmdStore = registerCommand<CmdStore>("store");
|
||||
|
|
Loading…
Reference in a new issue