1 module dud.options; 2 3 import std.array : empty; 4 import std.getopt; 5 import std.stdio; 6 import std.traits : FieldNameTuple; 7 8 @safe: 9 10 private struct OptionUDA { 11 string s; 12 string l; 13 string documentation; 14 string type; 15 } 16 17 private template buildSelector(alias thing) { 18 enum attr = __traits(getAttributes, thing); 19 static if(attr.length == 1) { 20 alias First = attr[0]; 21 alias FirstType = typeof(First); 22 static if(is(FirstType == OptionUDA)) { 23 24 static assert(First.s.length == 1 || First.s.length == 0); 25 static assert(First.l.length > 1 || First.l.length == 0); 26 27 static if(First.s.length > 0 && First.l.length > 0) { 28 enum buildSelector = First.s ~ "|" ~ First.l; 29 } else static if(First.s.length == 0 && First.l.length > 0) { 30 enum buildSelector = First.l; 31 } else static if(First.s.length > 0 && First.l.length == 0) { 32 enum buildSelector = First.s; 33 } else { 34 enum buildSelector = ""; 35 } 36 } 37 } else { 38 enum buildSelector = ""; 39 } 40 } 41 42 private template optionUDASelector(alias thing) { 43 enum attr = __traits(getAttributes, thing); 44 static if(attr.length == 1) { 45 alias First = attr[0]; 46 alias FirstType = typeof(First); 47 static if(is(FirstType == OptionUDA)) { 48 enum optionUDASelector = First; 49 } else { 50 enum optionUDASelector = OptionUDA.init; 51 } 52 } else { 53 enum optionUDASelector = OptionUDA.init; 54 } 55 } 56 57 private OptionUDA[] allOptions(T)() { 58 OptionUDA[] ret; 59 static foreach(mem; FieldNameTuple!T) {{ 60 OptionUDA tmp = optionUDASelector!(__traits(getMember, T, mem)); 61 if(tmp != OptionUDA.init) { 62 tmp.type = typeof(__traits(getMember, T, mem)).stringof; 63 ret ~= tmp; 64 } 65 }} 66 return ret; 67 } 68 69 struct OptionReturn(Option) { 70 const Option options; 71 const CommonOptions common; 72 } 73 74 void getOptions(T)(ref T t, ref string[] args) { 75 static foreach(mem; FieldNameTuple!T) { 76 getopt(args, config.passThrough, 77 buildSelector!(__traits(getMember, T, mem)), 78 &__traits(getMember, t, mem)); 79 } 80 } 81 82 void writeOption(Out, Ops)(auto ref Out output, const ref Ops option) { 83 import std.algorithm.searching : maxElement; 84 import std.algorithm.iteration : map, each; 85 import std.format : formattedWrite; 86 87 enum opsDoc = optionUDASelector!Ops; 88 writeln(opsDoc.documentation); 89 90 enum OptionUDA[] ops = allOptions!(Ops)(); 91 const size_t sMax = ops.map!(it => it.s.length).maxElement + "-".length; 92 const size_t lMax = ops.map!(it => it.l.length).maxElement + "--".length; 93 const size_t tMax = ops.map!(it => it.type.length).maxElement; 94 95 ops.each!(op => 96 formattedWrite(output, "%*s %*s %*s: %s\n", 97 sMax, (op.s.empty ? "" : "-" ~ op.s), 98 lMax, (op.l.empty ? "" : "--" ~ op.l), 99 tMax, op.type, 100 op.documentation) 101 ); 102 } 103 104 void writeOptions(Out, Ops)(auto ref Out output, 105 const ref OptionReturn!Ops options) 106 { 107 writeOption(output, options.options); 108 writeOption(output, options.common); 109 } 110 111 OptionReturn!ConvertOptions getConvertOptions(ref string[] args) { 112 CommonOptions common; 113 getOptions(common, args); 114 115 ConvertOptions conv; 116 getOptions(conv, args); 117 118 return OptionReturn!(ConvertOptions)(conv, common); 119 } 120 121 enum ConvertTargetFormat { 122 undefined, 123 sdl, 124 json 125 } 126 127 @OptionUDA("", "", ` 128 Command specific options 129 ======================== 130 131 dud convert to convert $input.(sdl,json) to $output.$format files. 132 133 If no $input filename is given the current working directory is search 134 for a file with name "dub.json", "dub.sdl", or "package.json". 135 136 If no $output is given the output file name will be derived from $format. 137 If also no $format is given an error is printed. 138 139 The option $keepInput will prevent dud convert from deleting $input. 140 The option $keepInput is false by default. 141 142 dud will not override $output if it exists. 143 To override a file with name $output pass $override to convert. 144 `) 145 struct ConvertOptions { 146 @OptionUDA("i", "input", "The filename of the dub file to convert") 147 string inputFilename; 148 149 @OptionUDA("o", "output", "The filename of the output") 150 string outputFilename; 151 152 @OptionUDA("f", "format", "The type to convert to") 153 ConvertTargetFormat outputTargetType; 154 155 @OptionUDA("k", "keepInput", "Keep the input file") 156 bool keepInput; 157 158 @OptionUDA("", "override", "Override output file if exists") 159 bool override_; 160 } 161 162 @OptionUDA("", "", ` 163 Common Options 164 ============== 165 166 General options that apply to all functions of dud`) 167 struct CommonOptions { 168 @OptionUDA("h", "help", "Display general or command specific help") 169 bool help; 170 171 @OptionUDA("v", "verbose", "Print diagnostic output") 172 bool verbose; 173 174 @OptionUDA("", "vverbose", "Print debug output") 175 bool vverbose; 176 }