diff options
Diffstat (limited to 'src/libutil')
-rw-r--r-- | src/libutil/ansicolor.hh | 24 | ||||
-rw-r--r-- | src/libutil/args.cc | 101 | ||||
-rw-r--r-- | src/libutil/args.hh | 157 | ||||
-rw-r--r-- | src/libutil/config.cc | 35 | ||||
-rw-r--r-- | src/libutil/logging.cc | 10 |
5 files changed, 176 insertions, 151 deletions
diff --git a/src/libutil/ansicolor.hh b/src/libutil/ansicolor.hh index 390bd4d17..8ae07b092 100644 --- a/src/libutil/ansicolor.hh +++ b/src/libutil/ansicolor.hh @@ -1,13 +1,15 @@ -#pragma once +#pragma once + +namespace nix { + +/* Some ANSI escape sequences. */ +#define ANSI_NORMAL "\e[0m" +#define ANSI_BOLD "\e[1m" +#define ANSI_FAINT "\e[2m" +#define ANSI_ITALIC "\e[3m" +#define ANSI_RED "\e[31;1m" +#define ANSI_GREEN "\e[32;1m" +#define ANSI_YELLOW "\e[33;1m" +#define ANSI_BLUE "\e[34;1m" -namespace nix -{ - /* Some ANSI escape sequences. */ - #define ANSI_NORMAL "\e[0m" - #define ANSI_BOLD "\e[1m" - #define ANSI_FAINT "\e[2m" - #define ANSI_RED "\e[31;1m" - #define ANSI_GREEN "\e[32;1m" - #define ANSI_YELLOW "\e[33;1m" - #define ANSI_BLUE "\e[34;1m" } diff --git a/src/libutil/args.cc b/src/libutil/args.cc index ba15ea571..f829415d1 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -3,16 +3,14 @@ namespace nix { -Args::FlagMaker Args::mkFlag() -{ - return FlagMaker(*this); -} - -Args::FlagMaker::~FlagMaker() +void Args::addFlag(Flag && flag_) { + auto flag = std::make_shared<Flag>(std::move(flag_)); + if (flag->handler.arity != ArityAny) + assert(flag->handler.arity == flag->labels.size()); assert(flag->longName != ""); - args.longFlags[flag->longName] = flag; - if (flag->shortName) args.shortFlags[flag->shortName] = flag; + longFlags[flag->longName] = flag; + if (flag->shortName) shortFlags[flag->shortName] = flag; } void Args::parseCmdline(const Strings & _cmdline) @@ -61,7 +59,7 @@ void Args::parseCmdline(const Strings & _cmdline) void Args::printHelp(const string & programName, std::ostream & out) { - std::cout << "Usage: " << programName << " <FLAGS>..."; + std::cout << fmt(ANSI_BOLD "Usage:" ANSI_NORMAL " %s " ANSI_ITALIC "FLAGS..." ANSI_NORMAL, programName); for (auto & exp : expectedArgs) { std::cout << renderLabels({exp.label}); // FIXME: handle arity > 1 @@ -72,11 +70,11 @@ void Args::printHelp(const string & programName, std::ostream & out) auto s = description(); if (s != "") - std::cout << "\nSummary: " << s << ".\n"; + std::cout << "\n" ANSI_BOLD "Summary:" ANSI_NORMAL " " << s << ".\n"; if (longFlags.size()) { std::cout << "\n"; - std::cout << "Flags:\n"; + std::cout << ANSI_BOLD "Flags:" ANSI_NORMAL "\n"; printFlags(out); } } @@ -101,15 +99,14 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end) auto process = [&](const std::string & name, const Flag & flag) -> bool { ++pos; std::vector<std::string> args; - for (size_t n = 0 ; n < flag.arity; ++n) { + for (size_t n = 0 ; n < flag.handler.arity; ++n) { if (pos == end) { - if (flag.arity == ArityAny) break; - throw UsageError(format("flag '%1%' requires %2% argument(s)") - % name % flag.arity); + if (flag.handler.arity == ArityAny) break; + throw UsageError("flag '%s' requires %d argument(s)", name, flag.handler.arity); } args.push_back(*pos++); } - flag.handler(std::move(args)); + flag.handler.fun(std::move(args)); return true; }; @@ -157,17 +154,18 @@ bool Args::processArgs(const Strings & args, bool finish) return res; } -Args::FlagMaker & Args::FlagMaker::mkHashTypeFlag(HashType * ht) +Args::Flag Args::Flag::mkHashTypeFlag(std::string && longName, HashType * ht) { - arity(1); - label("type"); - description("hash algorithm ('md5', 'sha1', 'sha256', or 'sha512')"); - handler([ht](std::string s) { - *ht = parseHashType(s); - if (*ht == htUnknown) - throw UsageError("unknown hash type '%1%'", s); - }); - return *this; + return Flag { + .longName = std::move(longName), + .description = "hash algorithm ('md5', 'sha1', 'sha256', or 'sha512')", + .labels = {"hash-algo"}, + .handler = {[ht](std::string s) { + *ht = parseHashType(s); + if (*ht == htUnknown) + throw UsageError("unknown hash type '%1%'", s); + }} + }; } Strings argvToStrings(int argc, char * * argv) @@ -183,7 +181,7 @@ std::string renderLabels(const Strings & labels) std::string res; for (auto label : labels) { for (auto & c : label) c = std::toupper(c); - res += " <" + label + ">"; + res += " " ANSI_ITALIC + label + ANSI_NORMAL; } return res; } @@ -192,10 +190,10 @@ void printTable(std::ostream & out, const Table2 & table) { size_t max = 0; for (auto & row : table) - max = std::max(max, row.first.size()); + max = std::max(max, filterANSIEscapes(row.first, true).size()); for (auto & row : table) { out << " " << row.first - << std::string(max - row.first.size() + 2, ' ') + << std::string(max - filterANSIEscapes(row.first, true).size() + 2, ' ') << row.second << "\n"; } } @@ -206,8 +204,7 @@ void Command::printHelp(const string & programName, std::ostream & out) auto exs = examples(); if (!exs.empty()) { - out << "\n"; - out << "Examples:\n"; + out << "\n" ANSI_BOLD "Examples:" ANSI_NORMAL "\n"; for (auto & ex : exs) out << "\n" << " " << ex.description << "\n" // FIXME: wrap @@ -223,49 +220,55 @@ MultiCommand::MultiCommand(const Commands & commands) auto i = commands.find(ss[0]); if (i == commands.end()) throw UsageError("'%s' is not a recognised command", ss[0]); - command = i->second(); - command->_name = ss[0]; + command = {ss[0], i->second()}; }}); + + categories[Command::catDefault] = "Available commands"; } void MultiCommand::printHelp(const string & programName, std::ostream & out) { if (command) { - command->printHelp(programName + " " + command->name(), out); + command->second->printHelp(programName + " " + command->first, out); return; } - out << "Usage: " << programName << " <COMMAND> <FLAGS>... <ARGS>...\n"; + out << fmt(ANSI_BOLD "Usage:" ANSI_NORMAL " %s " ANSI_ITALIC "COMMAND FLAGS... ARGS..." ANSI_NORMAL "\n", programName); - out << "\n"; - out << "Common flags:\n"; + out << "\n" ANSI_BOLD "Common flags:" ANSI_NORMAL "\n"; printFlags(out); - out << "\n"; - out << "Available commands:\n"; + std::map<Command::Category, std::map<std::string, ref<Command>>> commandsByCategory; - Table2 table; - for (auto & i : commands) { - auto command = i.second(); - command->_name = i.first; - auto descr = command->description(); - if (!descr.empty()) - table.push_back(std::make_pair(command->name(), descr)); + for (auto & [name, commandFun] : commands) { + auto command = commandFun(); + commandsByCategory[command->category()].insert_or_assign(name, command); + } + + for (auto & [category, commands] : commandsByCategory) { + out << fmt("\n" ANSI_BOLD "%s:" ANSI_NORMAL "\n", categories[category]); + + Table2 table; + for (auto & [name, command] : commands) { + auto descr = command->description(); + if (!descr.empty()) + table.push_back(std::make_pair(name, descr)); + } + printTable(out, table); } - 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; + if (command && command->second->processFlag(pos, end)) return true; return false; } bool MultiCommand::processArgs(const Strings & args, bool finish) { if (command) - return command->processArgs(args, finish); + return command->second->processArgs(args, finish); else return Args::processArgs(args, finish); } diff --git a/src/libutil/args.hh b/src/libutil/args.hh index 967efbe1c..1932e6a8a 100644 --- a/src/libutil/args.hh +++ b/src/libutil/args.hh @@ -32,13 +32,59 @@ protected: struct Flag { 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; char shortName = 0; std::string description; - Strings labels; - size_t arity = 0; - std::function<void(std::vector<std::string>)> handler; std::string category; + Strings labels; + Handler handler; + + static Flag mkHashTypeFlag(std::string && longName, HashType * ht); }; std::map<std::string, Flag::ptr> longFlags; @@ -65,49 +111,7 @@ protected: public: - class FlagMaker - { - Args & args; - Flag::ptr flag; - friend class Args; - FlagMaker(Args & args) : args(args), flag(std::make_shared<Flag>()) { } - public: - ~FlagMaker(); - FlagMaker & longName(const std::string & s) { flag->longName = s; return *this; } - FlagMaker & shortName(char s) { flag->shortName = s; return *this; } - FlagMaker & description(const std::string & s) { flag->description = s; return *this; } - FlagMaker & label(const std::string & l) { flag->arity = 1; flag->labels = {l}; return *this; } - FlagMaker & labels(const Strings & ls) { flag->arity = ls.size(); flag->labels = ls; return *this; } - FlagMaker & arity(size_t arity) { flag->arity = arity; return *this; } - FlagMaker & handler(std::function<void(std::vector<std::string>)> handler) { flag->handler = handler; return *this; } - FlagMaker & handler(std::function<void()> handler) { flag->handler = [handler](std::vector<std::string>) { handler(); }; return *this; } - FlagMaker & handler(std::function<void(std::string)> handler) { - flag->arity = 1; - flag->handler = [handler](std::vector<std::string> ss) { handler(std::move(ss[0])); }; - return *this; - } - FlagMaker & category(const std::string & s) { flag->category = s; return *this; } - - template<class T> - FlagMaker & dest(T * dest) - { - flag->arity = 1; - flag->handler = [=](std::vector<std::string> ss) { *dest = ss[0]; }; - return *this; - } - - template<class T> - FlagMaker & set(T * dest, const T & val) - { - flag->arity = 0; - flag->handler = [=](std::vector<std::string> ss) { *dest = val; }; - return *this; - } - - FlagMaker & mkHashTypeFlag(HashType * ht); - }; - - FlagMaker mkFlag(); + void addFlag(Flag && flag); /* Helper functions for constructing flags / positional arguments. */ @@ -116,13 +120,13 @@ public: const std::string & label, const std::string & description, std::function<void(std::string)> fun) { - mkFlag() - .shortName(shortName) - .longName(longName) - .labels({label}) - .description(description) - .arity(1) - .handler([=](std::vector<std::string> ss) { fun(ss[0]); }); + addFlag({ + .longName = longName, + .shortName = shortName, + .description = description, + .labels = {label}, + .handler = {[=](std::string s) { fun(s); }} + }); } void mkFlag(char shortName, const std::string & name, @@ -135,11 +139,12 @@ public: void mkFlag(char shortName, const std::string & longName, const std::string & description, T * dest, const T & value) { - mkFlag() - .shortName(shortName) - .longName(longName) - .description(description) - .handler([=](std::vector<std::string> ss) { *dest = value; }); + addFlag({ + .longName = longName, + .shortName = shortName, + .description = description, + .handler = {[=]() { *dest = value; }} + }); } template<class I> @@ -155,18 +160,18 @@ public: void mkFlag(char shortName, const std::string & longName, const std::string & description, std::function<void(I)> fun) { - mkFlag() - .shortName(shortName) - .longName(longName) - .labels({"N"}) - .description(description) - .arity(1) - .handler([=](std::vector<std::string> ss) { + addFlag({ + .longName = longName, + .shortName = shortName, + .description = description, + .labels = {"N"}, + .handler = {[=](std::string s) { I n; - if (!string2Int(ss[0], n)) + if (!string2Int(s, n)) throw UsageError("flag '--%s' requires a integer argument", longName); fun(n); - }); + }} + }); } /* Expect a string argument. */ @@ -192,17 +197,10 @@ public: run() method. */ struct Command : virtual Args { -private: - std::string _name; - friend class MultiCommand; -public: - virtual ~Command() { } - std::string name() { return _name; } - virtual void prepare() { }; virtual void run() = 0; @@ -216,6 +214,12 @@ public: virtual Examples examples() { return Examples(); } + typedef int Category; + + static constexpr Category catDefault = 0; + + virtual Category category() { return catDefault; } + void printHelp(const string & programName, std::ostream & out) override; }; @@ -228,7 +232,10 @@ class MultiCommand : virtual Args public: Commands commands; - std::shared_ptr<Command> command; + std::map<Command::Category, std::string> categories; + + // Selected command, if any. + std::optional<std::pair<std::string, ref<Command>>> command; MultiCommand(const Commands & commands); diff --git a/src/libutil/config.cc b/src/libutil/config.cc index 7551d97d1..f03e444ec 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -177,12 +177,13 @@ void BaseSetting<T>::toJSON(JSONPlaceholder & out) template<typename T> void BaseSetting<T>::convertToArg(Args & args, const std::string & category) { - args.mkFlag() - .longName(name) - .description(description) - .arity(1) - .handler([=](std::vector<std::string> ss) { overriden = true; set(ss[0]); }) - .category(category); + args.addFlag({ + .longName = name, + .description = description, + .category = category, + .labels = {"value"}, + .handler = {[=](std::string s) { overriden = true; set(s); }}, + }); } template<> void BaseSetting<std::string>::set(const std::string & str) @@ -227,16 +228,18 @@ template<> std::string BaseSetting<bool>::to_string() const template<> void BaseSetting<bool>::convertToArg(Args & args, const std::string & category) { - args.mkFlag() - .longName(name) - .description(description) - .handler([=](std::vector<std::string> ss) { override(true); }) - .category(category); - args.mkFlag() - .longName("no-" + name) - .description(description) - .handler([=](std::vector<std::string> ss) { override(false); }) - .category(category); + args.addFlag({ + .longName = name, + .description = description, + .category = category, + .handler = {[=]() { override(true); }} + }); + args.addFlag({ + .longName = "no-" + name, + .description = description, + .category = category, + .handler = {[=]() { override(false); }} + }); } template<> void BaseSetting<Strings>::set(const std::string & str) diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc index 3cc4ef8f1..777650de5 100644 --- a/src/libutil/logging.cc +++ b/src/libutil/logging.cc @@ -63,6 +63,16 @@ public: writeToStderr(prefix + filterANSIEscapes(fs.s, !tty) + "\n"); } + void result(ActivityId act, ResultType type, const std::vector<Field> & fields) override + { + if (type == resBuildLogLine || type == resPostBuildLogLine) { + assert(0 < fields.size()); + assert(fields[0].type == Logger::Field::tString); + auto lastLine = fields[0].s; + log(lvlInfo, lastLine); + } + } + void startActivity(ActivityId act, Verbosity lvl, ActivityType type, const std::string & s, const Fields & fields, ActivityId parent) override |