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 }