1 /// std.getopt.defaultPrinter alternative
2 module sily.getopt;
3 
4 import std.algorithm : max;
5 import std.getopt : Option;
6 import std.stdio : writefln;
7 
8 /** 
9 Helper function to get std.getopt.Option
10 Params:
11     _long = Option name
12     _help = Option help
13 Returns: std.getopt.Option
14 */
15 Option customOption(string _long, string _help) { return Option("", _long, _help, false); }
16 
17 private enum bool isOptionArray(T) = is(T == Option[]);
18 private enum bool isOption(T) = is(T == Option);
19 private enum bool isString(T) = is(T == string);
20 
21 /** 
22 Prints passed **Option**s and text in aligned manner on stdout, i.e:
23 ```
24 A simple cli tool
25 Usage: 
26   scli [options] [script] \
27   scli run [script]
28 Options: 
29   -h, --help   This help information. \
30   -c, --check  Check syntax without running. \
31   --quiet      Run silently (no output). 
32 Commands:
33   run          Runs script. \
34   compile      Compiles script.
35 ```
36 Can be used like:
37 ---------
38 printGetopt("Usage", "Options", help.options, "CustomOptions", customArray, customOption("opt", "-h"));
39 ---------
40 Params:
41   S = Can be either std.getopt.Option[], std.getopt.Option or string
42 */
43 void printGetopt(S...)(S args) { // string text, string usage, Option[] opt, 
44     size_t maxLen = 0;
45     bool[] isNextOpt = [];
46 
47     foreach (arg; args) {
48         alias A = typeof(arg);
49 
50         static if(isOptionArray!A) {
51             foreach (it; arg) {
52                 int sep = it.optShort == "" ? 0 : 2;
53                 maxLen = max(maxLen, it.optShort.length + it.optLong.length + sep);
54             }
55             isNextOpt ~= true;
56             continue;
57         } else
58         static if(isOption!A) {
59             int sep = arg.optShort == "" ? 0 : 2;
60             maxLen = max(maxLen, arg.optShort.length + arg.optLong.length + sep);
61             isNextOpt ~= true;
62             continue;
63         } else
64         static if(isString!A) {
65             isNextOpt ~= false;
66             continue;
67         }
68     }
69 
70     int i = 0;
71     foreach (arg; args) {
72         alias A = typeof(arg);
73         static if(isOptionArray!A) {
74             foreach (it; arg) {
75                 string opts = it.optShort ~ (it.optShort == "" ? "" : ", ") ~ it.optLong;
76                 writefln("  %-*s  %s", maxLen, opts, it.help);
77             }
78         } else 
79         static if(isOption!A) {
80             string opts = arg.optShort ~ (arg.optShort == "" ? "" : ", ") ~ arg.optLong;
81             writefln("  %-*s  %s", maxLen, opts, arg.help);
82         } else
83         static if(isString!A) {
84             bool nopt = i + 1 < isNextOpt.length ? (isNextOpt[ i + 1 ]) : (false);
85             bool popt = i - 1 > 0 ? (isNextOpt[ i - 1 ]) : (false);
86             writefln((popt ? "\n" : "") ~ arg ~ (nopt ? ":" : "\n"));
87         }
88 
89         ++i;
90     }
91 }