about summary refs log tree commit diff
path: root/src/libutil/args.hh
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2016-02-09T20·07+0100
committerEelco Dolstra <eelco.dolstra@logicblox.com>2016-02-09T20·07+0100
commit0db9e6cd1af299f7d65e0b99019f0ccdbb1aaf3f (patch)
tree78e8c206bef0ac59146157c1b35b5c92af0bb819 /src/libutil/args.hh
parentc780c1124ec6711f09b9855c3b574b6655af6625 (diff)
New command line parsing infrastructure
Diffstat (limited to 'src/libutil/args.hh')
-rw-r--r--src/libutil/args.hh160
1 files changed, 160 insertions, 0 deletions
diff --git a/src/libutil/args.hh b/src/libutil/args.hh
new file mode 100644
index 000000000000..c61a2b02a7ac
--- /dev/null
+++ b/src/libutil/args.hh
@@ -0,0 +1,160 @@
+#pragma once
+
+#include <iostream>
+#include <map>
+#include <memory>
+
+#include "util.hh"
+
+namespace nix {
+
+MakeError(UsageError, nix::Error);
+
+enum HashType : char;
+
+class Args
+{
+public:
+
+    /* Parse the command line, throwing a UsageError if something goes
+       wrong. */
+    void parseCmdline(const Strings & cmdline);
+
+    virtual void printHelp(const string & programName, std::ostream & out);
+
+    virtual std::string description() { return ""; }
+
+protected:
+
+    /* Flags. */
+    struct Flag
+    {
+        std::string description;
+        Strings labels;
+        size_t arity;
+        std::function<void(Strings)> handler;
+    };
+
+    std::map<std::string, Flag> longFlags;
+    std::map<char, Flag> shortFlags;
+
+    virtual bool processFlag(Strings::iterator & pos, Strings::iterator end);
+
+    void printFlags(std::ostream & out);
+
+    /* Positional arguments. */
+    struct ExpectedArg
+    {
+        std::string label;
+        size_t arity; // 0 = any
+        std::function<void(Strings)> handler;
+    };
+
+    std::list<ExpectedArg> expectedArgs;
+
+    virtual bool processArgs(const Strings & args, bool finish);
+
+public:
+
+    /* Helper functions for constructing flags / positional
+       arguments. */
+
+    void mkFlag(char shortName, const std::string & longName,
+        const Strings & labels, const std::string & description,
+        size_t arity, std::function<void(Strings)> handler)
+    {
+        auto flag = Flag{description, labels, arity, handler};
+        if (shortName) shortFlags[shortName] = flag;
+        longFlags[longName] = flag;
+    }
+
+    void mkFlag(char shortName, const std::string & longName,
+        const std::string & description, std::function<void()> fun)
+    {
+        mkFlag(shortName, longName, {}, description, 0, std::bind(fun));
+    }
+
+    void mkFlag1(char shortName, const std::string & longName,
+        const std::string & label, const std::string & description,
+        std::function<void(std::string)> fun)
+    {
+        mkFlag(shortName, longName, {label}, description, 1, [=](Strings ss) {
+            fun(ss.front());
+        });
+    }
+
+    void mkFlag(char shortName, const std::string & name,
+        const std::string & description, bool * dest)
+    {
+        mkFlag(0, name, description, dest, true);
+    }
+
+    void mkFlag(char shortName, const std::string & longName,
+        const std::string & label, const std::string & description,
+        string * dest)
+    {
+        mkFlag1(shortName, longName, label, description, [=](std::string s) {
+            *dest = s;
+        });
+    }
+
+    void mkHashTypeFlag(const std::string & name, HashType * ht);
+
+    template<class T>
+    void mkFlag(char shortName, const std::string & longName, const std::string & description,
+        T * dest, const T & value)
+    {
+        mkFlag(shortName, longName, {}, description, 0, [=](Strings ss) {
+            *dest = value;
+        });
+    }
+
+    template<class I>
+    void mkIntFlag(char shortName, const std::string & longName,
+        const std::string & description, I * dest)
+    {
+        mkFlag<I>(shortName, longName, description, [=](I n) {
+            *dest = n;
+        });
+    }
+
+    template<class I>
+    void mkFlag(char shortName, const std::string & longName,
+        const std::string & description, std::function<void(I)> fun)
+    {
+        mkFlag(shortName, longName, {"N"}, description, 1, [=](Strings ss) {
+            I n;
+            if (!string2Int(ss.front(), n))
+                throw UsageError(format("flag ‘--%1%’ requires a integer argument") % longName);
+            fun(n);
+        });
+    }
+
+    /* Expect a string argument. */
+    void expectArg(const std::string & label, string * dest)
+    {
+        expectedArgs.push_back(ExpectedArg{label, 1, [=](Strings ss) {
+            *dest = ss.front();
+        }});
+    }
+
+    /* Expect 0 or more arguments. */
+    void expectArgs(const std::string & label, Strings * dest)
+    {
+        expectedArgs.push_back(ExpectedArg{label, 0, [=](Strings ss) {
+            *dest = ss;
+        }});
+    }
+};
+
+Strings argvToStrings(int argc, char * * argv);
+
+/* Helper function for rendering argument labels. */
+std::string renderLabels(const Strings & labels);
+
+/* Helper function for printing 2-column tables. */
+typedef std::vector<std::pair<std::string, std::string>> Table2;
+
+void printTable(std::ostream & out, const Table2 & table);
+
+}