diff --git a/src/libutil/args.cc b/src/libutil/args.cc index 986c5d1cd..bf5e7ce95 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -3,6 +3,8 @@ #include +#include + namespace nix { void Args::addFlag(Flag && flag_) @@ -205,6 +207,42 @@ bool Args::processArgs(const Strings & args, bool finish) return res; } +nlohmann::json Args::toJSON() +{ + auto flags = nlohmann::json::object(); + + for (auto & [name, flag] : longFlags) { + auto j = nlohmann::json::object(); + if (flag->shortName) + j["shortName"] = std::string(1, flag->shortName); + if (flag->description != "") + j["description"] = flag->description; + if (flag->category != "") + j["category"] = flag->category; + if (flag->handler.arity != ArityAny) + j["arity"] = flag->handler.arity; + if (!flag->labels.empty()) + j["labels"] = flag->labels; + flags[name] = std::move(j); + } + + auto args = nlohmann::json::array(); + + for (auto & arg : expectedArgs) { + auto j = nlohmann::json::object(); + j["label"] = arg.label; + j["optional"] = arg.optional; + if (arg.handler.arity != ArityAny) + j["arity"] = arg.handler.arity; + args.push_back(std::move(j)); + } + + auto res = nlohmann::json::object(); + res["flags"] = std::move(flags); + res["args"] = std::move(args); + return res; +} + static void hashTypeCompleter(size_t index, std::string_view prefix) { for (auto & type : hashTypes) @@ -313,6 +351,22 @@ void Command::printHelp(const string & programName, std::ostream & out) } } +nlohmann::json Command::toJSON() +{ + auto exs = nlohmann::json::array(); + + for (auto & example : examples()) { + auto ex = nlohmann::json::object(); + ex["description"] = example.description; + ex["command"] = example.command; + exs.push_back(std::move(ex)); + } + + auto res = Args::toJSON(); + res["examples"] = std::move(exs); + return res; +} + MultiCommand::MultiCommand(const Commands & commands) : commands(commands) { @@ -387,4 +441,20 @@ bool MultiCommand::processArgs(const Strings & args, bool finish) return Args::processArgs(args, finish); } +nlohmann::json MultiCommand::toJSON() +{ + auto cmds = nlohmann::json::object(); + + for (auto & [name, commandFun] : commands) { + auto command = commandFun(); + auto j = command->toJSON(); + j["category"] = categories[command->category()]; + cmds[name] = std::move(j); + } + + auto res = Args::toJSON(); + res["commands"] = std::move(cmds); + return res; +} + } diff --git a/src/libutil/args.hh b/src/libutil/args.hh index 97a517344..c56044f1d 100644 --- a/src/libutil/args.hh +++ b/src/libutil/args.hh @@ -4,6 +4,8 @@ #include #include +#include + #include "util.hh" namespace nix { @@ -203,6 +205,8 @@ public: }); } + virtual nlohmann::json toJSON(); + friend class MultiCommand; }; @@ -234,6 +238,8 @@ struct Command : virtual Args virtual Category category() { return catDefault; } void printHelp(const string & programName, std::ostream & out) override; + + nlohmann::json toJSON() override; }; typedef std::map()>> Commands; @@ -259,6 +265,8 @@ public: bool processFlag(Strings::iterator & pos, Strings::iterator end) override; bool processArgs(const Strings & args, bool finish) override; + + nlohmann::json toJSON() override; }; Strings argvToStrings(int argc, char * * argv); diff --git a/src/nix/command.cc b/src/nix/command.cc index da32819da..2f1fbded1 100644 --- a/src/nix/command.cc +++ b/src/nix/command.cc @@ -4,12 +4,25 @@ #include "nixexpr.hh" #include "profiles.hh" +#include + extern char * * environ __attribute__((weak)); namespace nix { Commands * RegisterCommand::commands = nullptr; +void NixMultiCommand::printHelp(const string & programName, std::ostream & out) +{ + MultiCommand::printHelp(programName, out); +} + +nlohmann::json NixMultiCommand::toJSON() +{ + // FIXME: use Command::toJSON() as well. + return MultiCommand::toJSON(); +} + StoreCommand::StoreCommand() { } diff --git a/src/nix/command.hh b/src/nix/command.hh index bc46a2028..d60c8aeb6 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -21,6 +21,13 @@ static constexpr Command::Category catSecondary = 100; static constexpr Command::Category catUtility = 101; static constexpr Command::Category catNixInstallation = 102; +struct NixMultiCommand : virtual MultiCommand, virtual Command +{ + void printHelp(const string & programName, std::ostream & out) override; + + nlohmann::json toJSON() override; +}; + /* A command that requires a Nix store. */ struct StoreCommand : virtual Command { diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 653f8db1b..ae6f4c5f9 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -933,7 +933,7 @@ struct CmdFlakeShow : FlakeCommand } }; -struct CmdFlake : virtual MultiCommand, virtual Command +struct CmdFlake : NixMultiCommand { CmdFlake() : MultiCommand({ @@ -963,11 +963,6 @@ struct CmdFlake : virtual MultiCommand, virtual Command command->second->prepare(); command->second->run(); } - - void printHelp(const string & programName, std::ostream & out) override - { - MultiCommand::printHelp(programName, out); - } }; static auto r1 = registerCommand("flake"); diff --git a/src/nix/main.cc b/src/nix/main.cc index e62657e95..fa04bebea 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -17,6 +17,8 @@ #include #include +#include + extern std::string chrootHelperName; void chrootHelper(int argc, char * * argv); @@ -172,6 +174,11 @@ void mainWrapped(int argc, char * * argv) NixArgs args; + if (argc == 2 && std::string(argv[1]) == "dump-args") { + std::cout << args.toJSON().dump() << "\n"; + return; + } + Finally printCompletions([&]() { if (completions) { diff --git a/src/nix/profile.cc b/src/nix/profile.cc index cffc9ee44..9241931e9 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -437,7 +437,7 @@ struct CmdProfileDiffClosures : virtual StoreCommand, MixDefaultProfile } }; -struct CmdProfile : virtual MultiCommand, virtual Command +struct CmdProfile : NixMultiCommand { CmdProfile() : MultiCommand({ @@ -461,11 +461,6 @@ struct CmdProfile : virtual MultiCommand, virtual Command command->second->prepare(); command->second->run(); } - - void printHelp(const string & programName, std::ostream & out) override - { - MultiCommand::printHelp(programName, out); - } }; static auto r1 = registerCommand("profile"); diff --git a/src/nix/registry.cc b/src/nix/registry.cc index ebee4545c..367268683 100644 --- a/src/nix/registry.cc +++ b/src/nix/registry.cc @@ -115,7 +115,7 @@ struct CmdRegistryPin : virtual Args, EvalCommand } }; -struct CmdRegistry : virtual MultiCommand, virtual Command +struct CmdRegistry : virtual NixMultiCommand { CmdRegistry() : MultiCommand({ @@ -141,11 +141,6 @@ struct CmdRegistry : virtual MultiCommand, virtual Command command->second->prepare(); command->second->run(); } - - void printHelp(const string & programName, std::ostream & out) override - { - MultiCommand::printHelp(programName, out); - } }; static auto r1 = registerCommand("registry");