1 module nxt.getoptx;
2 
3 public import std.getopt;
4 
5 import std.meta;
6 import std.typecons;
7 import std.conv;
8 import std.array;
9 
10 alias GetOptRslt = Tuple!(bool, "help", Option[], "options");
11 
12 GetOptRslt getoptX(T...)(ref string[] args, T opts)
13 {
14     Option[] helpMsg = getoptHelp(opts); // extract all help strings
15 
16     bool helpPrinted = false; // state tells if called with "--help"
17 
18     getopt(args, GetoptEx!(opts), "h|help", &helpPrinted);
19 
20     GetOptRslt rslt;
21     rslt.help = helpPrinted;
22     rslt.options = helpMsg;
23 
24     return rslt;
25 }
26 
27 private template GetoptEx(TList...)
28 {
29     static if (TList.length)
30     {
31         static if (is(typeof(TList[0]) : config))
32         {
33             // it's a configuration flag, lets move on
34             alias TypeTuple!(TList[0],GetoptEx!(TList[1 .. $])) GetoptEx;
35         }
36         else
37         {
38             // it's an option string, eat help string
39             alias TypeTuple!(TList[0],TList[2],GetoptEx!(TList[3 .. $])) GetoptEx;
40         }
41     }
42     else
43     {
44         alias TList GetoptEx;
45     }
46 }
47 
48 alias Tuple!(string, "optShort", string, "optLong", string, "help") Option;
49 
50 private Option[] getoptHelp(T...)(T opts)
51 {
52     static if (opts.length)
53     {
54         static if (is(typeof(opts[0]) : config))
55         {
56             // it's a configuration flag, skip it
57             return getoptHelp(opts[1 .. $]);
58         }
59         else
60         {
61             // it's an option string
62             auto sp = split(opts[0], "|");
63             Option ret;
64             if (sp.length > 1)
65             {
66                 ret.optShort = "-" ~ (sp[0].length < sp[1].length ?
67                                       sp[0] : sp[1]);
68                 ret.optLong = "--" ~ (sp[0].length > sp[1].length ?
69                                       sp[0] : sp[1]);
70             }
71             else
72             {
73                 ret.optLong = "--" ~ sp[0];
74             }
75             ret.help = opts[1];
76 
77             return([ret]~getoptHelp(opts[3 .. $]) );
78         }
79     }
80     else
81     {
82         Option help;
83         help.optShort = "-h";
84         help.optLong = "--help";
85         help.help = "This help.";
86         return [help];
87     }
88 }
89 
90 void defaultGetoptPrinter(string text, Option[] opt)
91 {
92     import std.stdio : write, writeln, writef, writefln;
93     import std.algorithm : min, max;
94 
95     writeln(text);
96     writeln();
97     size_t ls, ll;
98     foreach (it; opt)
99     {
100         ls = max(ls, it.optShort.length);
101         ll = max(ll, it.optLong.length);
102     }
103 
104     size_t argLength = ls + ll + 2;
105     size_t rest = 79 - argLength;
106 
107     foreach (it; opt)
108     {
109         writef("%*s %*s ", ls, it.optShort, ll, it.optLong);
110         string helpMsg = it.help;
111         auto curLine = helpMsg[0 .. min($, rest)];
112         write(curLine);
113         helpMsg = helpMsg[min($, rest) .. $];
114         if (curLine.back != ' ' && !helpMsg.empty)
115         {
116             writeln('-');
117         }
118         else
119         {
120             writeln();
121         }
122 
123         while (!helpMsg.empty)
124         {
125             curLine = helpMsg[0 .. min($, rest)];
126             helpMsg = helpMsg[min($, rest) .. $];
127             writef("%*s", argLength, " ");
128             write(curLine);
129             if (curLine.back != ' ' && !helpMsg.empty)
130             {
131                 writeln('-');
132             }
133             else
134             {
135                 writeln();
136             }
137         }
138     }
139 }