1 /++ 2 $(H2 The extenstion module for Cmdline) 3 4 This module give a new programming way to build the command line prgram. 5 And it is publicly imported into main modlue `cmdline`. 6 7 Using the following mixin-macro which are used for being embedded in a struct whose name 8 is end with `Result` and fields must be among `ArgVal`, `OptVal` and the pointer to other 9 structs that are satisfied with and the two function `coustruct` and `parse`, you can 10 build and parse the command line argument and option parameters. And Also, We can get the 11 parsed value from command line more easily. 12 13 Authors: 笑愚(xiaoyu) 14 +/ 15 module cmdline.ext; 16 17 import std.traits; 18 import std.meta; 19 import std.range : ElementType; 20 21 import cmdline.option; 22 import cmdline.argument; 23 import cmdline.command; 24 25 public: 26 27 enum __CMDLINE_EXT_isInnerArgValField__(T) = hasMember!(T, "ARGVAL_FLAG_"); 28 enum __CMDLINE_EXT_isInnerOptValField__(T) = hasMember!(T, "OPTVAL_FLAG_"); 29 enum __CMDLINE_EXT_isInnerValField__(T) = __CMDLINE_EXT_isInnerArgValField__!T 30 || __CMDLINE_EXT_isInnerOptValField__!T; 31 enum __CMDLINE_EXT_isInnerSubField__(T) = isPointer!T && isOutputResult!(PointerTarget!T); 32 enum __CMDLINE_EXT_isInnerValFieldOrResult__(T) = __CMDLINE_EXT_isInnerValField__!T 33 || __CMDLINE_EXT_isInnerSubField__!T; 34 35 /// check whether a type is the struct that can be the 36 /// container to store the parsed value from command line. 37 enum isOutputResult(T) = is(T == struct) 38 && T.stringof.length > 6 && (T.stringof)[$ - 6 .. $] == "Result" 39 && FieldTypeTuple!T.length > 0 40 && anySatisfy!(__CMDLINE_EXT_isInnerValFieldOrResult__, FieldTypeTuple!T); 41 42 /// Add description to a registered argument. 43 /// Params: 44 /// field = the argument in the type of `ArgVal` 45 /// desc = the description 46 mixin template DESC_ARG(alias field, string desc) { 47 static assert(__CMDLINE_EXT_isInnerArgValField__!(typeof(field))); 48 debug pragma(msg, "enum DESC_ARG_" ~ field.stringof ~ "_ = \"" ~ desc ~ "\";"); 49 mixin("enum DESC_ARG_" ~ field.stringof ~ "_ = \"" ~ desc ~ "\";"); 50 } 51 52 /// Add description to a registered option. 53 /// Params: 54 /// field = the option in the type of `OptVal` 55 /// desc = the description 56 mixin template DESC_OPT(alias field, string desc) { 57 static assert(__CMDLINE_EXT_isInnerOptValField__!(typeof(field))); 58 debug pragma(msg, "enum DESC_OPT_" ~ field.stringof ~ "_ = \"" ~ desc ~ "\";"); 59 mixin("enum DESC_OPT_" ~ field.stringof ~ "_ = \"" ~ desc ~ "\";"); 60 } 61 62 /// Add description to a registered option or argument. 63 /// Params: 64 /// field = the option or argument in the type of `OptVal` or `ArgVal` 65 /// desc = the description 66 mixin template DESC(alias field, string desc) { 67 static if (__CMDLINE_EXT_isInnerArgValField__!(typeof(field))) { 68 mixin DESC_ARG!(field, desc); 69 } 70 else static if (__CMDLINE_EXT_isInnerOptValField__!(typeof(field))) { 71 mixin DESC_OPT!(field, desc); 72 } 73 else { 74 static assert(0); 75 } 76 } 77 78 /// set the default value to a registered option or argument 79 /// Params: 80 /// field = the option or argument in the type of `OptVal` or `ArgVal` 81 /// val = the default value 82 mixin template DEFAULT(alias field, alias val) { 83 alias ___FType___CMDLINE = typeof(field); 84 static assert(!is(___FType___CMDLINE.InnerType == bool)); 85 static if (__CMDLINE_EXT_isInnerOptValField__!___FType___CMDLINE 86 && ___FType___CMDLINE.VARIADIC 87 && is(ElementType!(___FType___CMDLINE.InnerType) == void)) { 88 static assert(is(typeof(val) == ___FType___CMDLINE.InnerType[])); 89 } 90 else { 91 static assert(is(typeof(val) : ___FType___CMDLINE.InnerType)); 92 } 93 debug pragma(msg, "static " ~ typeof(val) 94 .stringof ~ " DEFAULT_" ~ field.stringof ~ "_ = " ~ val.stringof ~ ";"); 95 mixin("static " ~ typeof(val).stringof ~ " DEFAULT_" ~ field.stringof ~ "_ = " ~ val.stringof ~ ";"); 96 } 97 98 /// set the preset value to a registered option 99 /// Params: 100 /// field = the option in the type of `OptVal` 101 /// val = the preset value 102 mixin template PRESET(alias field, alias val) { 103 alias ___FType___CMDLINE = typeof(field); 104 static assert(__CMDLINE_EXT_isInnerOptValField__!___FType___CMDLINE && !is( 105 ___FType___CMDLINE.InnerType == bool) && ___FType___CMDLINE 106 .OPTIONAL); 107 static if (___FType___CMDLINE.VARIADIC && is(ElementType!(___FType___CMDLINE.InnerType) == void)) { 108 static assert(is(typeof(val) == ___FType___CMDLINE.InnerType[])); 109 } 110 else { 111 static assert(is(typeof(val) : ___FType___CMDLINE.InnerType)); 112 } 113 debug pragma(msg, "static " ~ typeof(val) 114 .stringof ~ " PRESET_" ~ field.stringof ~ "_ = " ~ val.stringof ~ ";"); 115 mixin("static " ~ typeof(val).stringof ~ " PRESET_" ~ field.stringof ~ "_ = " ~ val.stringof ~ ";"); 116 } 117 118 /// set the env key from which get the values to a registered option 119 /// Params: 120 /// field = the option in the type of `OptVal` 121 /// envKey = the env key 122 mixin template ENV(alias field, string envKey) { 123 alias ___FType___CMDLINE = typeof(field); 124 static assert(__CMDLINE_EXT_isInnerOptValField__!___FType___CMDLINE && !is( 125 ___FType___CMDLINE.InnerType == bool)); 126 debug pragma(msg, "enum ENV_" ~ field.stringof ~ "_ = \"" ~ envKey ~ "\";"); 127 mixin("enum ENV_" ~ field.stringof ~ "_ = \"" ~ envKey ~ "\";"); 128 } 129 130 /// set the choices list to a registered option or argument 131 /// Params: 132 /// field = the option or argument in the type of `OptVal` or `ArgVal` 133 /// Args = the choices list items 134 mixin template CHOICES(alias field, Args...) { 135 static assert(Args.length); 136 alias ___FType___CMDLINE = typeof(field); 137 static assert(!is(___FType___CMDLINE.InnerType == bool)); 138 enum isRegularType(alias val) = is(typeof(val) : ___FType___CMDLINE.InnerType) 139 || is(ElementType!(___FType___CMDLINE.InnerType) == typeof(val)); 140 import std.meta; 141 142 static assert(allSatisfy!(isRegularType, Args)); 143 debug pragma(msg, "static " ~ typeof( 144 Args[0])[].stringof ~ 145 " CHOICES_" ~ field.stringof ~ 146 "_ = " ~ [Args].stringof ~ ";"); 147 mixin("static " ~ typeof( 148 Args[0])[].stringof ~ 149 " CHOICES_" ~ field.stringof ~ 150 "_ = " ~ [Args].stringof ~ ";"); 151 } 152 153 /// set the range of a registered option or argument which inner 154 /// type is numeric type 155 /// Params: 156 /// field = the option or argument in the type of `OptVal` or `ArgVal` 157 /// Args = the minimum and the maximum in numeric type 158 mixin template RANGE(alias field, Args...) { 159 static assert(Args.length > 1); 160 alias ___FType___CMDLINE = typeof(field); 161 static assert(is(___FType___CMDLINE.InnerType == int) || is( 162 ___FType___CMDLINE.InnerType == double)); 163 static assert(is(typeof(Args[0]) == typeof(Args[1])) && is(typeof(Args[0]))); 164 static assert(is(typeof(Args[0]) : ___FType___CMDLINE.InnerType) && Args[0] < Args[1]); 165 debug pragma(msg, "enum RANGE_" ~ field.stringof ~ "_MIN_ = " ~ Args[0].stringof ~ ";"); 166 debug pragma(msg, "enum RANGE_" ~ field.stringof ~ "_MAX_ = " ~ Args[1].stringof ~ ";"); 167 mixin("enum RANGE_" ~ field.stringof ~ "_MIN_ = " ~ Args[0].stringof ~ ";"); 168 mixin("enum RANGE_" ~ field.stringof ~ "_MAX_ = " ~ Args[1].stringof ~ ";"); 169 } 170 171 /// disable the merge feature of a registered variadic option 172 /// Params: 173 /// field = the registered variadic option in the type of `OptVal` 174 mixin template DISABLE_MERGE(alias field) { 175 alias ___FType___CMDLINE = typeof(field); 176 static assert(___FType___CMDLINE.VARIADIC); 177 debug pragma(msg, "enum DISABLE_MERGE_" ~ field.stringof ~ "_ = " ~ "true;"); 178 mixin("enum DISABLE_MERGE_" ~ field.stringof ~ "_ = " ~ "true;"); 179 } 180 181 /// hide a registered option from `help` sub-command and action-option 182 /// Params: 183 /// field = the registered option in the type of `OptVal` 184 mixin template HIDE(alias field) { 185 static assert(__CMDLINE_EXT_isInnerOptValField__!(typeof(field))); 186 debug pragma(msg, "enum HIDE_" ~ field.stringof ~ "_ = " ~ "true;"); 187 mixin("enum HIDE_" ~ field.stringof ~ "_ = " ~ "true;"); 188 } 189 190 /// set a negate option for a registered option, and its long flag is 191 /// `--no-NAME_OF_OPTION` 192 /// Params: 193 /// field = the registered option in the type of `OptVal` 194 /// shortFlag = the short flag of the negate option 195 /// desc = the description of the negate option 196 mixin template NEGATE(alias field, string shortFlag, string desc = "") { 197 static assert(__CMDLINE_EXT_isInnerOptValField__!(typeof(field))); 198 debug pragma(msg, "enum NEGATE_" ~ field.stringof ~ "_ = \"" ~ shortFlag ~ "\";"); 199 mixin("enum NEGATE_" ~ field.stringof ~ "_ = \"" ~ shortFlag ~ "\";"); 200 static if (desc.length) { 201 mixin("enum NEGATE_" ~ field.stringof ~ "_DESC_ =\"" ~ desc ~ "\";"); 202 } 203 } 204 205 /// set the version string and enable the `version` sub-command and action-option 206 /// Params: 207 /// ver = the version string 208 /// flags = the flag of the version action-option which name would be the name of 209 /// the relevant sub-command. if not defied then the flag would be `--version -V` 210 mixin template VERSION(string ver, string flags = "") { 211 debug pragma(msg, "enum VERSION_ = \"" ~ ver ~ "\";"); 212 mixin("enum VERSION_ = \"" ~ ver ~ "\";"); 213 debug pragma(msg, "enum VERSION_FLAGS_ = \"" ~ flags ~ "\";"); 214 mixin("enum VERSION_FLAGS_ = \"" ~ flags ~ "\";"); 215 } 216 217 /// set an alias 218 /// Params: 219 /// name = the alias 220 mixin template ALIAS(string name) { 221 debug pragma(msg, "enum ALIAS_ = \"" ~ name ~ "\";"); 222 mixin("enum ALIAS_ = \"" ~ name ~ "\";"); 223 } 224 225 /// set description 226 /// Params: 227 /// desc = the description 228 mixin template DESC(string desc) { 229 enum DESC_ = desc; 230 } 231 232 /// <br><br> 233 /// hide the command from `help` sub-command and action-option, usually used on sub-command 234 mixin template HIDE() { 235 enum HIDE_ = true; 236 } 237 238 /// disallow the excess arguments 239 mixin template DISALLOW_EXCESS_ARGS() { 240 enum DISALLOW_EXCESS_ARGS_ = true; 241 } 242 243 /// show help after parsing error 244 mixin template SHOW_HELP_AFTER_ERR() { 245 enum SHOW_HELP_AFTER_ERR_ = true; 246 } 247 248 /// don't show suggestions after parsing error 249 mixin template NO_SUGGESTION() { 250 enum NO_SUGGESTION_ = true; 251 } 252 253 /// disallow the feature that can combine flags 254 mixin template DISALLOW_COMBINE() { 255 enum DONT_COMBINE_ = true; 256 } 257 258 /// <br><br> 259 /// disable the feature that variadic options under the command can merge value 260 /// from various source 261 mixin template DISABLE_MERGE() { 262 enum DISABLE_MERGE_ = true; 263 } 264 265 /// disable the `help` sub-command and action-option 266 mixin template DISABLE_HELP() { 267 enum DISABLE_HELP_ = true; 268 } 269 270 /// allow the command to pass through its option flag behind sub command 271 mixin template PASS_THROUGH() { 272 enum bool PASS_THROUGH_ = true; 273 } 274 275 /// add custom help text before command help text 276 mixin template HELP_TEXT_BEFORE(string text) { 277 enum HELP_TEXT_BEFORE_ = text; 278 } 279 280 /// add custom help text after command help text 281 mixin template HELP_TEXT_AFTER(string text) { 282 enum HELP_TEXT_AFTER_ = text; 283 } 284 285 /// apply `Command.exportAs` to the command line container 286 ///Params: 287 /// field = the field with the type of `OptVal` 288 /// Flags = the new flags 289 mixin template EXPORT(alias field, Flags...) { 290 import std.meta; 291 import std.traits; 292 293 static assert(__CMDLINE_EXT_isInnerOptValField__!(typeof(field))); 294 enum bool ___is_str___CMDLINE__(alias arg) = isSomeString!(typeof(arg)); 295 static assert(allSatisfy!(___is_str___CMDLINE__, Flags)); 296 mixin("static string[] EXPORT_" ~ field.stringof ~ "_ = " ~ [Flags].stringof ~ ";"); 297 } 298 299 /// apply `Command.exportNAs` to the command line container 300 /// Params: 301 /// field = the field with the type of `OptVal`, the field must be set with negate option 302 /// Flags = the new flags 303 mixin template EXPORT_N(alias field, Flags...) { 304 import std.meta; 305 import std.traits; 306 307 static assert(__CMDLINE_EXT_isInnerOptValField__!(typeof(field))); 308 enum bool ___is_str___CMDLINE__(alias arg) = isSomeString!(typeof(arg)); 309 static assert(allSatisfy!(___is_str___CMDLINE__, Flags)); 310 // static assert(hasMember!(__SELF__, "NEGATE_" ~ field.stringof ~ "_")); 311 mixin("static string[] EXPORT_N_" ~ field.stringof ~ "_ = " ~ [Flags].stringof ~ ";"); 312 } 313 314 /// enable gaining value from config file in json and set an option that specifies 315 /// the directories where the config file should be 316 /// Params: 317 /// flags = the flag of the config option which is used for specifying the directories 318 /// where the config file should be. if not defied then the flag would be 319 /// `-C, --config <config-dirs...>` 320 mixin template CONFIG(string flags = "") { 321 debug pragma(msg, "enum CONFIG_FLAGS_ = \"" ~ flags ~ "\";"); 322 mixin("enum CONFIG_FLAGS_ = \"" ~ flags ~ "\";"); 323 } 324 325 /// set the options acting as arguments on command line 326 /// Params: 327 /// Args = the options in the type of `OptVal` 328 mixin template OPT_TO_ARG(Args...) { 329 static assert(Args.length); 330 enum ___to_string___CMDLINE(alias var) = var.stringof; 331 import std.meta; 332 333 debug pragma(msg, "static " ~ string[Args.length].stringof ~ 334 " OPT_TO_ARG_ = " ~ [staticMap!(___to_string___CMDLINE, Args)].stringof ~ ";"); 335 mixin("static " ~ string[Args.length].stringof ~ 336 " OPT_TO_ARG_ = " ~ [staticMap!(___to_string___CMDLINE, Args)].stringof ~ ";"); 337 } 338 339 /// set the default sub-command which would act like the main-command except 340 /// `help`, `version` and `config` options and sub-command if exists. 341 /// Params: 342 /// sub = the sub command of the command 343 mixin template DEFAULT(alias sub) { 344 alias ___SubType___CMDLINE = typeof(sub); 345 import std.traits; 346 347 static assert(isPointer!___SubType___CMDLINE && isOutputResult!( 348 PointerTarget!___SubType___CMDLINE)); 349 enum DEFAULT_ = sub.stringof; 350 } 351 352 /// set sub commands 353 /// Params: 354 /// SubCmds = the sub command containers that satisfies with `isOutputResult` 355 mixin template SUB_CMD(SubCmds...) { 356 import std.meta; 357 358 static assert(SubCmds.length && allSatisfy!(isOutputResult, SubCmds)); 359 static foreach (sub; SubCmds) { 360 debug pragma(msg, sub.stringof ~ "* " ~ (sub.stringof)[0 .. $ - 6] ~ "Sub;"); 361 mixin(sub.stringof ~ "* " ~ (sub.stringof)[0 .. $ - 6] ~ "Sub;"); 362 } 363 } 364 365 /// prepare for the future use of function `ready` and `getParent`, which must be embedded at the top 366 /// of struct domain with `END` mixin-marco at the end of this struct domain. 367 mixin template BEGIN() { 368 enum bool __SPE_BEGIN_SEPCIAL__ = true; 369 alias __SELF__ = __traits(parent, __SPE_BEGIN_SEPCIAL__); 370 static void* __PARENT__; 371 static string __PARENT_STRING_OF__; 372 } 373 374 /// prepare for the future use of function `ready` and `getParent`, which must be embedded at the end 375 /// of struct domain with `BEGIN` mixin-marco at the begin of this struct domain. 376 mixin template END() { 377 import std.traits; 378 import std.meta; 379 380 enum bool __SPE_END_SEPCIAL__ = true; 381 static foreach (index, Type; Filter!(__CMDLINE_EXT_isInnerSubField__, FieldTypeTuple!__SELF__)) { 382 mixin("static bool IF_" ~ PointerTarget!Type.stringof ~ "_ = false;"); 383 } 384 385 static T* __GET_PARENT__(T)() { 386 if (this.__PARENT__ is null) 387 return null; 388 if (this.__PARENT_STRING_OF__ != T.stringof) 389 return null; 390 return cast(T*) this.__PARENT__; 391 } 392 393 // init opt_to_arg, export, export_n from mxin DEF 394 enum __IS_FIELD_A_NAME__(string name) = name.length > 18 395 && name[0 .. 18] == "__CMDLINE_FIELD_A_"; 396 enum __IS_FIELD_E_NAME__(string name) = name.length > 18 397 && name[0 .. 18] == "__CMDLINE_FIELD_E_"; 398 enum __IS_FIELD_N_NAME__(string name) = name.length > 18 399 && name[0 .. 18] == "__CMDLINE_FIELD_N_"; 400 401 enum __GET_FIELD_NAME__(string name) = name[18 .. $]; 402 alias __GET_FIELD_FLAGS__(string name) = __traits(getMember, __SELF__, name); 403 404 alias __OPT_A_NAMES__ = staticMap!(__GET_FIELD_NAME__, Filter!(__IS_FIELD_A_NAME__, __traits(allMembers, __SELF__))); 405 alias __OPT_E_NAMES__ = staticMap!(__GET_FIELD_NAME__, Filter!(__IS_FIELD_E_NAME__, __traits(allMembers, __SELF__))); 406 alias __OPT_N_NAMES__ = staticMap!(__GET_FIELD_NAME__, Filter!(__IS_FIELD_N_NAME__, __traits(allMembers, __SELF__))); 407 408 static if (__OPT_A_NAMES__.length) { 409 mixin("static " ~ string[__OPT_A_NAMES__.length].stringof ~ 410 " OPT_TO_ARG_ = " ~ [__OPT_A_NAMES__].stringof ~ ";"); 411 } 412 413 static if (__OPT_E_NAMES__.length) { 414 alias __OPT_E_FLAGS__ = staticMap!(__GET_FIELD_FLAGS__, Filter!(__IS_FIELD_E_NAME__, __traits(allMembers, __SELF__))); 415 debug pragma(msg, __OPT_E_FLAGS__); 416 static foreach (idx, nm; __OPT_E_NAMES__) { 417 mixin("static string[] EXPORT_" ~ nm ~ "_ = " ~ [__OPT_E_FLAGS__].stringof ~ ";"); 418 } 419 } 420 421 static if (__OPT_N_NAMES__.length) { 422 alias __OPT_N_FLAGS__ = staticMap!(__GET_FIELD_FLAGS__, Filter!(__IS_FIELD_N_NAME__, __traits(allMembers, __SELF__))); 423 debug pragma(msg, __OPT_N_FLAGS__); 424 static foreach (idx, nm; __OPT_N_NAMES__) { 425 mixin("static string[] EXPORT_N_" ~ nm ~ "_ = " ~ [__OPT_N_FLAGS__].stringof ~ ";"); 426 } 427 } 428 } 429 430 /// get the pointer to result container of parent. 431 /// Params: 432 /// subOutput = the sub result container 433 /// Returns: `null` if type of `subOutput` not embeds `BEGIN` and `END` or `T` is not correct 434 T* getParent(T, U)(in U subOutput) if (isOutputResult!U && isOutputResult!T) { 435 static if (hasMember!(U, "__SPE_BEGIN_SEPCIAL__") && hasMember!(U, "__SPE_END_SEPCIAL__")) { 436 return subOutput.__GET_PARENT__!T; 437 } 438 else { 439 return null; 440 } 441 } 442 443 /// get the pointer to result container of parent. 444 /// Params: 445 /// subOutput = the pointer to the sub result container 446 /// Returns: `null` if type of `subOutput` not embeds `BEGIN` and `END` or `T` is not correct 447 T* getParent(T, U)(const(U)* subOutput) if (isOutputResult!U && isOutputResult!T) { 448 static if (hasMember!(U, "__SPE_BEGIN_SEPCIAL__") && hasMember!(U, "__SPE_END_SEPCIAL__")) { 449 return subOutput.__GET_PARENT__!T; 450 } 451 else { 452 return null; 453 } 454 } 455 456 /// detect whether a sub command's container of a main command is ready for use. 457 /// for using this function, the `BEGIN` and `END` mixin-macro must be embeed in 458 /// main command container. 459 /// `T` is the type in sub-command container, `U` is the type in main-command container 460 /// Params: 461 /// output = the main-command container 462 /// Returns: `true` if the sub-command is ready, otherwise is not ready. 463 bool ready(T, U)(const U* output) 464 if (isOutputResult!T && isOutputResult!U && hasMember!(U, "__SPE_END_SEPCIAL__")) { 465 return mixin(output.stringof ~ '.' ~ "IF_" ~ T.stringof ~ '_'); 466 } 467 468 /// get a pointer to sub-command container. 469 /// `T` is the type in sub-command container, `U` is the type in main-command container 470 /// Params: 471 /// output = the main-command container 472 /// Returns: a pointer to sub-command container if the sub-command is ready, 473 /// otherwise `null` 474 inout(T)* subResult(T, U)(inout(U)* output) 475 if (isOutputResult!T && isOutputResult!U) { 476 alias ftypes = FieldTypeTuple!U; 477 alias fnames = FieldNameTuple!U; 478 static foreach (index, Type; ftypes) { 479 { 480 static if (__CMDLINE_EXT_isInnerSubField__!Type && is(PointerTarget!Type == T)) { 481 return mixin(output.stringof ~ '.' ~ fnames[index]); 482 } 483 } 484 } 485 return null; 486 } 487 488 /// the field of command container which is used to register a argument on the command. 489 /// the name of it is the name of this argument. 490 /// can implicitly convert to bool value same as the result of `ArgVal.isValid`. 491 /// `T` is the innerType of the argument, 492 /// `isOptional` `true` to set it optional, default is `false`. 493 struct ArgVal(T, bool isOptional = false) { 494 static assert(isArgValueType!T); 495 Argument _inner; 496 497 enum ARGVAL_FLAG_ = true; 498 enum OPTIONAL = isOptional; 499 500 /// the inner type of the argument 501 alias InnerType = T; 502 503 /// test whether the inner value is ready. 504 /// Returns: `true` if ready, otherwise not ready. 505 bool isValid() const { 506 return _inner.settled; 507 } 508 509 // make `ArgVal` enable to implicitly convert to bool value 510 // same as the result of `isValid` 511 alias isValid this; 512 513 /// get the inner value 514 auto get() const { 515 return _inner.get!T; 516 } 517 518 /// assign the inner value through passing into `Argument` variable 519 auto opAssign(Argument value) { 520 this._inner = value; 521 return this; 522 } 523 } 524 525 /// the field of command container which is used to register a option on the command. 526 /// the name of it is the name of this option. 527 /// can implicitly convert to bool value same as the result of `ArgVal.isValid`. 528 /// `T` is the innerType of the option or the elemental type of innerType 529 /// `isMandatory` `true` to set it mandatory, default is `false`, 530 /// `shortAndVal` the short flag and value flag(if needed) seperated by space, comma and `|` 531 struct OptVal(T, string shortAndVal, bool isMandatory = false) { 532 static assert(isOptionValueType!T); 533 Option _inner; 534 535 /// the inner type of the option 536 alias InnerType = T; 537 538 enum OPTVAL_FLAG_ = true; 539 enum SHORT_FLAG_AND_VAL_STR = shortAndVal; 540 enum MANDATORY = isMandatory; 541 542 static if (!is(T == bool)) { 543 static if (shortAndVal[$ - 1] == ']') { 544 enum OPTIONAL = true; 545 } 546 else static if (shortAndVal[$ - 1] == '>') { 547 enum OPTIONAL = false; 548 } 549 else { 550 static assert(0); 551 } 552 static if (shortAndVal.length > 5 && shortAndVal[$ - 4 .. $ - 1] == "...") { 553 enum VARIADIC = true; 554 } 555 else { 556 enum VARIADIC = false; 557 } 558 } 559 560 /// test whether the inner value is ready. 561 /// Returns: `true` if ready, otherwise not ready. 562 bool isValid() const { 563 return _inner !is null && _inner.settled; 564 } 565 566 // make `OptVal` enable to implicitly convert to bool value 567 // same as the result of `isValid` 568 alias isValid this; 569 570 /// get the value in type of `T` 571 auto get() const { 572 assert(isValid); 573 return _inner.get!T; 574 } 575 576 /// assign the inner value through passing into `Option` variable 577 auto opAssign(Option value) { 578 this._inner = value; 579 return this; 580 } 581 } 582 583 /// use for defining a command line argument 584 /// Params: 585 /// name = the argument's name 586 /// T = the type of argument 587 /// Args = the configs 588 mixin template DEF_ARG(string name, T, Args...) { 589 mixin DEF!(name, T, Args); 590 } 591 592 /// use for defining a command line option 593 /// Params: 594 /// name = the name of option 595 /// T = the type of option 596 /// flag = the flag of option wihout long-flag 597 /// Args = the configs 598 mixin template DEF_OPT(string name, T, string flag, Args...) { 599 mixin DEF!(name, T, Flag_d!flag, Args); 600 } 601 602 /// the basic version of both `DEF_ARG` and `DEF_OPT` 603 /// Params: 604 /// name = the name of argument or option 605 /// T = the type of argument or option 606 /// Args = the configs 607 mixin template DEF(string name, T, Args...) { 608 import std.meta; 609 610 static assert(allSatisfy!(__CMDLINE_isFieldDef__, Args)); 611 612 static if (!is(__CMDLINE_getFiledById__!(-2, Args) == void)) { 613 mixin("enum " ~ "__CMDLINE_FIELD_isOptional_" ~ name ~ " = " ~ true.stringof ~ ";"); 614 } 615 else { 616 mixin("enum " ~ "__CMDLINE_FIELD_isOptional_" ~ name ~ " = " ~ false.stringof ~ ";"); 617 } 618 619 static if (!is(__CMDLINE_getFiledById__!(-1, Args) == void)) { 620 mixin("OptVal!(" ~ T.stringof ~ ", \"" ~ Args[0].args ~ "\", " 621 ~ "__CMDLINE_FIELD_isOptional_" ~ name ~ ")" ~ name ~ ";"); 622 debug pragma(msg, "OptVal!(" ~ T.stringof ~ ", \"" ~ Args[0].args ~ "\", " 623 ~ "__CMDLINE_FIELD_isOptional_" ~ name ~ ")" ~ name ~ ";"); 624 } 625 else { 626 mixin("ArgVal!(" ~ T.stringof ~ ", " ~ "__CMDLINE_FIELD_isOptional_" ~ name ~ ")" ~ name ~ ";"); 627 debug pragma(msg, "ArgVal!(" ~ T.stringof ~ ", " ~ "__CMDLINE_FIELD_isOptional_" ~ name ~ ")" ~ name ~ ";"); 628 } 629 630 mixin("alias " ~ "__CMDLINE_FIELD_F_" ~ name ~ "= " ~ name ~ ";"); 631 632 static foreach (decl; Args) { 633 static if (decl.__CMDLINE_FIELD_DEF__ == 0) { 634 mixin DESC!( 635 mixin("__CMDLINE_FIELD_F_" ~ name), 636 decl.args 637 ); 638 } 639 else static if (decl.__CMDLINE_FIELD_DEF__ == 1) { 640 mixin RANGE!( 641 mixin("__CMDLINE_FIELD_F_" ~ name), 642 decl.args 643 ); 644 } 645 else static if (decl.__CMDLINE_FIELD_DEF__ == 2) { 646 mixin CHOICES!( 647 mixin("__CMDLINE_FIELD_F_" ~ name), 648 decl.args 649 ); 650 } 651 else static if (decl.__CMDLINE_FIELD_DEF__ == 3) { 652 mixin DEFAULT!( 653 mixin("__CMDLINE_FIELD_F_" ~ name), 654 decl.args 655 ); 656 } 657 else static if (decl.__CMDLINE_FIELD_DEF__ == 4) { 658 mixin PRESET!( 659 mixin("__CMDLINE_FIELD_F_" ~ name), 660 decl.args 661 ); 662 } 663 else static if (decl.__CMDLINE_FIELD_DEF__ == 5) { 664 mixin ENV!( 665 mixin("__CMDLINE_FIELD_F_" ~ name), 666 decl.args 667 ); 668 } 669 else static if (decl.__CMDLINE_FIELD_DEF__ == 6) { 670 mixin NEGATE!( 671 mixin("__CMDLINE_FIELD_F_" ~ name), 672 decl.args 673 ); 674 } 675 else static if (decl.__CMDLINE_FIELD_DEF__ == 7) { 676 mixin HIDE!(mixin("__CMDLINE_FIELD_F_" ~ name)); 677 } 678 else static if (decl.__CMDLINE_FIELD_DEF__ == 8) { 679 mixin DISABLE_MERGE!(mixin("__CMDLINE_FIELD_F_" ~ name)); 680 } 681 else static if (decl.__CMDLINE_FIELD_DEF__ == 9) { 682 mixin("enum " ~ "__CMDLINE_FIELD_A_" ~ name ~ " = 1;"); 683 } 684 else static if (decl.__CMDLINE_FIELD_DEF__ == 10) { 685 mixin("alias " ~ "__CMDLINE_FIELD_E_" ~ name ~ " = decl.args;"); 686 } 687 else static if (decl.__CMDLINE_FIELD_DEF__ == 11) { 688 mixin("alias " ~ "__CMDLINE_FIELD_N_" ~ name ~ " = decl.args;"); 689 } 690 } 691 } 692 693 /// used inside the bracket of `DEF` to set the option mandatory or set the argument optional 694 struct Optional_d { 695 enum __CMDLINE_FIELD_DEF__ = -2; 696 } 697 698 /// used inside the bracket of `DEF` to set the flag of an option 699 struct Flag_d(alias flag) { 700 enum __CMDLINE_FIELD_DEF__ = -1; 701 alias args = flag; 702 } 703 704 /// used inside the bracket of `DEF`, `DEF_ARG` and `DEF_OPT` to set the desc of an option or an argument 705 struct Desc_d(alias desc) { 706 enum __CMDLINE_FIELD_DEF__ = 0; 707 alias args = desc; 708 } 709 710 /// used inside the bracket of `DEF`, `DEF_ARG` and `DEF_OPT` to set the range of an option or an argument 711 struct Range_d(Args...) { 712 enum __CMDLINE_FIELD_DEF__ = 1; 713 alias args = Args; 714 } 715 716 /// used inside the bracket of `DEF`, `DEF_ARG` and `DEF_OPT` to set the choices of an option or an argument 717 struct Choices_d(Args...) { 718 enum __CMDLINE_FIELD_DEF__ = 2; 719 alias args = Args; 720 } 721 722 /// used inside the bracket of `DEF`, `DEF_ARG` and `DEF_OPT` to set the default value of an option or an argument 723 struct Default_d(alias val) { 724 enum __CMDLINE_FIELD_DEF__ = 3; 725 alias args = val; 726 } 727 728 /// used inside the bracket of `DEF`, `DEF_OPT` to set the preset value of an option 729 struct Preset_d(alias val) { 730 enum __CMDLINE_FIELD_DEF__ = 4; 731 alias args = val; 732 } 733 734 /// used inside the bracket of `DEF`, `DEF_OPT` to set the value from environment of an option 735 struct Env_d(alias envKey) { 736 enum __CMDLINE_FIELD_DEF__ = 5; 737 alias args = envKey; 738 } 739 740 /// used inside the bracket of `DEF`, `DEF_OPT` to set the negate option of an option 741 struct Negate_d(alias shortFlag, alias desc = "") { 742 enum __CMDLINE_FIELD_DEF__ = 6; 743 alias args = AliasSeq!(shortFlag, desc); 744 } 745 746 /// used inside the bracket of `DEF`, `DEF_OPT` to hide an option from help info 747 struct Hide_d { 748 enum __CMDLINE_FIELD_DEF__ = 7; 749 } 750 751 /// used inside the bracket of `DEF`, `DEF_OPT` to disable the merge feature of variadic option 752 struct DisableMerge_d { 753 enum __CMDLINE_FIELD_DEF__ = 8; 754 } 755 756 /// used inside the bracket of `DEF`, `DEF_OPT` to make an option act like an argument 757 struct ToArg_d { 758 enum __CMDLINE_FIELD_DEF__ = 9; 759 } 760 761 /// used inside the bracket of `DEF`, `DEF_OPT`, see `EXPORT` 762 struct Export_d(Args...) { 763 enum __CMDLINE_FIELD_DEF__ = 10; 764 alias args = Args; 765 } 766 767 /// used inside the bracket of `DEF`, `DEF_OPT`, see `EXPORT_N` 768 struct N_Export_d(Args...) { 769 enum __CMDLINE_FIELD_DEF__ = 11; 770 alias args = Args; 771 } 772 773 enum __CMDLINE_isFieldDef__(T) = hasMember!(T, "__CMDLINE_FIELD_DEF__"); 774 template __CMDLINE_getFiledById__(int id, Types...) { 775 enum __XX(T) = T.__CMDLINE_FIELD_DEF__ == id; 776 alias tmp = Filter!(__XX, Types); 777 static if (tmp.length) 778 alias __CMDLINE_getFiledById__ = tmp[0]; 779 else 780 alias __CMDLINE_getFiledById__ = void; 781 } 782 783 private alias getMember(alias T, string flag) = __traits(getMember, T, flag); 784 785 /// construct the command line program without action callback 786 /// Returns: the root command in `Command` that is confiured according to the given command conatiner type. 787 Command construct(T)() if (isOutputResult!T) { 788 alias fnames = FieldNameTuple!T; 789 alias ftypes = FieldTypeTuple!T; 790 Command cmd = createCommand(T.stringof[0 .. $ - 6]._tokeytab); 791 static if (hasMember!(T, "DESC_")) { 792 cmd.description(T.DESC_); 793 } 794 static if (hasMember!(T, "HIDE_")) { 795 cmd._hidden = true; 796 } 797 static if (hasMember!(T, "DISALLOW_EXCESS_ARGS_")) { 798 cmd.allowExcessArguments(false); 799 } 800 static if (hasMember!(T, "VERSION_")) { 801 cmd.setVersion(T.VERSION_, T.VERSION_FLAGS_); 802 } 803 static if (hasMember!(T, "SHOW_HELP_AFTER_ERR_")) { 804 cmd.showHelpAfterError(true); 805 } 806 static if (hasMember!(T, "NO_SUGGESTION_")) { 807 cmd.showSuggestionAfterError(false); 808 } 809 static if (hasMember!(T, "DONT_COMBINE_")) { 810 cmd.comineFlagAndOptionValue(false); 811 } 812 static if (hasMember!(T, "DISABLE_MERGE_")) { 813 cmd.allowVariadicMerge(false); 814 } 815 static if (hasMember!(T, "DISABLE_HELP_")) { 816 cmd.disableHelp(); 817 } 818 static if (hasMember!(T, "CONFIG_FLAGS_")) { 819 cmd.setConfigOption(T.CONFIG_FLAGS_); 820 } 821 static if (hasMember!(T, "DEFAULT_")) { 822 cmd._defaultCommandName = T.DEFAULT_; 823 } 824 static if (hasMember!(T, "PASS_THROUGH_")) { 825 cmd._passThroughOptionValue = true; 826 } 827 static if (hasMember!(T, "ALIAS_")) { 828 cmd.aliasName(T.ALIAS_); 829 } 830 static if (hasMember!(T, "HELP_TEXT_BEFORE_")) { 831 cmd.addHelpText(AddHelpPos.Before, T.HELP_TEXT_BEFORE_); 832 } 833 static if (hasMember!(T, "HELP_TEXT_AFTER_")) { 834 cmd.addHelpText(AddHelpPos.After, T.HELP_TEXT_AFTER_); 835 } 836 static foreach (index, Type; ftypes) { 837 static if (__CMDLINE_EXT_isInnerArgValField__!Type) { 838 { 839 mixin SetArgValField!(cmd, Type, T, index, fnames); 840 } 841 } 842 else static if (__CMDLINE_EXT_isInnerOptValField__!Type) { 843 { 844 mixin SetOptValField!(cmd, Type, T, index, fnames); 845 } 846 } 847 else static if (__CMDLINE_EXT_isInnerSubField__!Type) { 848 { 849 mixin SetSubCommand!(cmd, Type); 850 } 851 } 852 } 853 static if (hasStaticMember!(T, "OPT_TO_ARG_")) { 854 auto arr = getMember!(T, "OPT_TO_ARG_"); 855 import std.algorithm; 856 import std.array; 857 858 auto tmp = arr[].map!(str => _tokeytab(str)).array; 859 cmd.argToOpt(tmp[0], tmp[1 .. $]); 860 } 861 return cmd; 862 } 863 864 /// parse the command line option and argument parameters according to the given command conatiner type. 865 /// T = the command conatiner type 866 /// Params: 867 /// argv = the command line arguments in string 868 /// Returns: an initialized instance of the command conatiner type 869 T* parse(T)(in string[] argv) if (isOutputResult!T) { 870 alias fnames = FieldNameTuple!T; 871 alias ftypes = FieldTypeTuple!T; 872 assert(argv.length); 873 auto cmd = construct!T; 874 cmd.parse(argv); 875 T* output = new T; 876 static foreach (index, name; fnames) { 877 { 878 mixin InitOutputResultField!(cmd, output, index, name, ftypes); 879 } 880 } 881 return output; 882 } 883 884 /// parse the command line option and argument parameters according to the given command conatiner type. 885 /// And invoke the `action` member function and return if exists, otherwise invoke member container's `action` 886 /// member function recursely. 887 /// T = the root command container type 888 /// Params: 889 /// argv = the arguments list in string 890 void run(T)(in string[] argv) if (isOutputResult!T) { 891 T* output = parse!T(argv); 892 runImpl(output); 893 } 894 895 private: 896 897 void runImpl(T)(T* output) if (isOutputResult!T) { 898 static if (hasMember!(T, "action")) { 899 output.action(); 900 } 901 else { 902 alias fnames = FieldNameTuple!T; 903 static foreach (index, Type; Filter!(__CMDLINE_EXT_isInnerSubField__, FieldTypeTuple!T)) { 904 if (auto sub_output = output.subResult!(PointerTarget!Type)) { 905 runImpl(sub_output); 906 } 907 } 908 } 909 } 910 911 mixin template InitOutputResultField(alias cmd, alias output, alias index, alias name, ftypes...) { 912 alias Type = ftypes[index]; 913 static if (__CMDLINE_EXT_isInnerArgValField__!Type) { 914 auto x = mixin(output.stringof ~ '.' ~ name) = cmd.findArgument(name._tokeytab); 915 } 916 else static if (__CMDLINE_EXT_isInnerOptValField__!Type) { 917 auto x = mixin(output.stringof ~ '.' ~ name) = cmd.findOption(name._tokeytab); 918 } 919 else static if (__CMDLINE_EXT_isInnerSubField__!Type) { 920 alias T = PointerTarget!Type; 921 alias sfnames = FieldNameTuple!T; 922 alias sftypes = FieldTypeTuple!T; 923 debug pragma(msg, T.stringof, " ", typeof(output).stringof); 924 Command sub = cmd.findCommand(T.stringof[0 .. $ - 6]._tokeytab); 925 auto xfn = () { 926 if (cmd._called_sub == sub._name) { 927 static if (hasMember!(typeof(output), "__SPE_END_SEPCIAL__")) { 928 mixin(output.stringof ~ '.' ~ "IF_" ~ T.stringof ~ '_') = true; 929 } 930 auto sub_output = mixin(output.stringof ~ '.' ~ name) = new T; 931 static if (hasMember!(T, "__SPE_BEGIN_SEPCIAL__") && hasMember!(T, "__SPE_END_SEPCIAL__")) { 932 auto x = T.__PARENT__ = output; 933 auto xx = T.__PARENT_STRING_OF__ = PointerTarget!(typeof(output)).stringof; 934 } 935 static foreach (index, name; sfnames) { 936 { 937 mixin InitOutputResultField!(sub, sub_output, index, name, sftypes); 938 } 939 } 940 } 941 return 1; 942 }; 943 auto _x_ = xfn(); 944 } 945 } 946 947 mixin template SetArgValField(alias cmd, Type, T, alias index, fnames...) { 948 alias IType = Type.InnerType; 949 enum string arg_name = fnames[index]; 950 enum bool optional = Type.OPTIONAL; 951 enum bool variadic = !isSomeString!IType && !is(ElementType!IType == void); 952 string nameOutput = _tokeytab(arg_name) ~ (variadic ? "..." : ""); 953 Argument arg = createArgument!(IType)( 954 optional ? "[" ~ nameOutput ~ "]" : "<" ~ nameOutput ~ ">" 955 ); 956 enum string fdesc = "DESC_ARG_" ~ arg_name ~ '_'; 957 enum string fdef = "DEFAULT_" ~ arg_name ~ '_'; 958 enum string fchoices = "CHOICES_" ~ arg_name ~ "_"; 959 enum string frange_min = "RANGE_" ~ arg_name ~ "_MIN_"; 960 enum string frange_max = "RANGE_" ~ arg_name ~ "_MAX_"; 961 static if (hasMember!(T, fdesc)) { 962 auto x = arg.description(getMember!(T, fdesc)); 963 } 964 static if (hasStaticMember!(T, fdef)) { 965 auto xx = arg.defaultVal(getMember!(T, fdef)); 966 } 967 static if (hasStaticMember!(T, fchoices)) { 968 auto xxx = arg.choices(getMember!(T, fchoices)); 969 } 970 static if (hasMember!(T, frange_min) && hasMember!(T, frange_max)) { 971 auto xxxxx = arg.rangeOf( 972 getMember!(T, frange_min), 973 getMember!(T, frange_max) 974 ); 975 } 976 auto xxxxxx = cmd.addArgument(arg); 977 } 978 979 mixin template SetOptValField(alias cmd, Type, T, alias idnex, fnames...) { 980 alias IType = Type.InnerType; 981 enum string opt_name = fnames[idnex]; 982 enum bool mandatory = Type.MANDATORY; 983 string kname = _tokeytab(opt_name); 984 Option opt = createOption!IType("--" ~ kname ~ ' ' 985 ~ Type.SHORT_FLAG_AND_VAL_STR); 986 enum string fdesc = "DESC_OPT_" ~ opt_name ~ '_'; 987 enum string fdef = "DEFAULT_" ~ opt_name ~ '_'; 988 enum string fpreset = "PRESET_" ~ opt_name ~ '_'; 989 enum string fenv = "ENV_" ~ opt_name ~ '_'; 990 enum string fchoices = "CHOICES_" ~ opt_name ~ "_"; 991 enum string frange_min = "RANGE_" ~ opt_name ~ "_MIN_"; 992 enum string frange_max = "RANGE_" ~ opt_name ~ "_MAX_"; 993 enum string fnegate_sh = "NEGATE_" ~ opt_name ~ '_'; 994 enum string fnegate_desc = "NEGATE_" ~ opt_name ~ "_DESC_"; 995 enum string fexport = "EXPORT_" ~ opt_name ~ "_"; 996 enum string fexport_n = "EXPORT_N_" ~ opt_name ~ "_"; 997 auto x = opt.makeMandatory(mandatory); 998 static if (hasMember!(T, "DISABLE_MERGE_" ~ opt_name ~ '_')) { 999 auto xx = opt.merge(false); 1000 } 1001 static if (hasMember!(T, "HIDE_" ~ opt_name ~ '_')) { 1002 auto xxx = opt.hidden = true; 1003 } 1004 NegateOption nopt = null; 1005 static if (hasMember!(T, fnegate_sh)) { 1006 string short_flag = getMember!(T, fnegate_sh); 1007 static if (hasMember!(T, fnegate_desc)) { 1008 auto _x_ = getMember!(T, fnegate_desc); 1009 } 1010 else { 1011 auto _x_ = ""; 1012 } 1013 auto xxxx = nopt = createNegateOption("--no-" ~ kname ~ ' ' ~ short_flag, _x_); 1014 } 1015 static if (hasMember!(T, fdesc)) { 1016 auto xxxxx = opt.description(getMember!(T, fdesc)); 1017 } 1018 static if (hasStaticMember!(T, fdef)) { 1019 auto xxxxxx = opt.defaultVal(getMember!(T, fdef)); 1020 } 1021 static if (hasStaticMember!(T, fchoices)) { 1022 auto xxxxxxx = opt.choices(getMember!(T, fchoices)); 1023 } 1024 static if (hasMember!(T, frange_min) && hasMember!(T, frange_max)) { 1025 auto xxxxxxxx = opt.rangeOf( 1026 getMember!(T, frange_min), 1027 getMember!(T, frange_max) 1028 ); 1029 } 1030 static if (hasStaticMember!(T, fpreset)) { 1031 auto xxxxxxxxx = opt.preset(getMember!(T, fpreset)); 1032 } 1033 static if (hasMember!(T, fenv)) { 1034 auto xxxxxxxxxx = opt.env(getMember!(T, fenv)); 1035 } 1036 auto xxxxxxxxxxx = cmd.addOption(opt); 1037 auto xxxxxxxxxxxx = nopt ? cmd.addOption(nopt) : cmd; 1038 static if (hasStaticMember!(T, fexport)) { 1039 auto xxxxxxxxxxxxx = cmd.exportAs(opt_name, getMember!(T, fexport)); 1040 } 1041 static if (hasStaticMember!(T, fexport_n)) { 1042 auto xxxxxxxxxxxxxx = cmd.exportNAs(opt_name, getMember!(T, fexport_n)); 1043 } 1044 // 14 x 1045 } 1046 1047 mixin template SetSubCommand(alias cmd, Type) { 1048 alias SubT = PointerTarget!Type; 1049 Command sub = construct!SubT; 1050 auto x = cmd.addCommand(sub); 1051 } 1052 1053 string _tokeytab(string from) { 1054 import std.regex; 1055 import std.string : toLower; 1056 1057 auto trans = (Captures!(string) m) { return '-' ~ toLower(m.hit); }; 1058 return cast(char) toLower(from[0]) ~ replaceAll!(trans)(from[1 .. $], regex(`[A-Z]`)); 1059 } 1060 1061 // mixin template IMPLIES(alias field, string key, alias val) { 1062 // alias ___FType___CMDLINE = typeof(field); 1063 // static assert(__CMDLINE_EXT_isInnerOptValField__!___FType___CMDLINE && !is(___FType___CMDLINE.InnerType == bool)); 1064 // static assert(isOptionValueType!(typeof(val))); 1065 // debug pragma(msg, "static " ~ typeof(val) 1066 // .stringof ~ 1067 // " IMPLIES_" ~ field.stringof ~ "_" ~ key ~ 1068 // "_ = " ~ val.stringof ~ ";"); 1069 // mixin("static " ~ typeof(val) 1070 // .stringof ~ 1071 // " IMPLIES_" ~ field.stringof ~ "_" ~ key ~ 1072 // "_ = " ~ val.stringof ~ ";"); 1073 // } 1074 1075 // mixin template IMPLIES_BOOL(alias field, Args...) { 1076 // static assert(Args.length); 1077 // alias ___FType___CMDLINE = typeof(field); 1078 // static assert(allSatisfy!(isSomeString, Args)); 1079 // debug pragma(msg, "static " ~ typeof( 1080 // Args[0])[].stringof ~ 1081 // " IMPLIES_" ~ field.stringof ~ 1082 // "_ = " ~ [Args].stringof ~ ";"); 1083 // mixin("static " ~ typeof( 1084 // Args[0])[].stringof ~ 1085 // " IMPLIES_" ~ field.stringof ~ 1086 // "_ = " ~ [Args].stringof ~ ";"); 1087 // }