1 module tagion.tools.OneMain;
2 
3 import std.typecons : Tuple;
4 
5 string[] getMains(alias _package)() {
6     return [__traits(allMembers, _package)];
7 }
8 
9 private __gshared string __main_name;
10 string main_name() nothrow @nogc {
11     return __main_name;
12 }
13 
14 void main_name(string name) nothrow
15 in (__main_name.length is 0)
16 do {
17     __main_name = name.idup;
18 }
19 
20 mixin template doOneMain(alltools...) {
21     import std.algorithm.searching : canFind;
22     import std.array : split;
23     import std.array : join;
24     import std.format;
25     import std.getopt;
26     import std.path : baseName;
27     import std.range : tail;
28     import std.stdio;
29     import std.traits;
30     import tagion.tools.Basic : Result;
31     import tagion.tools.revision;
32 
33     /*
34     * Strips the non package name from a module-name
35     */
36     enum tailName(string name) = name.split(".").tail(1)[0];
37     enum toolName(alias tool) = tailName!(moduleName!tool);
38 
39     /* 
40      * 
41      * Params:
42      *   tool = The name of the tool to be called
43      *   args = optarg for the tools
44      * Returns: 
45      *  The exit-code and if the tool has been executed
46      */
47     Result call_main(string tool, string[] args) {
48         main_name = tool;
49     SelectTool:
50         switch (tool) {
51             static foreach (toolmod; alltools) {
52                 {
53                     enum toolname = toolName!toolmod;
54                     static if (toolmod.alternative_name) {
55         case toolmod.alternative_name:
56                     }
57         case toolname:
58                     enum code = format(q{return Result(%s.%s(args), true);},
59                                 toolname, tailName!(toolmod.main_name));
60                     mixin(code);
61                     break SelectTool;
62                 }
63             }
64         default:
65             return Result(0, true);
66         }
67         assert(0);
68     }
69 
70     enum alternative(alias mod) = mod.alternative_name;
71     enum notNull(string name) = name !is null;
72     /* 
73     * Lists all the toolnames inclusive the alternative name as a alias-sequency
74     */
75     immutable toolnames = [
76         AliasSeq!(
77                 staticMap!(toolName, alltools),
78                 Filter!(notNull, staticMap!(alternative, alltools))
79         )
80     ];
81 
82     /* 
83      * Handles the arguments for the onetool main
84      * Params:
85      *   args = cli arguments
86      * Returns: true if the onetool should continue to execute a tool
87      */
88     Result onetool_main(string[] args) {
89         import std.file : exists, symlink, remove, thisExePath,
90             getLinkAttributes, attrIsSymlink, FileException;
91         import std.path;
92 
93         immutable program = args[0];
94         bool version_switch;
95         bool link_switch;
96         bool force_switch;
97         try {
98             auto main_args = getopt(args,
99                     std.getopt.config.caseSensitive,
100                     "version", "display the version", &version_switch,
101                     std.getopt.config.bundling,
102                     "s|link", "Creates symbolic links all the tool", &link_switch,
103                     "f|force", "Force a symbolic link to be created", &force_switch,
104             );
105 
106             if (version_switch) {
107                 revision_text.writeln;
108                 return Result(0, true);
109             }
110 
111             if (link_switch || force_switch) {
112                 foreach (toolname; toolnames) {
113                     const symlink_filename = thisExePath.dirName.buildPath(toolname);
114                     if (force_switch && symlink_filename.exists) {
115                         if (symlink_filename.getLinkAttributes.attrIsSymlink) {
116                             symlink_filename.remove;
117                         }
118                         else {
119                             stderr.writefln("Error: %s is not a symbolic link", symlink_filename);
120                             return Result(1, true);
121                         }
122                     }
123                     writefln("%s -> %s", toolname, thisExePath);
124                     symlink(thisExePath, symlink_filename);
125                 }
126                 return Result(0, true);
127             }
128 
129             if (main_args.helpWanted) {
130                 defaultGetoptPrinter(
131                         [
132                     revision_text,
133                     "Documentation: https://tagion.org/",
134                     "Usage:",
135                     format("%s <program> [<option>...]", program),
136                     format("Tool programs %-(%s, %)", toolnames),
137                     "",
138                     "<option>:",
139 
140                 ].join("\n"),
141                         main_args.options);
142                 return Result(0, true);
143             }
144         }
145         catch (GetOptException e) {
146             stderr.writefln("Error: %s", e.msg);
147             return Result(1, true);
148         }
149         catch (FileException e) {
150             stderr.writefln("Error: %s", e.msg);
151             return Result(1, true);
152         }
153         return Result.init;
154     }
155 
156     /* 
157      * 
158      * Params:
159      *   args = optarg for the one tool
160      *          args[0] or arg[1] is the name of tool 
161      * Returns: exit-code for the program
162      */
163     int do_main(string[] args) {
164         Result result;
165         auto tool = args[0].baseName;
166         if (toolnames.canFind(tool)) {
167             result = call_main(tool, args);
168         }
169         else if (args.length > 1) {
170             tool = args[1];
171             if (toolnames.canFind(tool)) {
172                 result = call_main(tool, args[1 .. $]);
173             }
174         }
175         if (!result.executed) {
176             result = onetool_main(args);
177             if (!result.executed) {
178                 stderr.writefln("Error: Invalid tool %s, available tools are\n%-(%s\n%)", tool, toolnames);
179             }
180         }
181 
182         return result.exit_code;
183     }
184 }