1 /++
2 $(H2 The Command Type for Cmdline)
3 
4 This modlue mainly has `Command` Type.
5 
6 we can configure the command in manly way
7 and then use `Command.parse` to parse the input command line.
8 if you define its action callback, this callback would be called when parsing. 
9 
10 Authors: 笑愚(xiaoyu)
11 +/
12 module cmdline.command;
13 
14 import std.stdio;
15 import std.process;
16 import std.conv;
17 import std.array;
18 import std.range;
19 import std.range.primitives;
20 import std.traits;
21 import std.regex;
22 import std.string;
23 import std.typecons;
24 import std.algorithm;
25 import std.format;
26 import std.file;
27 import std.path;
28 import std.typetuple;
29 import std.json;
30 
31 import cmdline.error;
32 import cmdline.argument;
33 import cmdline.option;
34 import cmdline.help;
35 import cmdline.pattern;
36 import cmdline.event;
37 
38 version (Windows) import core.sys.windows.windows;
39 
40 /// the enum represents the position of appendent help text
41 enum AddHelpPos : string {
42     BeforeAll = "beforeAll",
43     Before = "before",
44     After = "after",
45     AfterAll = "afterAll"
46 }
47 
48 /// the callback passed as a parameter of `this.action`
49 alias ActionCallback = void delegate();
50 
51 /// the callback passed as a parameter of `this.action`
52 alias ActionCallback_0 = void delegate(in OptsWrap);
53 /// the callback passed as a parameter of `this.action`
54 alias ActionCallback_1 = void delegate(in OptsWrap, in ArgWrap);
55 /// the callback passed as a parameter of `this.action`
56 alias ActionCallback_2 = void delegate(in OptsWrap, in ArgWrap, in ArgWrap);
57 /// the callback passed as a parameter of `this.action`
58 alias ActionCallback_3 = void delegate(in OptsWrap, in ArgWrap, in ArgWrap, in ArgWrap);
59 /// the callback passed as a parameter of `this.action`
60 alias ActionCallback_4 = void delegate(in OptsWrap, in ArgWrap, in ArgWrap, in ArgWrap, in ArgWrap);
61 /// the callback passed as a parameter of `this.action`
62 alias ActionCallback_5 = void delegate(in OptsWrap, in ArgWrap, in ArgWrap, in ArgWrap, in ArgWrap, in ArgWrap);
63 
64 /// the sequence of action callbacks
65 alias ActionCallBackSeq = AliasSeq!(
66     ActionCallback,
67     ActionCallback_0,
68     ActionCallback_1,
69     ActionCallback_2,
70     ActionCallback_3,
71     ActionCallback_4,
72     ActionCallback_5
73 );
74 
75 /// Command Type
76 class Command : EventManager {
77 package:
78     Command parent = null;
79     Command[] _commands = [];
80 
81     Option[] _options = [];
82     NegateOption[] _negates = [];
83     Option[] _abandons = [];
84 
85     Argument[] _arguments = [];
86 
87     string _name;
88     string _defaultCommandName = "";
89     string[] _aliasNames = [];
90     string _selfPath = "";
91 
92     string _called_sub = "";
93 
94     string _version = "*";
95     string _usage = "";
96 
97     string _description = "";
98     string[string] _argsDescription = null;
99 
100     bool _execHandler = false;
101     string _execFile = "";
102     string _execDir = "";
103     string[string] _externalCmdHelpFlagMap = null;
104 
105     string[] rawFlags = [];
106     string[] argFlags = [];
107     string[] errorFlags = [];
108     string[] unknownFlags = [];
109 
110     Command subCommand = null;
111 
112     bool immediately = false;
113 
114     public ArgVariant[] args = [];
115     public OptionVariant[string] opts = null;
116 
117     bool _allowExcessArguments = true;
118     bool _showHelpAfterError = false;
119     bool _showSuggestionAfterError = true;
120     bool _combineFlagAndOptionalValue = true;
121     bool _allowVariadicMerge = true;
122     bool _allowExposeOptionValue = false;
123     bool _passThroughOptionValue = false;
124 
125     void delegate() _actionHandler = null;
126     Help _helpConfiguration = new Help;
127     OutputConfiguration _outputConfiguration = new OutputConfiguration();
128 
129     bool _hidden = false;
130     bool _addImplicitHelpCommand = true;
131     bool _addImplicitHelpOption = true;
132     Option _helpOption = null;
133     Command _helpCommand = null;
134 
135     Option _versionOption = null;
136     Command _versionCommand = null;
137 
138     Option _configOption = null;
139     const(JSONValue)*[] jconfig = [];
140     string[] _configPaths = [];
141 
142     string[] _argToOptNames = [];
143 
144     string[] _provide_arr = [];
145     string[] _inject_arr = [];
146 
147     Option[string] _import_map = null;
148     NegateOption[string] _import_n_map = null;
149 
150     Option[string] _export_map = null;
151     NegateOption[string] _export_n_map = null;
152 
153     this(string name) {
154         this._name = name;
155     }
156 
157     alias Self = typeof(this);
158 
159     /// inherit the basic configuration from another command
160     public Self copyInheritedSettings(Command src) {
161         this._allowExcessArguments = src._allowExcessArguments;
162         this._showHelpAfterError = src._showHelpAfterError;
163         this._showSuggestionAfterError = src._showSuggestionAfterError;
164         this._combineFlagAndOptionalValue = src._combineFlagAndOptionalValue;
165         this._allowVariadicMerge = src._allowVariadicMerge;
166         this._allowExposeOptionValue = src._allowExposeOptionValue;
167         this._passThroughOptionValue = src._passThroughOptionValue;
168         this._outputConfiguration = src._outputConfiguration;
169         this._helpConfiguration = src._helpConfiguration;
170         return this;
171     }
172 
173     inout(Command)[] _getCommandAndAncestors() inout {
174         Command[] result = [];
175         for (Command cmd = cast(Command) this; cmd; cmd = cmd.parent) {
176             result ~= cmd;
177         }
178         return cast(inout(Command)[]) result;
179     }
180 
181 public:
182     /// get the description of command
183     string description() const {
184         return "description: " ~ this._description;
185     }
186 
187     /// set the description of command
188     Self description(string str, string[string] argsDesc = null) {
189         this._description = str;
190         if (argsDesc !is null)
191             this._argsDescription = argsDesc;
192         return this;
193     }
194 
195     /// configurre the help
196     Self configureHelp(Help helpConfig) {
197         this._helpConfiguration = helpConfig;
198         return this;
199     }
200 
201     /// get the help configuration
202     inout(Help) configureHelp() inout {
203         return this._helpConfiguration;
204     }
205 
206     /// register an option that can be as the argument of command line.
207     /// remember that it is positional sensitive and if you want to registered a variadic
208     /// option, this option must be registered at the end and not other variadic options to be registered
209     /// Params:
210     ///   optName = the option's name or its long/short flag to be registered
211     ///   rest = the rest be registered
212     /// Returns: `Self` for chain call
213     Self argToOpt(string optName, string[] rest...) {
214         auto tmp = [optName] ~ rest;
215         string variadic = "";
216         foreach (string key; tmp) {
217             if (variadic.length)
218                 this.error(
219                     format!"not allowed to register option `%s` after variadic option `%s` which is also registered as argument"(
220                         key, variadic
221                 ));
222             auto opt = _findOption(key);
223             if (!opt) {
224                 this.error(
225                     format!"connot register option `%s` as argument for it doesn't exist"(key));
226             }
227             if (opt.variadic)
228                 variadic = opt.flags;
229         }
230         if (this._arguments.length && this._arguments[$ - 1].variadic) {
231             this.error(
232                 format!"connot register options `%s` as arguments for the last registred argument `%s` is variadic"(
233                     tmp.to!string,
234                     this._arguments[$ - 1]._name
235             )
236             );
237         }
238         this._argToOptNames ~= tmp;
239         return this;
240     }
241 
242     /// define a sub command and sub command's arguments,
243     /// this sub command inherit the basic configuration of parent command
244     /// Params:
245     ///   nameAndArgs = like `sub <arg1> [arg2] [arg3...]`
246     ///   cmdOpts = controll the behavior,
247     ///             `isDefault` determine whether this sub command is default sub command,
248     ///             `hidden` determine whether this sub command is hidden out of help
249     /// `Args`: that is type sequence of the arguments' type sequence
250     /// Returns: the sub command that you define
251     Command command(Args...)(string nameAndArgs, bool[string] cmdOpts = null) {
252         auto cmd = createCommand!Args(nameAndArgs);
253         this._registerCommand(cmd);
254         cmd.parent = this;
255         cmd.copyInheritedSettings(this);
256         if (cmdOpts) {
257             auto is_default = "isDefault" in cmdOpts;
258             auto is_hidden = "hidden" in cmdOpts;
259             if (is_default && *is_default)
260                 this._defaultCommandName = cmd._name;
261             if (is_hidden)
262                 cmd._hidden = *is_hidden;
263         }
264         return cmd;
265     }
266 
267     /// define a sub command, which represents the external command line program
268     /// Params:
269     ///   name = only the name of this sub command
270     ///   desc = the description of this sub command
271     ///   execOpts = controll the behavior,
272     ///              `file` the file name of external command line program,
273     ///              `dir` the directory that external command line program situates on
274     ///              `help` the help option of this external command line program, which is useful when invoke the help of this sub command
275     /// Returns: `Self` for chain call
276     Self command(string name, string desc, string[string] execOpts = null) {
277         auto cmd = createCommand(name, desc);
278         cmd._execHandler = true;
279         version (Posix) {
280             cmd._execFile = format("%s-%s", this._name, cmd._name);
281         }
282         else version (Windows) {
283             cmd._execFile = format("%s-%s.exe", this._name, cmd._name);
284         }
285         cmd._execDir = dirName(thisExePath());
286         if (execOpts) {
287             if (auto exec_file = "file" in execOpts) {
288                 string tmp = strip(*exec_file);
289                 auto cp = matchFirst(tmp, PTN_EXECUTABLE);
290                 if (cp.empty)
291                     error("error format of excutable file: " ~ tmp);
292                 version (Windows) {
293                     tmp = cp[1].empty ? tmp ~ ".exe" : tmp;
294                 }
295                 else version (Posix) {
296                     tmp = !cp[1].empty ? tmp[0 .. $ - 4] : tmp;
297                 }
298                 cmd._execFile = tmp;
299             }
300             if (auto exec_dir = "dir" in execOpts)
301                 cmd._execDir = *exec_dir;
302             if (auto exec_help_flag = "help" in execOpts) {
303                 this._externalCmdHelpFlagMap[cmd._name] = *exec_help_flag;
304             }
305         }
306         if (!this._externalCmdHelpFlagMap || !(cmd._name in this._externalCmdHelpFlagMap))
307             this._externalCmdHelpFlagMap[cmd._name] = "--help";
308         cmd.usage(format!"run `%s %s --help` to see"(this._name, cmd._name));
309         cmd.parent = this;
310         cmd._allowExcessArguments = true;
311         cmd._showHelpAfterError = false;
312         cmd._showSuggestionAfterError = false;
313         cmd._combineFlagAndOptionalValue = false;
314         cmd._outputConfiguration = this._outputConfiguration;
315         cmd._helpConfiguration = null;
316         cmd.disableHelp();
317         this._registerCommand(cmd);
318         return this;
319     }
320 
321     /// add the sub command, which is often used when you want more detail configuration of sub command
322     /// Params:
323     ///   cmd = the command used as the sub command
324     ///   cmdOpts = see also `this.command(Args...)(string nameAndArgs, bool[string] cmdOpts = null)`
325     /// `Args`: see alos `this.command(Args...)(string nameAndArgs, bool[string] cmdOpts = null)`
326     /// Returns: `Self` for chain call
327     Self addCommand(Command cmd, bool[string] cmdOpts = null) {
328         if (!cmd._name.length) {
329             this.error("Command passed to .addCommand() must have a name
330                 - specify the name in Command constructor or using .name()");
331         }
332         this._registerCommand(cmd);
333         cmd.parent = this;
334         cmd.copyInheritedSettings(this);
335         if (cmdOpts) {
336             auto is_default = "isDefault" in cmdOpts;
337             auto is_hidden = "hidden" in cmdOpts;
338             if (is_default && *is_default)
339                 this._defaultCommandName = cmd._name;
340             if (is_hidden)
341                 cmd._hidden = *is_hidden;
342         }
343         return this;
344     }
345 
346     /// define the arguments' descriptions
347     /// Params:
348     ///   argName = the argument's name
349     ///   desc = the description of the argument
350     /// Returns: `Self` for chain call
351     Self argumentDesc(string argName, string desc) {
352         auto arg = _findArgument(argName);
353         if (arg)
354             arg._description = desc;
355         return this;
356     }
357 
358     /// define the arguments' descriptions
359     /// Params:
360     ///   descMap = the map of arguments' description, key is the name of argument
361     /// Returns: `Self` for chain call
362     Self argumentDesc(string[string] descMap) {
363         foreach (argName, desc; descMap) {
364             argumentDesc(argName, desc);
365         }
366         return this;
367     }
368 
369 package:
370     void _registerArgument(Argument arg) {
371         auto other = _findArgument(arg._name);
372         if (other) {
373             this.error(format!"cannot add argument `%s` as this name already used "(
374                     arg._name));
375         }
376         this._arguments ~= arg;
377     }
378 
379     void _registerCommand(Command command) {
380         auto knownBy = (Command cmd) => [cmd.name] ~ cmd.aliasNames;
381         auto alreadyUsed = knownBy(command).find!(name => this._findCommand(name));
382         if (!alreadyUsed.empty) {
383             string exit_cmd = knownBy(this._findCommand(alreadyUsed[0])).join("|");
384             string new_cmd = knownBy(command).join("|");
385             this.error(format!"cannot add command `%s` as already have command `%s`"(new_cmd, exit_cmd));
386         }
387         if (auto help_cmd = this._helpCommand) {
388             auto num = knownBy(command).count!(name => name == help_cmd._name ||
389                     cast(bool) help_cmd._aliasNames.count(name));
390             if (num) {
391                 string help_cmd_names = knownBy(help_cmd).join("|");
392                 string new_cmd_names = knownBy(command).join("|");
393                 this.error(format(
394                         "cannot add command `%s` as this command name cannot be same as " ~
395                         "the name of help command `%s`",
396                         new_cmd_names, help_cmd_names));
397             }
398         }
399         else if (this._addImplicitHelpCommand) {
400             string help_cmd_names = "help";
401             if (auto num = knownBy(command).count(help_cmd_names)) {
402                 string new_cmd_names = knownBy(command).join("|");
403                 this.error(format(
404                         "cannot add command `%s` as this command name cannot be same as " ~
405                         "the name of help command `%s`",
406                         new_cmd_names, help_cmd_names));
407             }
408         }
409         if (auto version_cmd = this._versionCommand) {
410             auto num = knownBy(command).count!(name => name == version_cmd._name || cast(bool) version_cmd
411                     ._aliasNames.count(
412                         name));
413             if (num) {
414                 string version_cmd_names = knownBy(version_cmd).join("|");
415                 string new_cmd_names = knownBy(command).join("|");
416                 this.error(format(
417                         "cannot add command `%s` as this command name cannot be same as " ~
418                         "the name of version command `%s`",
419                         new_cmd_names, version_cmd_names));
420             }
421         }
422         command.parent = this;
423         this._commands ~= command;
424     }
425 
426     void _registerOption(Option option) {
427         Option match_lopt = this._findOption(option.longFlag);
428         Option match_sopt = this._findOption(option.shortFlag);
429         NegateOption match_nopt = this._findNegateOption(option.shortFlag);
430         if (match_lopt) {
431             string match_flags = match_lopt is match_sopt ? match_lopt.flags : match_lopt.longFlag;
432             this.error(
433                 format!"Cannot add option '%s' due to conflicting flag '%s' - already ued by option '%s'"(
434                     option.flags, match_flags, match_lopt.flags
435             ));
436         }
437         if (match_sopt && match_sopt !is match_lopt) {
438             auto match_flags = option.shortFlag;
439             this.error(
440                 format!"Cannot add option '%s' due to conflicting flag '%s' - already ued by option '%s'"(
441                     option.flags, match_flags, match_sopt.flags
442             ));
443         }
444         if (match_nopt) {
445             string match_flags = match_nopt.shortFlag;
446             this.error(
447                 format!"Cannot add option '%s' due to conflicting flag '%s' - already ued by option '%s'"(
448                     option.flags, match_flags, match_nopt.flags
449             ));
450         }
451         if (auto help_option = this._helpOption) {
452             if (option.matchFlag(help_option)) {
453                 this.error(format!"Cannot add option '%s' due to confliction help option `%s`"(
454                         option.flags, help_option.flags));
455             }
456         }
457         else if (this._addImplicitHelpOption && (option.shortFlag == "-h" || option.longFlag == "--help")) {
458             this.error(format!"Cannot add option '%s' due to confliction help option `%s`"(option.flags, "-h, --help"));
459         }
460         if (auto version_option = this._versionOption) {
461             if (option.matchFlag(version_option)) {
462                 this.error(format!"Cannot add option '%s' due to confliction version option `%s`"(
463                         option.flags, version_option.flags));
464             }
465         }
466         if (auto config_option = this._configOption) {
467             if (option.matchFlag(config_option)) {
468                 this.error(format!"Cannot add option '%s' due to confliction config option `%s`"(
469                         option.flags, config_option.flags));
470             }
471         }
472         this._options ~= option;
473     }
474 
475     void _registerOption(NegateOption option) {
476         NegateOption match_lopt = this._findNegateOption(option.longFlag);
477         NegateOption match_sopt = this._findNegateOption(option.shortFlag);
478         Option match_opt = this._findOption(option.shortFlag);
479         if (match_lopt) {
480             string match_flags = match_lopt is match_sopt ? match_lopt.flags : match_lopt.longFlag;
481             this.error(
482                 format!"Cannot add option '%s' due to conflicting flag '%s' - already ued by option '%s'"(
483                     option.flags, match_flags, match_lopt.flags
484             ));
485         }
486         if (match_sopt && match_sopt !is match_lopt) {
487             auto match_flags = option.shortFlag;
488             this.error(
489                 format!"Cannot add option '%s' due to conflicting flag '%s' - already ued by option '%s'"(
490                     option.flags, match_flags, match_sopt.flags
491             ));
492         }
493         if (match_opt) {
494             auto match_flags = match_opt.shortFlag;
495             this.error(
496                 format!"Cannot add option '%s' due to conflicting flag '%s' - already ued by option '%s'"(
497                     option.flags, match_flags, match_opt.flags
498             ));
499         }
500         if (auto help_option = this._helpOption) {
501             if (option.matchFlag(help_option))
502                 this.error(format!"Cannot add negate-option '%s' due to confliction help option `%s`"(
503                         option.flags, help_option.flags));
504         }
505         else if (this._addImplicitHelpOption && (option.shortFlag == "-h" || option.longFlag == "--no-help")) {
506             this.error(format!"Cannot add negate-option '%s' due to confliction help option `%s`"(
507                     option.flags, "-h, --help"));
508         }
509         if (auto version_option = this._versionOption) {
510             if (option.matchFlag(version_option))
511                 this.error(format!"Cannot add negate-option '%s' due to confliction version option `%s`"(
512                         option.flags, version_option.flags));
513         }
514         if (auto config_option = this._configOption) {
515             if (option.matchFlag(config_option)) {
516                 this.error(format!"Cannot add negate-option '%s' due to confliction config option `%s`"(
517                         option.flags, config_option.flags));
518             }
519         }
520         this._negates ~= option;
521     }
522 
523 public:
524     /// add the option to command
525     Self addOption(Option option) {
526         if (!this._allowVariadicMerge)
527             option.merge(false);
528         this._registerOption(option);
529         bool is_required = option.isRequired;
530         bool is_optional = option.isOptional;
531         bool is_variadic = option.variadic;
532         string name = option.name;
533         if (is_required) {
534             if (is_variadic) {
535                 this.on("option:" ~ name, (string[] vals) {
536                     if (vals.length == 0) {
537                         this.parsingError(
538                             format!"the value's num of variadic option `%s` cannot be zero"(name));
539                     }
540                     setOptionVal!(Source.Cli)(name, vals);
541                 });
542             }
543             else {
544                 this.on("option:" ~ name, (string val) {
545                     setOptionVal!(Source.Cli)(name, val);
546                 });
547             }
548         }
549         else if (is_optional) {
550             if (is_variadic) {
551                 this.on("option:" ~ name, (string[] vals) {
552                     if (vals.length)
553                         setOptionVal!(Source.Cli)(name, vals);
554                     else
555                         option.found = true;
556                 });
557             }
558             else {
559                 this.on("option:" ~ name, (string val) {
560                     setOptionVal!(Source.Cli)(name, val);
561                 });
562                 this.on("option:" ~ name, () { option.found = true; });
563             }
564         }
565         else {
566             this.on("option:" ~ name, () { option.found = true; });
567         }
568         return this;
569     }
570 
571     /// add the negate option to command
572     Self addOption(NegateOption option) {
573         auto opt = _findOption(option.name);
574         if (!opt) {
575             opt = createOption("--" ~ option.name, "see also option " ~ option.flags).defaultVal();
576             _registerOption(opt);
577             _registerOption(option);
578             this.on("negate:" ~ option.name, () {
579                 setOptionValDirectly(option.name, false, Source.Cli);
580             });
581         }
582         else {
583             _registerOption(option);
584             if (opt.isBoolean) {
585                 opt.defaultVal();
586                 this.on("negate:" ~ option.name, () {
587                     setOptionValDirectly(option.name, false, Source.Cli);
588                 });
589             }
590             else
591                 this.on("negate:" ~ option.name, () {
592                     this._options = this._options.remove!(ele => ele is opt);
593                     this._abandons ~= opt;
594                 });
595         }
596         return this;
597     }
598 
599     /// add the action option to command, which will invoke the callback we injected when parsing the flag of this option, only useful in client cmd
600     Self addActionOption(Option option, void delegate(string[] vals...) call_back, bool endMode = true) {
601         if (!this._allowVariadicMerge)
602             option.merge(false);
603         this._registerOption(option);
604         string name = option.name;
605         this.on("option:" ~ name, () {
606             call_back();
607             if (endMode)
608                 this._exitSuccessfully();
609         });
610         this.on("option:" ~ name, (string str) {
611             call_back(str);
612             if (endMode)
613                 this._exitSuccessfully();
614         });
615         this.on("option:" ~ name, (string[] strs) {
616             call_back(strs);
617             if (endMode)
618                 this._exitSuccessfully();
619         });
620         return this;
621     }
622 
623     package Self _optionImpl(T, bool isMandatory = false)(string flags, string desc)
624             if (isOptionValueType!T) {
625         auto option = createOption!T(flags, desc);
626         option.makeMandatory(isMandatory);
627         return this.addOption(option);
628     }
629 
630     package Self _optionImpl(string flags, string desc) {
631         auto lflag = splitOptionFlags(flags).longFlag;
632         auto is_negate = !matchFirst(lflag, PTN_NEGATE).empty;
633         if (is_negate) {
634             auto nopt = createNOption(flags, desc);
635             return this.addOption(nopt);
636         }
637         else {
638             auto opt = createOption(flags, desc);
639             return this.addOption(opt);
640         }
641     }
642 
643     /// define a value/variadic option to this command
644     Self option(T)(string flags, string desc) {
645         return _optionImpl!(T)(flags, desc);
646     }
647 
648     /// define a bool option to this command
649     Self option(string flags, string desc) {
650         return _optionImpl(flags, desc);
651     }
652 
653     /// define a mandatory value/variadic option to this command
654     Self requiredOption(T)(string flags, string desc) {
655         return _optionImpl!(T, true)(flags, desc);
656     }
657 
658     /// define a mandatory bool option to this command
659     Self requireOption(string flags, string desc) {
660         return _optionImpl!(bool, true)(flags, desc);
661     }
662 
663     package Self _optionImpl(T, bool isMandatory = false)(string flags, string desc, T defaultValue)
664             if (isOptionValueType!T) {
665         auto option = createOption!T(flags, desc);
666         option.makeMandatory(isMandatory);
667         option.defaultVal(defaultValue);
668         return this.addOption(option);
669     }
670 
671     /// see also `Self option(T)(string flags, string desc)` and define a default value for this option
672     Self option(T)(string flags, string desc, T defaultValue) {
673         return _optionImpl!T(flags, desc, defaultValue);
674     }
675 
676     /// see also `Self requiredOption(T)(string flags, string desc)` and define a default value for this option
677     Self requiredOption(T)(string flags, string desc, T defaultValue) {
678         return _optionImpl!(T, true)(flags, desc, defaultValue);
679     }
680 
681     /// expose its option values by names to its sub or sub sub and etc command.
682     /// its sub or sub sub and etc command can access these option values using `Command.injects` by names.
683     /// remember that it is only useful for non-builtin options!
684     /// Params:
685     ///   name = the name of option to be exposed
686     ///   rest = the rest names of options to be exposed
687     /// Returns: `Self` for chain call
688     Self provides(in string name, in string[] rest...) {
689         auto tmp = [name] ~ rest;
690         foreach (string n; tmp) {
691             if (n.length == 0 || n[1] == '-') {
692                 error(format!"the option name `\"%s\"` is not in legacy in `Command.provides`"(n));
693             }
694         }
695         if (tmp.any!(str => this._findOption(str) is null)) {
696             this.error(format!"the non-builtin option names `%s` you provide doesn't all exist in `Command.provides`"(
697                     tmp.to!string
698             ));
699         }
700         this._provide_arr.each!((str) {
701             if (str.canFind(':') && name.length < str.length
702             && (name == str[$ - name.length .. $] || name == str[0 .. name.length]))
703                 error(format!"the option name `%s` has been registered to be exposed as `%s`, you cannot register it again in `Command.provides`"(
704                     name, str
705                 ));
706         });
707         this._provide_arr = (tmp ~ this._provide_arr).uniq.array;
708         return this;
709     }
710 
711     /// see also `Self provides(string name, string[] rest...)`.
712     /// this member function are used for register an option to be exposed as an new name.
713     /// this is usually used when you want to avoid name confilict.
714     /// Params:
715     ///   name = the name of option to be exposed
716     ///   asName = the new name of option to be exposed
717     /// Returns: `Self` for chain call
718     Self providesAs(in string name, in string asName) {
719         if (name.length == 0 || name[1] == '-') {
720             error(format!"the option name `\"%s\"` is not in legacy in `Command.providesAs`"(name));
721         }
722         if (asName.length == 0 || asName[1] == '-') {
723             error(format!"the option as-name `\"%s\"` is not in legacy in `Command.providesAs`"(
724                     asName));
725         }
726         if (this._findOption(name) is null) {
727             this.error(
728                 format!"the non-builtin option name `%s` you provide doesn't exist in `Command.providesAs`"(
729                     name));
730         }
731         if (this._findOption(asName) !is null) {
732             this.error(
733                 format!"the as-name `%s` has been an option's name, you cannot make it as the name of option `%s` in `Command.providesAs`"(
734                     asName, name
735             ));
736         }
737         if (this._provide_arr.count(name) > 0) {
738             error(format!"the option name `%s` has been registered to be exposed, you cannot register it again in `Command.providesAs`"(
739                     name));
740         }
741         if (this._provide_arr.count(asName) > 0) {
742             error(format!"the option as-name `%s` has been registered to be exposed, you cannot register it again in `Command.providesAs`"(
743                     asName));
744         }
745         this._provide_arr.each!((str) {
746             if (str.canFind(':') && asName.length < str.length && asName == str[0 .. asName.length])
747                 error(format!"the option as-name `%s` has been registered to be exposed as `%s`, you cannot register it again in `Command.providesAs`"(
748                     asName, str
749                 ));
750         });
751         this._provide_arr.each!((str) {
752             if (str.canFind(':') && name.length < str.length && name == str[$ - name.length .. $])
753                 error(format!"the option name `%s` has been registered to be exposed as `%s`, you cannot register it again in `Command.providesAs`"(
754                     name, str
755                 ));
756         });
757         this._provide_arr ~= asName ~ ':' ~ name;
758         return this;
759     }
760 
761     /// see also `Self providesAs(in string name, in string asName)`.
762     /// expose option values in new names at the same time
763     /// Params:
764     ///   optsMap = the map of name and asName
765     /// Returns: `Self` for chain call
766     Self providesAs(in string[string] optsMap) {
767         foreach (name, asName; optsMap) {
768             this.providesAs(name, asName);
769         }
770         return this;
771     }
772 
773     /// inject the options' values exposed by ancestor command by name that are registered
774     /// using `Command.provides` or `Command.providesAs`, so that we can access this option value.
775     /// Params:
776     ///   name = the name of option to be injected
777     ///   rest = the rest name of option to be injected
778     /// Returns: `Self` for chain call
779     Self injects(in string name, in string[] rest...) {
780         auto tmp = ([name] ~ rest).uniq.array;
781         foreach (string n; tmp) {
782             if (n.length == 0 || n[1] == '-') {
783                 error(format!"the option name `\"%s\"` is not in legacy in `Command.injects`"(n));
784             }
785         }
786         foreach (str; tmp) {
787             if (_findOption(str) !is null)
788                 error(format!"connot inject option `%s` for this option has been exist in its option list using `Command.injects`"(
789                         str));
790         }
791         Command[] ancestors = this._getCommandAndAncestors()[1 .. $];
792         auto get_front = () => tmp.length > 0 ? tmp.front : "";
793         while (true) {
794             string str = get_front();
795             if (str == "")
796                 break;
797             this._inject_arr.each!((istr) {
798                 if (istr.canFind(':') && str.length < istr.length
799                 && (str == istr[$ - str.length .. $] || str == istr[0 .. str.length])) {
800                     error(format!"the option name `%s` has been injected as `%s`, you cannot register it again in `Command.injects`"(
801                         str, istr
802                     ));
803                 }
804             });
805             foreach (Command cmd; ancestors) {
806                 auto provide_arr = cmd._provide_arr;
807                 if (provide_arr.any!((pstr) {
808                         if (str.length <= pstr.length && str == pstr[0 .. str.length])
809                             return true;
810                         else
811                             return false;
812                     })) {
813                     this._inject_arr ~= str;
814                     tmp.popFront;
815                     break;
816                 }
817             }
818             if (str == get_front())
819                 error(format!"cannnot inject the name `%s` for there is not any option match option name the ancestors provided using `Command.injects`"(
820                         str));
821         }
822         this._inject_arr = this._inject_arr.uniq.array;
823         return this;
824     }
825 
826     /// see also `Self injects(string name, string[] rest...)`.
827     /// this member function are used for inject an option value as an new name.
828     /// this is usually used when you want to avoid name confilict.
829     /// Params:
830     ///   name = the name of option to be injected
831     ///   asName = the new name of option to be injected
832     /// Returns: `Self` for chain call
833     Self injectsAs(in string name, in string asName) {
834         if (name.length == 0 || name[1] == '-') {
835             error(format!"the option name `\"%s\"` is not in legacy in `Command.injectsAs`"(name));
836         }
837         if (asName.length == 0 || asName[1] == '-') {
838             error(format!"the option asName `\"%s\"` is not in legacy in `Command.injectsAs`"(
839                     asName));
840         }
841         if (this._findOption(asName) !is null) {
842             this.error(
843                 format!"the as-name `%s` has been an option's name, you cannot make it as the name of option `%s` in `Command.injectsAs`"(
844                     asName, name
845             ));
846         }
847         if (this._inject_arr.count(name) > 0)
848             error(format!"the option name `%s` has been injected, you cannot inject it again in `Command.injectsAs`"(
849                     name));
850         if (this._inject_arr.count(asName) > 0)
851             error(format!"the option name `%s` has been injected, you cannot inject it again in `Command.injectsAs`"(
852                     asName));
853         this._inject_arr.each!((str) {
854             if (str.canFind(':') && asName.length < str.length && asName == str[0 .. asName.length])
855                 error(format!"the option as-name `%s` has been injected as `%s`, you cannot injected it again in `Command.providesAs`"(
856                     asName, str
857                 ));
858         });
859         this._inject_arr.each!((str) {
860             if (str.canFind(':') && name.length < str.length && name == str[0 .. name.length])
861                 error(format!"the option name `%s` has been injected as `%s`, you cannot injected it again in `Command.providesAs`"(
862                     name, str
863                 ));
864         });
865         Command[] ancestors = this._getCommandAndAncestors()[1 .. $];
866         bool flag = false;
867         foreach (Command cmd; ancestors) {
868             auto provide_arr = cmd._provide_arr;
869             auto str = name;
870             if (provide_arr.any!((pstr) {
871                     if (str.length <= pstr.length && str == pstr[0 .. str.length])
872                         return true;
873                     else
874                         return false;
875                 })) {
876                 flag = true;
877                 break;
878             }
879         }
880         if (!flag)
881             error(format!"cannnot inject the name `%s` for there is not any option match option name the ancestors provided using `Command.injectsAs`"(
882                     name));
883         this._inject_arr ~= asName ~ ':' ~ name;
884         return this;
885     }
886 
887     /// see also `Self injectsAs(in string name, in string asName)`.
888     /// inject option values in new names at the same time
889     /// Params:
890     ///   optsMap = the map of name and asName
891     /// Returns: `Self` for chain call
892     Self injectsAs(in string[string] optsMap) {
893         foreach (name, asName; optsMap) {
894             injectsAs(name, asName);
895         }
896         return this;
897     }
898 
899 package:
900     Self setOptionVal(Source src, T)(string key, T value) if (isOptionValueType!T) {
901         Option opt = this._findOption(key);
902         if (!opt) {
903             this.parsingError(format!"option `%s` doesn't exist"(key));
904         }
905         switch (src) {
906         case Source.Default:
907             opt.defaultVal(value);
908             break;
909         case Source.Config:
910             opt.configVal(value);
911             break;
912         case Source.Imply:
913             opt.implyVal(value);
914             break;
915         case Source.Preset:
916             opt.preset(value);
917             break;
918         default:
919             this.error;
920             break;
921         }
922         return this;
923     }
924 
925     Self setOptionVal(Source src)(string key) {
926         auto opt = this._findOption(key);
927         if (!opt) {
928             this.parsingError(format!"option `%s` doesn't exist"(key));
929         }
930         switch (src) {
931         case Source.Default:
932             opt.defaultVal();
933             break;
934         case Source.Config:
935             opt.configVal();
936             break;
937         case Source.Imply:
938             opt.implyVal();
939             break;
940         case Source.Preset:
941             opt.preset();
942             break;
943         default:
944             this.error;
945             break;
946         }
947         return this;
948     }
949 
950     Self setOptionVal(Source src : Source.Env)(string key) {
951         auto opt = this._findOption(key);
952         if (!opt) {
953             this.parsingError(format!"option `%s` doesn't exist"(key));
954         }
955         opt.envVal();
956         return this;
957     }
958 
959     Self setOptionVal(Source src : Source.Cli, T:
960         string)(string key, T value, T[] rest...) {
961         auto opt = this._findOption(key);
962         if (!opt) {
963             this.parsingError(format!"option `%s` doesn't exist"(key));
964         }
965         opt.cliVal(value, rest);
966         opt.found = true;
967         return this;
968     }
969 
970     Self setOptionVal(Source src : Source.Cli, T:
971         string)(string key, T[] values) {
972         if (values.length == 0) {
973             this.parsingError(
974                 format!"the value's num of option `%s` cannot be zero"(key));
975         }
976         return this.setOptionVal!src(key, values[0], values[1 .. $]);
977     }
978 
979     Self setOptionValDirectly(T)(string key, T value, Source src = Source.None)
980             if (isOptionValueType!T) {
981         auto opt = this._findOption(key);
982         if (!opt) {
983             this.parsingError(format!"option `%s` doesn't exist"(key));
984         }
985         static if (!is(ElementType!T U == void) && !is(T == string)) {
986             VariadicOption!U derived = cast(VariadicOption!U) opt;
987             if (!derived) {
988                 error(format!"connot set value `%s` in option `%s` directly using `Command.setOptionValDirectly`"(
989                         value.to!string,
990                         opt.flags
991                 ));
992             }
993             derived.innerValueData = value;
994             derived.isValueData = true;
995             derived.source = src;
996             derived.settled = true;
997         }
998         else {
999             ValueOption!T derived = cast(ValueOption!T) opt;
1000             if (!derived) {
1001                 error(format!"connot set value `%s` in option `%s` directly using `Command.setOptionValDirectly`"(
1002                         value.to!string,
1003                         opt.flags
1004                 ));
1005             }
1006             derived.innerValueData = value;
1007             derived.isValueData = true;
1008             derived.source = src;
1009             derived.settled = true;
1010         }
1011         return this;
1012     }
1013 
1014     Self setOptionValDirectly(T)(string key, Source src = Source.None)
1015             if (isOptionValueType!T) {
1016         auto opt = this._findOption(key);
1017         if (!opt) {
1018             this.parsingError(format!"option `%s` doesn't exist"(key));
1019         }
1020         if (!opt.isOptional) {
1021             this.parsingError(format!"option `%s` must be optional"(key));
1022         }
1023         static if (!is(ElementType!T U == void) && !is(T == string)) {
1024             VariadicOption!U derived = cast(VariadicOption!U) opt;
1025             if (!derived) {
1026                 error(format!"connot set value `true` in option `%s` directly using `Command.setOptionValDirectly`"(
1027                         opt.flags
1028                 ));
1029             }
1030             derived.innerBoolData = true;
1031             derived.isValueData = false;
1032             derived.source = src;
1033             derived.settled = true;
1034         }
1035         else {
1036             ValueOption!T derived = cast(ValueOption!T) opt;
1037             if (!derived) {
1038                 error(format!"connot set value `true` in option `%s` directly using `Command.setOptionValDirectly`"(
1039                         opt.flags
1040                 ));
1041             }
1042             derived.innerBoolData = true;
1043             derived.isValueData = false;
1044             derived.source = src;
1045             derived.settled = true;
1046         }
1047         return this;
1048     }
1049 
1050     Self setOptionValDirectly(string key, bool value = true, Source src = Source.None) {
1051         auto opt = this._findOption(key);
1052         if (!opt) {
1053             this.parsingError(format!"option `%s` doesn't exist"(key));
1054         }
1055         BoolOption derived = cast(BoolOption) opt;
1056         if (!derived) {
1057             error(format!"connot set value `%s` in option `%s` directly using `Command.setOptionValDirectly`"(
1058                     value.to!string,
1059                     opt.flags
1060             ));
1061         }
1062         derived.innerBoolData = value;
1063         derived.isValueData = false;
1064         derived.source = src;
1065         derived.settled = true;
1066         return this;
1067     }
1068 
1069 public:
1070     /// get the inner value of a option by name wrapped by `ArgWrap`.
1071     /// remember better use it in action callabck or after parsing
1072     /// Params:
1073     ///   key = the name of option
1074     /// Returns: the value wrapped by `ArgWrap`, which may be empty when cannot find the option
1075     ArgWrap getOptVal(string key) const {
1076         if (!this.opts) {
1077             this.error("the options has not been initialized, cannnot get the value of option now");
1078         }
1079         auto ptr = key in this.opts;
1080         return ptr ? ArgWrap(*ptr) : ArgWrap(null);
1081     }
1082 
1083     /// get the inner value in specified type of an option by flags
1084     /// Params:
1085     ///   key = the long|short flag or the name of the option 
1086     /// Returns: the inner value not wrapped
1087     T getOptVal(T)(string key) const {
1088         auto opt = this.findOption(key);
1089         if (!opt) {
1090             this.error(format!"connot find option `%s` when try to get its value of type `%s`"(
1091                     opt.flags,
1092                     T.stringof
1093             ));
1094         }
1095         return opt.get!T;
1096     }
1097 
1098     // ArgWrap getOptionValWithGlobal(string key) const {
1099     //     auto cmds = this._getCommandAndAncestors();
1100     //     foreach (cmd; cmds) {
1101     //         if (cmd.opts && key in cmd.opts)
1102     //             return ArgWrap(this.opts[key]);
1103     //         auto opt = this._findOption(key);
1104     //         if (!opt) {
1105     //             this.error(format!"option `%s` doesn't exist"(key));
1106     //         }
1107     //         return ArgWrap(opt.get);
1108     //     }
1109     //     this.error(format!"cannot get the option `%s`'s value"(key));
1110     //     assert(0);
1111     // }
1112 
1113     /// get the source of option
1114     /// remember better use it in action callabck or after parsing
1115     /// Params:
1116     ///   key = the flag of option
1117     /// Returns: the source of option final value in `Source`
1118     Source getOptionValSource(string key) const {
1119         auto opt = this._findOption(key);
1120         if (!opt || !opt.settled) {
1121             this.error(format!"option `%s` doesn't exist or not settled"(key));
1122         }
1123         return opt.source;
1124     }
1125 
1126     // Source getOptionValWithGlobalSource(string key) const {
1127     //     auto cmds = this._getCommandAndAncestors();
1128     //     foreach (cmd; cmds) {
1129     //         auto opt = this._findOption(key);
1130     //         if (!opt) {
1131     //             this.error(format!"option `%s` doesn't exist"(key));
1132     //         }
1133     //         return opt.source;
1134     //     }
1135     //     this.error(format!"cannot get the option `%s`'s source"(key));
1136     //     assert(0);
1137     // }
1138 
1139     package void execSubCommand(in string[] unknowns) {
1140         string sub_path = buildPath(_execDir, _execFile);
1141         const(string)[] inputs;
1142         if (!(sub_path[0] == '"' && sub_path[$ - 1] == '"') && sub_path.any!(c => c == ' ')) {
1143             sub_path = '"' ~ sub_path ~ '"';
1144         }
1145         unknowns.each!((str) {
1146             if (!(str[0] == '"' && str[$ - 1] == '"') && str.any!(c => c == ' ')) {
1147                 string new_str = '"' ~ str ~ '"';
1148                 inputs ~= new_str;
1149             }
1150             else {
1151                 inputs ~= str;
1152             }
1153         });
1154         auto result = executeShell(sub_path ~ " " ~ inputs.join(" "));
1155         if (result.status == 0) {
1156             this._outputConfiguration.writeOut(result.output);
1157             this._exitSuccessfully();
1158         }
1159         else {
1160             this._outputConfiguration.writeErr(result.output);
1161             this._exit(1);
1162         }
1163     }
1164 
1165     /// parse the command line argument variables
1166     /// Params:
1167     ///   argv = the command line argument variables
1168     void parse(in string[] argv) {
1169         auto user_argv = _prepareUserArgs(argv);
1170         try {
1171             _parseCommand(user_argv);
1172         }
1173         catch (InvalidArgumentError e) {
1174             parsingError(e.msg, e.code);
1175         }
1176         catch (InvalidOptionError e) {
1177             parsingError(e.msg, e.code);
1178         }
1179     }
1180 
1181 package:
1182     string[] _prepareUserArgs(in string[] args) {
1183         auto arr = args.filter!(str => str.length).array;
1184         this._selfPath = arr[0];
1185         this.rawFlags = arr.dup;
1186         return args[1 .. $].dup;
1187     }
1188 
1189     void _parseCommand(in string[] unknowns) {
1190         auto has_cmd = (const string str) {
1191             auto _cmd = _findCommand(str);
1192             auto vcmd = this._versionCommand;
1193             auto hcmd = this._helpCommand;
1194             _cmd = !_cmd && vcmd && vcmd._name == str ? vcmd : _cmd;
1195             _cmd = !_cmd && hcmd && hcmd._name == str ? hcmd : _cmd;
1196             return _cmd ? true : (this._addImplicitHelpCommand && str == "help");
1197         };
1198         auto has_hvopt = (const string str) {
1199             auto vopt = this._versionOption;
1200             auto hopt = this._helpOption;
1201             auto _opt = vopt && vopt.isFlag(str) ? vopt : null;
1202             _opt = !_opt && hopt && hopt.isFlag(str) ? hopt : null;
1203             return _opt ? true : (this._addImplicitHelpOption &&
1204                     (str == "--help" || str == "-h"));
1205         };
1206         if (!this._defaultCommandName.empty &&
1207             unknowns.all!(str => !has_cmd(str) && !has_hvopt(str))) {
1208             auto cmd = _findCommand(this._defaultCommandName);
1209             if (!cmd)
1210                 parsingError("cannot find the default sub command `"
1211                         ~ this._defaultCommandName ~ "`");
1212             if (this._configOption && !cmd._execHandler) {
1213                 auto j_config = _processConfigFile();
1214                 if (j_config.length) {
1215                     auto cmd_name = cmd._name;
1216                     const(JSONValue)*[] tmp = [];
1217                     foreach (v; j_config) {
1218                         auto jtmp = cmd_name in *v;
1219                         if (jtmp)
1220                             tmp ~= jtmp;
1221                     }
1222                     cmd.jconfig = tmp;
1223                 }
1224             }
1225             this._called_sub = cmd._name;
1226             cmd._parseCommand(unknowns);
1227             return;
1228         }
1229         this.parseOptionsEnv();
1230         auto parsed = this.parseOptions(unknowns);
1231         this.argFlags = parsed[0];
1232         this.unknownFlags = parsed[1];
1233         this.parseOptionsConfig();
1234         this.parseOptionsImply();
1235         this._options
1236             .filter!(opt => opt.settled || opt.isValid)
1237             .each!((opt) { opt.initialize; });
1238         this.parseArguments(parsed[0]);
1239         if (this._argToOptNames.length > 0) {
1240             this._options
1241                 .filter!(opt => opt.settled || opt.isValid)
1242                 .each!((opt) { opt.initialize; });
1243         }
1244         _checkConfilctOption();
1245         _checkMissingMandatoryOption();
1246         this.opts = this._options
1247             .filter!(opt => opt.settled)
1248             .map!(opt => tuple(opt.name, opt.get))
1249             .assocArray;
1250         if (this.parent && this.parent._allowExposeOptionValue) {
1251             auto popts = this.parent.opts;
1252             foreach (string pkey, ref OptionVariant pvalue; popts) {
1253                 this.opts[':' ~ pkey] = pvalue;
1254             }
1255         }
1256         if (!this._inject_arr.empty) {
1257             foreach (string str; _inject_arr) {
1258                 string key, fkey;
1259                 if (str.canFind(':')) {
1260                     auto tmp = str.split(':').array;
1261                     key = tmp[0];
1262                     fkey = tmp[1];
1263                 }
1264                 else
1265                     key = fkey = str;
1266                 auto ancestors = this._getCommandAndAncestors()[1 .. $];
1267                 foreach (Command cmd; ancestors) {
1268                     auto provide_arr = cmd._provide_arr;
1269                     provide_arr.each!((const str) {
1270                         string pkey, pfkey;
1271                         if (str.canFind(':')) {
1272                             auto tmp = str.split(':').array;
1273                             pkey = tmp[0];
1274                             pfkey = tmp[1];
1275                         }
1276                         else
1277                             pkey = pfkey = str;
1278                         if (fkey == pkey)
1279                             this.opts[key] = cmd.opts[pfkey];
1280                     });
1281                 }
1282             }
1283         }
1284         if (this.subCommand && this.subCommand._execHandler) {
1285             this._called_sub = this.subCommand._name;
1286             this.subCommand.execSubCommand(parsed[1]);
1287         }
1288         if (this.subCommand) {
1289             this._called_sub = this.subCommand._name;
1290             this.subCommand._parseCommand(parsed[1]);
1291         }
1292         else {
1293             this.emit("action:" ~ this._name);
1294         }
1295     }
1296 
1297     private
1298     mixin template InitAfterDash(alias _args, alias get_front, alias find_cmd, alias operands, alias unknowns) {
1299         import std.range : popFront;
1300 
1301         auto __xfn__InitFaterDash = {
1302             auto value = get_front(_args);
1303             if (value.length == 0) {
1304                 this.parsingError(
1305                     "cannot end with `--`");
1306             }
1307             auto cmd = find_cmd(value);
1308             while (!cmd && value.length) {
1309                 operands ~= value;
1310                 popFront(_args);
1311                 value = get_front(_args);
1312                 cmd = find_cmd(value);
1313             }
1314             if (cmd) {
1315                 this.subCommand = cmd;
1316                 popFront(_args);
1317                 if (subCommand.immediately)
1318                     subCommand._parseCommand(_args);
1319                 unknowns ~= _args;
1320             }
1321             return 1;
1322         }();
1323         // auto x = __xfn__xfn__InitFaterDash();
1324     }
1325 
1326     private
1327     mixin template InitVariadicOpt(alias _args, alias get_front, alias maybe_opt, alias vvm, alias name) {
1328         import std.range : empty, popFront;
1329 
1330         auto __xfn_InitVariadicOpt = {
1331             auto value = get_front(_args);
1332             string[] tmps = [];
1333             while (value.length && !maybe_opt(value)) {
1334                 tmps ~= value;
1335                 popFront(_args);
1336                 value = get_front(_args);
1337             }
1338             auto ptr = name in vvm;
1339             if (ptr)
1340                 *ptr ~= tmps;
1341             else
1342                 vvm[name] = tmps;
1343             return 1;
1344         }();
1345     }
1346 
1347     private
1348     mixin template InitValueOpt(alias cmd, bool requried, alias _args,
1349         alias get_front, alias maybe_opt, alias opt, alias name) {
1350         import std.range : empty, popFront;
1351 
1352         static if (requried) {
1353             auto __xfn_InitValueOpt = {
1354                 auto value = get_front(_args);
1355                 if (value.empty || maybe_opt(value))
1356                     cmd.optionMissingArgument(opt);
1357                 cmd.emit("option:" ~ name, value);
1358                 if (cmd !is this) {
1359                     opt.settled = false;
1360                 }
1361                 _args.popFront;
1362                 return 1;
1363             }();
1364         }
1365         else {
1366             auto __xfn_InitValueOpt = {
1367                 auto value = get_front(_args);
1368                 if (value.empty || maybe_opt(value))
1369                     cmd.emit("option:" ~ name);
1370                 else {
1371                     cmd.emit("option:" ~ name, value);
1372                     _args.popFront;
1373                 }
1374                 if (cmd !is this) {
1375                     opt.settled = false;
1376                 }
1377                 return 1;
1378             }();
1379         }
1380     }
1381 
1382     private
1383     mixin template InitOptComb(alias cmd, alias _args, alias opt, alias vvm, alias arg) {
1384         import std.array : insertInPlace;
1385         import std.format;
1386         import cmdline.option;
1387 
1388         auto __xfn_InitOptComp = {
1389             string name = opt.name;
1390             if (opt.isRequired || opt.isOptional) {
1391                 bool is_variadic = opt.variadic;
1392                 if (!is_variadic)
1393                     cmd.emit("option:" ~ name, arg[2 .. $]);
1394                 else {
1395                     auto ptr = name in vvm;
1396                     if (ptr)
1397                         *ptr ~= arg[2 .. $];
1398                     else
1399                         vvm[name] = [arg[2 .. $]];
1400                 }
1401             }
1402             else if (cmd._combineFlagAndOptionalValue) {
1403                 cmd.emit("option:" ~ name);
1404                 _args.insertInPlace(0, "-" ~ arg[2 .. $]);
1405             }
1406             else {
1407                 cmd.parsingError(format!"invalid value: `%s` for bool option `%s`"(
1408                         arg[2 .. $],
1409                         opt.flags
1410                 ));
1411             }
1412             if (cmd !is this) {
1413                 opt.settled = false;
1414             }
1415             return 1;
1416         }();
1417     }
1418 
1419     private
1420     mixin template InitOptAssign(alias cmd, alias opt, alias vvm, alias value) {
1421         import cmdline.option;
1422 
1423         auto __xfn_InitOptAssign = {
1424             string name = opt.name;
1425             if (opt.isRequired || opt.isOptional) {
1426                 bool is_variadic = opt.variadic;
1427                 if (!is_variadic)
1428                     cmd.emit("option:" ~ name, value);
1429                 else {
1430                     auto ptr = name in vvm;
1431                     if (ptr)
1432                         *ptr ~= value;
1433                     else
1434                         vvm[name] = [value];
1435                 }
1436             }
1437             else
1438                 cmd.parsingError("invalid value: `" ~ value ~ "` for bool option " ~ opt.flags);
1439             if (cmd !is this) {
1440                 opt.settled = false;
1441             }
1442             return 1;
1443         }();
1444     }
1445 
1446     private bool maybe_opt(string str) {
1447         return (str.length > 1 && str[0] == '-' &&
1448                 (str[1] == '-' || (str[1] >= 'A' && str[1] <= 'Z') ||
1449                     (str[1] >= 'a' && str[1] <= 'z')));
1450     }
1451 
1452     private string get_front(string[] strs) {
1453         return strs.empty ? "" : strs.front;
1454     }
1455 
1456     private bool init_opt(Command cmd, string arg, ref string[] _args, ref string[][string] vvm) {
1457         bool is_continue = false;
1458         if (auto opt = cmd._findOption(arg)) {
1459             auto name = opt.name;
1460             bool is_variadic = opt.variadic;
1461             if (opt.isRequired) {
1462                 if (!is_variadic) {
1463                     mixin InitValueOpt!(cmd, true, _args, get_front, maybe_opt, opt, name);
1464                 }
1465                 else {
1466                     mixin InitVariadicOpt!(_args, get_front, maybe_opt, vvm, name);
1467                 }
1468             }
1469             else if (opt.isOptional) {
1470                 if (!is_variadic) {
1471                     mixin InitValueOpt!(cmd, false, _args, get_front, maybe_opt, opt, name);
1472                 }
1473                 else {
1474                     mixin InitVariadicOpt!(_args, get_front, maybe_opt, vvm, name);
1475                 }
1476             }
1477             else {
1478                 cmd.emit("option:" ~ name);
1479             }
1480             is_continue = true;
1481         }
1482         if (auto nopt = cmd._findNegateOption(arg)) {
1483             cmd.emit("negate:" ~ nopt.name);
1484             is_continue = true;
1485         }
1486         return is_continue;
1487     }
1488 
1489     private bool init_comb(Command cmd, string flag, string arg, ref string[] _args, ref string[][string] vvm) {
1490         bool is_continue = false;
1491         if (Option opt = cmd._findOption(flag)) {
1492             mixin InitOptComb!(cmd, _args, opt, vvm, arg);
1493             is_continue = true;
1494         }
1495         if (NegateOption nopt = cmd._findNegateOption(flag)) {
1496             if (cmd._combineFlagAndOptionalValue) {
1497                 cmd.emit("negate:" ~ nopt.name);
1498                 _args.insertInPlace(0, "-" ~ arg[2 .. $]);
1499             }
1500             else {
1501                 cmd.parsingError(format!"invalid value: `%s` for negate option `%s`"(
1502                         arg[2 .. $],
1503                         nopt.flags
1504                 ));
1505             }
1506             is_continue = true;
1507         }
1508         return is_continue;
1509     }
1510 
1511     private bool init_assign(Command cmd, string flag, string value, ref string[][string] vvm) {
1512         bool is_continue = false;
1513         if (Option opt = cmd._findOption(flag)) {
1514             mixin InitOptAssign!(cmd, opt, vvm, value);
1515             is_continue = true;
1516         }
1517         return is_continue;
1518     }
1519 
1520     private void init_variadic(Command cmd, ref string[][string] vvm) {
1521         if (!vvm)
1522             return;
1523         foreach (key, ref value; vvm) {
1524             if (cmd is this && cmd._configOption && cmd._configOption.name == key) {
1525                 cmd.emit("option:" ~ key, value.reverse);
1526             }
1527             else {
1528                 cmd.emit("option:" ~ key, value);
1529                 if (cmd !is this) {
1530                     auto opt = cmd._findOption(key);
1531                     opt.settled = false;
1532                 }
1533             }
1534         }
1535         vvm = null;
1536     }
1537 
1538     private void init_hv(Command cmd, string arg) {
1539         if (Option help_opt = cmd._helpOption) {
1540             if (help_opt.isFlag(arg)) {
1541                 cmd.emit("option:" ~ help_opt.name);
1542             }
1543         }
1544         else if (cmd._addImplicitHelpOption) {
1545             if (arg == "-h" || arg == "--help") {
1546                 auto hopt = cmd._getHelpOption();
1547                 cmd.emit("option:" ~ hopt.name);
1548             }
1549         }
1550         if (Option vopt = cmd._versionOption) {
1551             if (vopt.isFlag(arg)) {
1552                 cmd.emit("option:" ~ vopt.name);
1553             }
1554         }
1555     }
1556 
1557     private Command find_cmd(string str) {
1558         auto _cmd = _findCommand(str);
1559         auto vcmd = this._versionCommand;
1560         auto hcmd = this._helpCommand;
1561         _cmd = !_cmd && vcmd && vcmd._name == str ? vcmd : _cmd;
1562         _cmd = !_cmd && hcmd && hcmd._name == str ? hcmd : _cmd;
1563         _cmd = _cmd ? _cmd : (!hcmd && str == "help") ? this._getHelpCommand() : null;
1564         return _cmd;
1565     }
1566 
1567     private bool init_opt_command(string arg, ref string[] _args, ref string[][string] vvm, ref string[][string] pvvm) {
1568         init_hv(this, arg);
1569         if (Option copt = this._configOption) {
1570             if (copt.isFlag(arg)) {
1571                 string name = copt.name;
1572                 mixin InitVariadicOpt!(_args, get_front, maybe_opt, vvm, name);
1573                 return true;
1574             }
1575         }
1576         if (init_opt(this, arg, _args, vvm)) {
1577             return true;
1578         }
1579         if (this.parent && this.parent._passThroughOptionValue) {
1580             if (init_imex_command!false(arg, _args, pvvm))
1581                 return true;
1582             if (init_imex_command!true(arg, _args, pvvm))
1583                 return true;
1584             if (init_opt(this.parent, arg, _args, pvvm))
1585                 return true;
1586         }
1587         return false;
1588     }
1589 
1590     private bool init_imex_command(bool isExport = false)(string arg, ref string[] _args, ref string[][string] pvvm) {
1591         static if (isExport) {
1592             Option[string] _map = this.parent._export_map;
1593             NegateOption[string] _n_map = this.parent._export_n_map;
1594         }
1595         else {
1596             Option[string] _map = this._import_map;
1597             NegateOption[string] _n_map = this._import_n_map;
1598         }
1599         foreach (key, opt; _map) {
1600             if (arg != key)
1601                 continue;
1602             if (init_opt(this.parent, opt.shortFlag, _args, pvvm))
1603                 return true;
1604         }
1605         foreach (key, nopt; _n_map) {
1606             if (arg != key)
1607                 continue;
1608             if (init_opt(this.parent, nopt.longFlag, _args, pvvm))
1609                 return true;
1610         }
1611         return false;
1612     }
1613 
1614     private bool init_opt_comb(string arg, ref string[] _args, ref string[][string] vvm, ref string[][string] pvvm) {
1615         string flag = "-" ~ arg[1];
1616         init_hv(this, flag);
1617         if (Option copt = this._configOption) {
1618             if (copt.isFlag(flag)) {
1619                 string name = copt.name;
1620                 auto ptr = name in vvm;
1621                 if (ptr)
1622                     *ptr ~= arg[2 .. $];
1623                 else
1624                     vvm[name] = [arg[2 .. $]];
1625                 return true;
1626             }
1627         }
1628         if (init_comb(this, flag, arg, _args, vvm))
1629             return true;
1630         if (this.parent && this.parent._passThroughOptionValue) {
1631             if (init_imex_comb!false(flag, arg, _args, pvvm))
1632                 return true;
1633             if (init_imex_comb!true(flag, arg, _args, pvvm))
1634                 return true;
1635             if (init_comb(this.parent, flag, arg, _args, pvvm))
1636                 return true;
1637         }
1638         return false;
1639     }
1640 
1641     private bool init_imex_comb(bool isExport = false)(string flag, string arg,
1642         ref string[] _args, ref string[][string] pvvm) {
1643         static if (isExport) {
1644             Option[string] _map = this.parent._export_map;
1645             NegateOption[string] _n_map = this.parent._export_n_map;
1646         }
1647         else {
1648             Option[string] _map = this._import_map;
1649             NegateOption[string] _n_map = this._import_n_map;
1650         }
1651         foreach (key, opt; _map) {
1652             if (flag != key)
1653                 continue;
1654             if (init_comb(this.parent, opt.longFlag, arg, _args, pvvm))
1655                 return true;
1656 
1657         }
1658         foreach (key, nopt; _n_map) {
1659             if (flag != key)
1660                 continue;
1661             if (init_comb(this.parent, nopt.longFlag, arg, _args, pvvm))
1662                 return true;
1663         }
1664         return false;
1665     }
1666 
1667     private bool init_opt_assign(in Captures!string cp, ref string[][string] vvm, ref string[][string] pvvm) {
1668         string flag = cp[1];
1669         string value = cp[2];
1670         if (Option copt = this._configOption) {
1671             if (copt.isFlag(flag)) {
1672                 name = copt.name;
1673                 auto ptr = name in vvm;
1674                 if (ptr)
1675                     *ptr ~= value;
1676                 else
1677                     vvm[name] = [value];
1678                 return true;
1679             }
1680         }
1681         if (init_assign(this, flag, value, vvm))
1682             return true;
1683         if (this.parent && this.parent._passThroughOptionValue) {
1684             foreach (key, opt; _import_map) {
1685                 if (flag != key)
1686                     continue;
1687                 if (init_assign(this.parent, opt.longFlag, value, pvvm))
1688                     return true;
1689             }
1690             foreach (key, opt; this.parent._export_map) {
1691                 if (flag != key)
1692                     continue;
1693                 if (init_assign(this.parent, opt.longFlag, value, pvvm))
1694                     return true;
1695             }
1696             if (init_assign(this.parent, flag, value, pvvm))
1697                 return true;
1698         }
1699         return false;
1700     }
1701 
1702     Tuple!(string[], string[]) parseOptions(in string[] argv) {
1703         string[] operands = [];
1704         string[] unknowns = [];
1705         string[] _args = argv.dup;
1706         string[][string] variadic_val_map = null;
1707         string[][string] pvariadic_val_map = null;
1708 
1709         while (_args.length) {
1710             auto arg = _args.front;
1711             _args.popFront;
1712 
1713             if (arg == "--") {
1714                 mixin InitAfterDash!(_args, get_front, find_cmd, operands, unknowns);
1715                 break;
1716             }
1717 
1718             if (maybe_opt(arg)) {
1719                 if (init_opt_command(arg, _args, variadic_val_map, pvariadic_val_map))
1720                     continue;
1721             }
1722 
1723             if (arg.length > 2 && arg[0] == '-' && arg[1] != '-') {
1724                 if (init_opt_comb(arg, _args, variadic_val_map, pvariadic_val_map))
1725                     continue;
1726                 this.unknownOption("-" ~ arg[1]);
1727             }
1728 
1729             auto cp = matchFirst(arg, PTN_LONGASSIGN);
1730             if (cp.length) {
1731                 if (init_opt_assign(cp, variadic_val_map, pvariadic_val_map))
1732                     continue;
1733                 this.unknownOption(cp[1]);
1734             }
1735 
1736             if (auto _cmd = find_cmd(arg)) {
1737                 this.subCommand = _cmd;
1738                 if (subCommand.immediately && subCommand._actionHandler !is null) {
1739                     this.parseOptionsConfig();
1740                     subCommand._parseCommand(_args);
1741                 }
1742                 unknowns ~= _args;
1743                 break;
1744             }
1745 
1746             if (maybe_opt(arg))
1747                 unknownOption(arg);
1748             operands ~= arg;
1749         }
1750 
1751         init_variadic(this, variadic_val_map);
1752         init_variadic(this.parent, pvariadic_val_map);
1753         if (this.parent && this.parent._passThroughOptionValue) {
1754             this.parent
1755                 ._options
1756                 .filter!(opt => opt.isValid && !opt.settled)
1757                 .each!((opt) { opt.initialize; });
1758             this.parent.opts = this.parent
1759                 ._options
1760                 .filter!(opt => opt.settled)
1761                 .map!(opt => tuple(opt.name, opt.get))
1762                 .assocArray;
1763         }
1764         return tuple(operands, unknowns);
1765     }
1766 
1767     void optionMissingArgument(in Option opt) const {
1768         string message = format("option '%s' argument missing", opt.flags);
1769         this.parsingError(message);
1770     }
1771 
1772     void unknownOption(string flag) const {
1773         string msg = "";
1774         auto any_abandon = this._abandons.find!((const Option opt) => opt.isFlag(
1775                 flag) || opt.name == flag);
1776         if (!any_abandon.empty) {
1777             msg = format("this option `%s` has been disable by its related negate option `--%s`",
1778                 any_abandon[0].flags, any_abandon[0].name);
1779             this.parsingError(msg, "command.disableOption");
1780         }
1781         else {
1782             string suggestion = this.getSuggestion(flag);
1783             if (this.parent && this.parent._passThroughOptionValue && suggestion.length == 0) {
1784                 suggestion = this.parent.getSuggestion(flag);
1785             }
1786             msg = format("unknown option `%s` %s", flag, suggestion);
1787             this.parsingError(msg, "command.unknownOption");
1788         }
1789     }
1790 
1791     string getSuggestion(string flag) const {
1792         auto cmd = this;
1793         auto hlp = cmd._helpConfiguration;
1794         string suggestion = "";
1795         const(string)[] more_flags;
1796         string[] candidate_flags = [];
1797         if (flag[0 .. 2] == "--" && this._showSuggestionAfterError) {
1798             more_flags = hlp.visibleOptions(cmd).map!(opt => opt.longFlag).array;
1799             candidate_flags ~= more_flags;
1800         }
1801         suggestion = suggestSimilar(flag, candidate_flags);
1802         return suggestion;
1803     }
1804 
1805     void excessArguments() const {
1806         if (!this._allowExcessArguments) {
1807             this.parsingError("too much args!!!");
1808         }
1809     }
1810 
1811     void _checkMissingMandatoryOption() const {
1812         auto f = this._options.filter!(opt => opt.mandatory && !opt.settled);
1813         if (!f.empty) {
1814             auto strs = f.map!(opt => format!"`%s`"(opt.name)).join(" and ");
1815             this.parsingError(format!"the option: %s must have a valid value!"(strs));
1816         }
1817     }
1818 
1819     void _checkConfilctOption() const {
1820         auto opts = this._options
1821             .filter!(opt => opt.settled)
1822             .filter!(opt => !(opt.source == Source.Default || opt.source == Source.None || opt.source == Source
1823                     .Imply))
1824             .array;
1825         auto is_conflict = (const Option opt) {
1826             const string[] confilcts = opt.conflictsWith;
1827             foreach (name; confilcts) {
1828                 opts.each!((o) {
1829                     if (opt !is o && o.name == name)
1830                         this.parsingError(
1831                             format!"cannot set option `%s` and `%s` at the same time"(o.name, name));
1832                 });
1833             }
1834         };
1835         opts.each!(is_conflict);
1836     }
1837 
1838     void parseArguments(in string[] _args) {
1839         auto args = _args.dup;
1840         auto get_front = () => args.empty ? "" : args.front;
1841         foreach (argument; this._arguments) {
1842             auto is_v = argument.variadic;
1843             if (!is_v) {
1844                 auto value = get_front();
1845                 if (!value.length)
1846                     break;
1847                 argument.cliVal(value);
1848                 popFront(args);
1849             }
1850             else {
1851                 if (!args.length)
1852                     break;
1853                 argument.cliVal(args[0], args[1 .. $]);
1854                 args = [];
1855                 break;
1856             }
1857         }
1858         if (args.length && this._argToOptNames.length) {
1859             auto len = min(args.length, this._argToOptNames.length);
1860             foreach (index; 0 .. len) {
1861                 auto opt = this._findOption(this._argToOptNames[index]);
1862                 if (!opt)
1863                     unknownOption(this._argToOptNames[index]);
1864                 if (opt.variadic) {
1865                     this.setOptionVal!(Source.Cli)(opt.name, args);
1866                     opt.settled = false;
1867                     args = [];
1868                     break;
1869                 }
1870                 if (!opt.isValid || !opt.settled || opt.source != Source.Cli) {
1871                     if (opt.isBoolean) {
1872                         try {
1873                             bool value = args[index].to!bool;
1874                             this.setOptionValDirectly(opt.name, value, Source.Cli);
1875                             args.popFront;
1876                         }
1877                         catch (ConvException e) {
1878                             parsingError(format!"on bool option `%s` cannot convert the input `%s` to type `%s`"(
1879                                     opt.flags,
1880                                     args[index],
1881                                     bool.stringof
1882                             ));
1883                         }
1884                     }
1885                     else {
1886                         this.setOptionVal!(Source.Cli)(opt.name, get_front());
1887                         opt.settled = false;
1888                         args.popFront;
1889                     }
1890                 }
1891             }
1892         }
1893         if (args.length)
1894             this.excessArguments();
1895         this._arguments.each!((Argument arg) {
1896             if (arg.isRequired && !arg.isValid)
1897                 this.parsingError(
1898                     format!"argument `%s` is required but its value is invalid"(arg._name));
1899         });
1900         this._arguments.each!((Argument arg) {
1901             if (arg.isValid || arg.settled)
1902                 arg.initialize;
1903         });
1904         Argument prev = null;
1905         foreach (i, arg; this._arguments) {
1906             if (prev && arg.settled && !prev.settled)
1907                 this.parsingError(
1908                     format!"arg should be valid in row, the prev arg `%s` is invalid, while cur arg `%s` is valid"(
1909                         prev._name, arg._name
1910                 ));
1911             prev = arg;
1912         }
1913         this.args = this._arguments
1914             .filter!(arg => arg.settled)
1915             .map!(arg => arg.get)
1916             .array;
1917     }
1918 
1919     void parseOptionsEnv() {
1920         this._options.each!((Option opt) { opt.envVal(); });
1921     }
1922 
1923     void parseOptionsImply() {
1924         auto set_imply = (Option option) {
1925             auto imply_map = option.implyMap;
1926             foreach (string key, OptionVariant value; imply_map) {
1927                 auto tmp = split(key, ':');
1928                 string name = tmp[0];
1929                 string type = tmp[1];
1930                 Option opt = _findOption(name);
1931                 if (opt && opt._isMerge) {
1932                     opt.settled = false;
1933                     opt.implyVal(value);
1934                 }
1935                 else if (opt) {
1936                     if (!opt.isValid)
1937                         opt.implyVal(value);
1938                     if (opt.source == Source.Default) {
1939                         opt.settled = false;
1940                         opt.implyVal(value);
1941                     }
1942                 }
1943                 auto any_abandon = this._abandons.find!(
1944                     (const Option opt) => opt.name == name);
1945                 if (!opt && !any_abandon.empty) {
1946                     this.unknownOption(name);
1947                 }
1948                 if (!opt) {
1949                     string flag = format("--%s <%s-value>", name, name);
1950                     string flag2 = format("--%s", name);
1951                     string flag3 = format("--%s <%s-value...>", name, name);
1952                     switch (type) {
1953                     case int.stringof:
1954                         opt = createOption!int(flag);
1955                         break;
1956                     case double.stringof:
1957                         opt = createOption!double(flag);
1958                         break;
1959                     case string.stringof:
1960                         opt = createOption!string(flag);
1961                         break;
1962                     case bool.stringof:
1963                         opt = createOption!bool(flag2);
1964                         break;
1965                     case (int[]).stringof:
1966                         opt = createOption!int(flag3);
1967                         break;
1968                     case (double[]).stringof:
1969                         opt = createOption!double(flag3);
1970                         break;
1971                     case (string[]).stringof:
1972                         opt = createOption!string(flag3);
1973                         break;
1974                     default:
1975                         break;
1976                     }
1977                     if (opt) {
1978                         opt.implyVal(value);
1979                         this.addOption(opt);
1980                     }
1981                 }
1982             }
1983         };
1984         this._options
1985             .filter!(opt => opt.settled || opt.isValid)
1986             .each!((opt) { opt.initialize; });
1987         this._options
1988             .filter!(opt => opt.settled)
1989             .filter!(opt => !(opt.source == Source.Default || opt.source == Source.None || opt
1990                     .source == Source
1991                     .Imply))
1992             .each!(set_imply);
1993     }
1994 
1995     void parseOptionsConfig() {
1996         alias Value = const(JSONValue)*;
1997         if (this._configOption) {
1998             Value[] j_config = _processConfigFile();
1999             this.jconfig ~= j_config;
2000             this.jconfig = this.jconfig.uniq.array;
2001         }
2002         this.jconfig.each!((jconfig) { parseConfigOptionsImpl(jconfig); });
2003     }
2004 
2005 public:
2006     /// set the name of command
2007     Self name(string str) {
2008         this._name = str;
2009         return this;
2010     }
2011 
2012     /// get the name of command
2013     string name() const {
2014         return this._name.idup;
2015     }
2016 
2017     /// set the version of the command.
2018     /// if `flags` and `desc` not defined, then it will automatically set the version option flag `-V, --version` and version command name `version` 
2019     /// Params:
2020     ///   str = the version string like `0.0.1`
2021     ///   flags = the version option flag, which is a `bool` option
2022     ///   desc = the description of the version command and version option
2023     /// Returns: `Self` for chain call
2024     Self setVersion(string str, string flags = "", string desc = "") {
2025         this._version = str;
2026         setVersionOption(flags, desc);
2027         string vname = this._versionOption.name;
2028         setVersionCommand(vname, desc);
2029         return this;
2030     }
2031 
2032     /// get the version string of this command, if not set version, the default is `*`
2033     string getVersion() const {
2034         return this._version.idup;
2035     }
2036 
2037     /// set the version option of the command, see also `Self setVersion(string str, string flags = "", string desc = "")`
2038     Self setVersionOption(string flags = "", string desc = "") {
2039         assert(!this._versionOption);
2040         flags = flags == "" ? "-V, --version" : flags;
2041         desc = desc == "" ? "output the version number" : desc;
2042         auto vopt = createOption(flags, desc);
2043         if (auto help_opt = this._helpOption) {
2044             if (vopt.matchFlag(help_opt))
2045                 this.error(format!"Cannot add option '%s' due to confliction help option `%s`"(vopt.flags, help_opt
2046                         .flags));
2047         }
2048         else if (this._addImplicitHelpOption && (vopt.shortFlag == "-h" || vopt.longFlag == "--help")) {
2049             this.error(format!"Cannot add option '%s' due to confliction help option `%s`"(vopt.flags, "-h, --help"));
2050         }
2051         if (auto config_opt = this._configOption) {
2052             if (vopt.matchFlag(config_opt))
2053                 this.error(format!"Cannot add option '%s' due to confliction config option `%s`"(vopt.flags, config_opt
2054                         .flags));
2055         }
2056         this._versionOption = vopt;
2057         string vname = vopt.name;
2058         this.on("option:" ~ vname, () {
2059             this._outputConfiguration.writeOut(this._version ~ "\n");
2060             this._exitSuccessfully();
2061         });
2062         return this;
2063     }
2064 
2065     /// set the version option of the command, see also `Self setVersion(string str, string flags = "", string desc = "")`
2066     Self addVersionOption(string flags = "", string desc = "") {
2067         return setVersionOption(flags, desc);
2068     }
2069 
2070     /// custom the version option
2071     /// Params:
2072     ///   opt = the version option
2073     ///   action = the action callback when parsing the flag of version option, if `null`, then do nothing and exit mutely
2074     /// Returns: `Self` for chain call
2075     Self addVersionOption(Option opt, void delegate() action = null) {
2076         assert(!this._versionOption);
2077         this._versionOption = opt;
2078         if (!action) {
2079             this.on("option:" ~ opt.name, () {
2080                 this._outputConfiguration.writeOut(this._version ~ "\n");
2081                 this._exitSuccessfully();
2082             });
2083         }
2084         else
2085             this.on("option:" ~ opt.name, () {
2086                 action();
2087                 this._exitSuccessfully();
2088             });
2089         return this;
2090     }
2091 
2092     /// set version command, see also `Self setVersion(string str, string flags = "", string desc = "")`
2093     Self setVersionCommand(string flags = "", string desc = "") {
2094         assert(!this._versionCommand);
2095         flags = flags == "" ? "version" : flags;
2096         desc = desc == "" ? "output the version number" : desc;
2097         Command cmd = createCommand(flags).description(desc);
2098         cmd.setHelpOption(false);
2099         cmd.setHelpCommand(false);
2100         string vname = cmd._name;
2101         if (auto help_cmd = this._helpCommand) {
2102             auto help_cmd_name_arr = help_cmd._aliasNames ~ help_cmd._name;
2103             auto none = help_cmd_name_arr.find!(name => vname == name).empty;
2104             if (!none) {
2105                 string help_cmd_names = help_cmd_name_arr.join("|");
2106                 this.error(
2107                     format!"cannot add command `%s` as this command name cannot be same as the name of help command `%s`"(
2108                         vname, help_cmd_names));
2109             }
2110         }
2111         else if (this._addImplicitHelpCommand) {
2112             string help_cmd_names = "help";
2113             if (vname == help_cmd_names) {
2114                 this.error(
2115                     format!"cannot add command `%s` as this command name cannot be same as the name of help command `%s`"(
2116                         vname, help_cmd_names));
2117             }
2118         }
2119         this.on("command:" ~ vname, () {
2120             this._outputConfiguration.writeOut(this._version ~ "\n");
2121             this._exitSuccessfully();
2122         });
2123         ActionCallback fn = () {
2124             this._outputConfiguration.writeOut(this._version ~ "\n");
2125             this._exitSuccessfully();
2126         };
2127         cmd.parent = this;
2128         cmd.action(fn, true);
2129         this._versionCommand = cmd;
2130         return this;
2131     }
2132 
2133     /// set version command, see also `Self setVersion(string str, string flags = "", string desc = "")`
2134     Self addVersionCommand(string flags = "", string desc = "") {
2135         return setVersionCommand(flags, desc);
2136     }
2137 
2138     /// set version command, see also `Self setVersion(string str, string flags = "", string desc = "")`
2139     Self addVersionCommand(Command cmd) {
2140         assert(!this._versionCommand);
2141         string vname = cmd._name;
2142         if (auto help_cmd = this._helpCommand) {
2143             auto help_cmd_name_arr = help_cmd._aliasNames ~ help_cmd._name;
2144             auto none = help_cmd_name_arr.find!(name => vname == name).empty;
2145             if (!none) {
2146                 string help_cmd_names = help_cmd_name_arr.join("|");
2147                 this.error(
2148                     format!"cannot add command `%s` as this command name cannot be same as the name of help command `%s`"(
2149                         vname, help_cmd_names));
2150             }
2151         }
2152         else if (this._addImplicitHelpCommand) {
2153             string help_cmd_names = "help";
2154             if (vname == help_cmd_names) {
2155                 this.error(format!"cannot add command `%s` as this command name cannot be same as the name of help command `%s`"(
2156                         vname, help_cmd_names));
2157             }
2158         }
2159         cmd.parent = this;
2160         this._versionCommand = cmd;
2161         return this;
2162     }
2163 
2164     /// set the config option, if `flags` not defined, then config option's flags is `-C, --config <config-dirs...>`;
2165     /// if `desc` not defined, then description is by default;
2166     /// if `defaultDir` not defined, then the `defaultDir` would be `${CURRENT_WORKER_DIR}`, which automatically store the path to dir of config file
2167     /// Params:
2168     ///   flags = the flags of config option
2169     ///   desc = the description of config option
2170     ///   defaultDir = the built-in path to the dir of config file, which priority is higher than `${YOUR_PROGRAM_DIR}/{YOUR_PROGRAM_NAME}.config.json`
2171     /// Returns: `Self` for chain call
2172     Self setConfigOption(string flags = "", string desc = "", string defaultDir = "") {
2173         assert(!this._configOption);
2174         flags = flags == "" ? "-C, --config <config-dirs...>" : flags;
2175         defaultDir = defaultDir == "" ? thisExePath.dirName : defaultDir;
2176         string cwd = getcwd();
2177         desc = desc == "" ?
2178             format("define the directories of the config file," ~
2179                     "if not specified, the config file name would be" ~
2180                     " `%s.config.json` and it is on the dir `%s` and current woker dir `%s`",
2181                     this._name, defaultDir, cwd) : desc;
2182         this._configPaths ~= defaultDir;
2183         this._configPaths ~= cwd;
2184         this._configPaths = this._configPaths.uniq.array;
2185         auto copt = createOption!string(flags, desc);
2186         if (auto help_opt = this._helpOption) {
2187             if (
2188                 copt.matchFlag(
2189                     help_opt))
2190                 this.error(
2191                     format!"Cannot add option '%s'
2192                     due to confliction help option `%s`"(copt.flags, help_opt.flags));
2193         }
2194         else if (this._addImplicitHelpOption && (copt.shortFlag == "-h" || copt.longFlag == "--help")) {
2195             this.error(
2196                 format!"Cannot add option '%s'
2197                     due to confliction help option `%s`"(copt.flags, "-h, --help"));
2198         }
2199         if (
2200             auto version_opt = this
2201             ._versionOption) {
2202             if (
2203                 copt.matchFlag(
2204                     version_opt))
2205                 this.error(
2206                     format!"Cannot add option '%s'
2207                     due to confliction version option `%s`"(copt.flags, version_opt.flags));
2208         }
2209         this._configOption = copt;
2210         string cname = copt.name;
2211         this.on("option:" ~ cname, (
2212                 string[] configPaths) {
2213             string current_dir = getcwd();
2214             configPaths.each!((path) {
2215                 auto rpath = buildPath(current_dir, path);
2216                 auto npath = exists(rpath) ? rpath : path;
2217                 if (!exists(npath)) {
2218                     parsingError(format!"invalid path `%s` or `%s`"(rpath, path));
2219                 }
2220                 this._configPaths ~= npath;
2221             });
2222             this._configPaths = this._configPaths.uniq.array;
2223         });
2224         return this;
2225     }
2226 
2227     package void parseConfigOptionsImpl(const(JSONValue)* config) {
2228         alias Value = const(JSONValue)*;
2229         if (this.subCommand && !this.subCommand._execHandler) {
2230             string sub_name = this.subCommand._name;
2231             Value sub_config = sub_name in *config;
2232             if (sub_config && this.subCommand.jconfig.count(sub_config) == 0)
2233                 this.subCommand.jconfig ~= sub_config;
2234         }
2235         if (this.subCommand && this.subCommand.immediately)
2236             return;
2237         Value[string] copts;
2238         Value[] cargs;
2239         if (Value ptr = "arguments" in *config) {
2240             if (ptr.type != JSONType.ARRAY) {
2241                 this.parsingError("the `arguments`'s value must be array!");
2242             }
2243             ptr.array.each!((const ref JSONValue ele) {
2244                 if (ele.type == JSONType.NULL || ele.type == JSONType.OBJECT) {
2245                     this.parsingError("the `argument`'s element value cannot be object or null!");
2246                 }
2247                 cargs ~= &ele;
2248             });
2249         }
2250         if (Value ptr = "options" in *config) {
2251             if (ptr.type != JSONType.OBJECT) {
2252                 this.parsingError("the `options`'s value must be object!");
2253             }
2254             foreach (string key, const ref JSONValue ele; ptr.object) {
2255                 if (ele.type == JSONType.NULL || ele.type == JSONType.OBJECT) {
2256                     this.parsingError("the `option`'s value cannot be object or null!");
2257                 }
2258                 if (ele.type == JSONType.ARRAY) {
2259                     if (ele.array.length < 1)
2260                         this.parsingError(
2261                             "if the `option`'s value is array, then its length cannot be 0");
2262                     bool all_int = ele.array.all!((ref e) => e.type == JSONType.INTEGER);
2263                     bool all_double = ele.array.all!((ref e) => e.type == JSONType.FLOAT);
2264                     bool all_string = ele.array.all!((ref e) => e.type == JSONType.STRING);
2265                     if (!(all_int || all_double || all_string)) {
2266                         this.parsingError("if the `option`'s value is array, then its element type must all be int or double or string the same");
2267                     }
2268                 }
2269                 copts[key] = &ele;
2270             }
2271         }
2272         auto get_front = () => cargs.empty ? null : cargs.front;
2273         auto test_regulra = () {
2274             bool all_int = cargs.all!((ref e) => e.type == JSONType.INTEGER);
2275             bool all_double = cargs.all!((ref e) => e.type == JSONType.FLOAT);
2276             bool all_string = cargs.all!((ref e) => e.type == JSONType.STRING);
2277             if (!(all_int || all_double || all_string)) {
2278                 this.parsingError(
2279                     "the variadic `arguments`'s element type must all be int or double or string the same");
2280             }
2281         };
2282         auto assign_arg_arr = (Argument arg) {
2283             test_regulra();
2284             auto tmp = cargs[0];
2285             switch (tmp.type) {
2286             case JSONType.INTEGER:
2287                 arg.configVal(cargs.map!((ref ele) => cast(int) ele.get!int).array);
2288                 break;
2289             case JSONType.FLOAT:
2290                 arg.configVal(cargs.map!((ref ele) => cast(double) ele.get!double).array);
2291                 break;
2292             case JSONType.STRING:
2293                 arg.configVal(cargs.map!((ref ele) => cast(string) ele.get!string).array);
2294                 break;
2295             default:
2296                 break;
2297             }
2298         };
2299         foreach (Argument argument; this._arguments) {
2300             auto is_v = argument.variadic;
2301             if (!is_v) {
2302                 if (Value value = get_front()) {
2303                     mixin AssignOptOrArg!(argument, value);
2304                     cargs.popFront();
2305                 }
2306                 else
2307                     break;
2308             }
2309             else {
2310                 if (cargs.length) {
2311                     assign_arg_arr(argument);
2312                     break;
2313                 }
2314             }
2315         }
2316         foreach (string key, Value value; copts) {
2317             Option opt = _findOption(key);
2318             if (opt) {
2319                 mixin AssignOptOrArg!(opt, value);
2320             }
2321             else {
2322                 this.unknownOption(key);
2323             }
2324         }
2325     }
2326 
2327     private mixin template AssignOptOrArg(alias target, alias src)
2328             if (is(typeof(src) == const(JSONValue)*)) {
2329         static if (is(typeof(target) == Argument)) {
2330             Argument arg = target;
2331         }
2332         else static if (is(typeof(target) == Option)) {
2333             Option arg = target;
2334         }
2335         else {
2336             static assert(false);
2337         }
2338         const(JSONValue)* val = src;
2339 
2340         static if (is(typeof(target) == Option)) {
2341             auto assign_arr = () {
2342                 auto tmp = (val.array)[0];
2343                 switch (tmp.type) {
2344                 case JSONType.INTEGER:
2345                     arg.configVal(val.array.map!((ref ele) => cast(int) ele.get!int).array);
2346                     break;
2347                 case JSONType.FLOAT:
2348                     arg.configVal(val.array.map!((ref ele) => cast(double) ele.get!double).array);
2349                     break;
2350                 case JSONType.STRING:
2351                     arg.configVal(val.array.map!((ref ele) => cast(string) ele.get!string).array);
2352                     break;
2353                 default:
2354                     break;
2355                 }
2356             };
2357         }
2358 
2359         auto assign = () {
2360             switch (val.type) {
2361             case JSONType.INTEGER:
2362                 arg.configVal(cast(int) val.get!int);
2363                 break;
2364             case JSONType.FLOAT:
2365                 arg.configVal(cast(double) val.get!double);
2366                 break;
2367             case JSONType.STRING:
2368                 arg.configVal(cast(string) val.get!string);
2369                 break;
2370             case JSONType.FALSE, JSONType.TRUE:
2371                 arg.configVal(cast(bool) val.get!bool);
2372                 break;
2373                 static if (is(typeof(target) == Option)) {
2374             case JSONType.ARRAY:
2375                     assign_arr();
2376                     break;
2377                 }
2378             default:
2379                 break;
2380             }
2381             return 0;
2382         };
2383         auto _x_Inner_ff = assign();
2384     }
2385 
2386     package const(JSONValue)*[] _processConfigFile() const {
2387         const(JSONValue)*[] tmp = [];
2388         foreach (path; this._configPaths) {
2389             string config_file = buildPath(path, format("%s.config.json", this._name));
2390             if (config_file.length > 12 && config_file[$ - 12 .. $] == ".config.json"
2391                 && exists(config_file)) {
2392                 try {
2393                     string raw = readText(config_file);
2394                     auto rele = new JSONValue(parseJSON(raw));
2395                     if (rele.type != JSONType.OBJECT) {
2396                         parsingError(
2397                             format!"the json must be object in json file `%s`"(config_file));
2398                     }
2399                     tmp ~= rele;
2400                 }
2401                 catch (Exception e) {
2402                     this.parsingError(e.msg);
2403                 }
2404             }
2405         }
2406         return tmp;
2407     }
2408 
2409     package void _helpCommandAction(in OptsWrap _, in ArgWrap hcommand) {
2410         if (hcommand.isValid) {
2411             auto sub_cmd_name = hcommand.get!string;
2412             auto sub_cmd = this._findCommand(sub_cmd_name);
2413             auto vcmd = this._versionCommand;
2414             sub_cmd = sub_cmd ? sub_cmd : vcmd && vcmd._name == sub_cmd_name ? vcmd : null;
2415             if (!sub_cmd || sub_cmd._hidden)
2416                 this.parsingError("can not find the sub command `" ~ sub_cmd_name ~ "`!");
2417             if (sub_cmd._execHandler) {
2418                 sub_cmd.execSubCommand([
2419                     this._externalCmdHelpFlagMap[sub_cmd._name]
2420                 ]);
2421             }
2422             sub_cmd.help();
2423         }
2424         this.help();
2425     }
2426 
2427     /// set the help command
2428     Self setHelpCommand(string flags = "", string desc = "") {
2429         assert(!this._helpCommand);
2430         bool has_sub_cmd = this._versionCommand !is null || !this._commands.find!(
2431             cmd => !cmd._hidden).empty;
2432         flags = flags == "" ? has_sub_cmd ? "help [command]" : "help" : flags;
2433         desc = desc == "" ? "display help for command" : desc;
2434         Command help_cmd = has_sub_cmd ? createCommand!(string)(flags, desc) : createCommand(flags, desc);
2435         help_cmd.setHelpOption(false);
2436         help_cmd.setHelpCommand(false);
2437         string hname = help_cmd._name;
2438         if (auto verison_cmd = this._versionCommand) {
2439             auto version_cmd_name_arr = verison_cmd._aliasNames ~ verison_cmd._name;
2440             auto none = version_cmd_name_arr.find!(name => hname == name).empty;
2441             if (!none) {
2442                 string version_cmd_names = version_cmd_name_arr.join("|");
2443                 this.error(
2444                     format!"cannot add command `%s` as this command name cannot be same as
2445                         the name of version command `%s`"(
2446                         hname, version_cmd_names));
2447             }
2448         }
2449         help_cmd.parent = this;
2450         help_cmd.action(&this._helpCommandAction, true);
2451         this._helpCommand = help_cmd;
2452         return setHelpCommand(true);
2453     }
2454 
2455     /// enable or disable the help command
2456     Self setHelpCommand(bool enable) {
2457         this._addImplicitHelpCommand = enable;
2458         return this;
2459     }
2460 
2461     /// add the help command
2462     Self addHelpCommand(Command cmd) {
2463         assert(!this._helpCommand);
2464         string[] hnames = cmd._aliasNames ~ cmd._name;
2465         if (auto verison_cmd = this._versionCommand) {
2466             auto version_cmd_name_arr = verison_cmd._aliasNames ~ verison_cmd._name;
2467             auto none = version_cmd_name_arr.find!((name) {
2468                 return !hnames.find!(h => h == name).empty;
2469             }).empty;
2470             if (!none) {
2471                 string version_cmd_names = version_cmd_name_arr.join("|");
2472                 this.error(
2473                     format!"cannot add command `%s` as this command name cannot be same as
2474                         the name of version command `%s`"(
2475                         hnames.join("|"), version_cmd_names));
2476             }
2477         }
2478         cmd.parent = this;
2479         cmd.action(&this._helpCommandAction, true);
2480         this._helpCommand = cmd;
2481         return setHelpCommand(true);
2482     }
2483 
2484     /// add the help command
2485     Self addHelpCommand(string flags = "", string desc = "") {
2486         return this.setHelpCommand(flags, desc);
2487     }
2488 
2489     package Command _getHelpCommand() {
2490         if (!this._addImplicitHelpCommand)
2491             return null;
2492         if (!this._helpCommand)
2493             this.setHelpCommand();
2494         return this._helpCommand;
2495     }
2496 
2497     /// set the help option
2498     Self setHelpOption(string flags = "", string desc = "") {
2499         assert(!this._helpOption);
2500         flags = flags == "" ? "-h, --help" : flags;
2501         desc = desc == "" ? "display help for command" : desc;
2502         auto hopt = createOption(flags, desc);
2503         if (auto config_opt = this._configOption) {
2504             if (hopt.matchFlag(config_opt))
2505                 this.error(
2506                     format!"Cannot add option '%s'
2507                     due to confliction config option `%s`"(hopt.flags, config_opt.flags));
2508         }
2509         if (auto version_opt = this._versionOption) {
2510             if (hopt.matchFlag(version_opt))
2511                 this.error(
2512                     format!"Cannot add option '%s'
2513                     due to confliction version option `%s`"(hopt.flags, version_opt.flags));
2514         }
2515         this._helpOption = hopt;
2516         this.on("option:" ~ hopt.name, () { this.help(); });
2517         return setHelpOption(true);
2518     }
2519 
2520     /// enable the help option or not
2521     Self setHelpOption(bool enable) {
2522         this._addImplicitHelpOption = enable;
2523         return this;
2524     }
2525 
2526     /// add the help option
2527     Self addHelpOption(Option option, void delegate() action = null) {
2528         assert(!this._helpOption);
2529         this._helpOption = option;
2530         if (!action)
2531             this.on("option:" ~ option.name, () { this.help(); });
2532         else
2533             this.on("option:" ~ option.name, () {
2534                 action();
2535                 this._exitSuccessfully();
2536             });
2537         return setHelpOption(true);
2538     }
2539 
2540     /// add the help option
2541     Self addHelpOption(string flags = "", string desc = "") {
2542         return this.setHelpOption(flags, desc);
2543     }
2544 
2545     /// disable the help support
2546     Self disableHelp() {
2547         setHelpCommand(false);
2548         setHelpOption(false);
2549         return this;
2550     }
2551 
2552     package Option _getHelpOption() {
2553         if (!this._addImplicitHelpCommand)
2554             return null;
2555         if (!this._helpOption)
2556             this.setHelpOption();
2557         return this._helpOption;
2558     }
2559 
2560     package void outputHelp(bool isErrorMode = false) const {
2561         auto writer = isErrorMode ?
2562             this._outputConfiguration.writeErr : this._outputConfiguration.writeOut;
2563         auto ancestors = cast(Command[]) _getCommandAndAncestors();
2564         ancestors.reverse.each!(
2565             cmd => cmd.emit("beforeAllHelp", isErrorMode)
2566         );
2567         this.emit("beforeHelp", isErrorMode);
2568         writer(helpInfo(isErrorMode) ~ "\n");
2569         this.emit("afterHelp", isErrorMode);
2570         ancestors.each!(cmd => cmd.emit("afterAllHelp", isErrorMode)
2571         );
2572     }
2573 
2574     /// generate the help info
2575     /// Params:
2576     ///   isErrorMode = turn on the error mode, which would make the info output to this command's error output
2577     /// Returns: the string of help info
2578     string helpInfo(bool isErrorMode = false) const {
2579         auto helper = cast(Help) this._helpConfiguration;
2580         helper.helpWidth = isErrorMode ?
2581             this._outputConfiguration.getErrHelpWidth() : this._outputConfiguration.getOutHelpWidth();
2582         return helper.formatHelp(this);
2583     }
2584 
2585     /// invoke the help if the help support is not disabled
2586     /// Params:
2587     ///   isErrorMode = turn on the error mode, which would make the info output to this command's error output
2588     void help(bool isErrorMode = false) {
2589         this.outputHelp(isErrorMode);
2590         if (isErrorMode)
2591             this._exitErr("(outputHelp)", "command.help");
2592         this._exit(0);
2593     }
2594 
2595     /// add a appendent help text at specified position
2596     /// Params:
2597     ///   pos = the postion to insert at
2598     ///   text = the appendent hlp text
2599     /// Returns: `Self` for chain call
2600     Self addHelpText(AddHelpPos pos, string text) {
2601         assert(this._addImplicitHelpCommand || this._addImplicitHelpOption);
2602         string help_event = pos ~ "Help";
2603         this.on(help_event, (bool isErrMode) {
2604             if (text.length) {
2605                 auto writer = isErrMode ?
2606                     this._outputConfiguration.writeErr : this._outputConfiguration.writeOut;
2607                 writer(text ~ "\n");
2608             }
2609         });
2610         return this;
2611     }
2612 
2613     /// whether sort sub commands when invoke help, default: false
2614     Self sortSubCommands(bool enable = true) {
2615         this._helpConfiguration.sortSubCommands = enable;
2616         return this;
2617     }
2618 
2619     /// whether sort sub options when invoke help, default: false
2620     Self sortOptions(bool enable = true) {
2621         this._helpConfiguration.sortOptions = enable;
2622         return this;
2623     }
2624 
2625     /// whether show the global options when invoke help, default: false
2626     Self showGlobalOptions(bool enable = true) {
2627         this._helpConfiguration.showGlobalOptions = enable;
2628         return this;
2629     }
2630 
2631     package void _outputHelpIfRequested(string[] flags) {
2632         auto help_opt = this._getHelpOption();
2633         bool help_requested = help_opt !is null && !flags.find!(
2634             flag => help_opt.isFlag(flag)).empty;
2635         if (help_requested) {
2636             this.outputHelp();
2637             this._exitSuccessfully();
2638         }
2639     }
2640 
2641     static foreach (Action; ActionCallBackSeq) {
2642         /// define the action at the end of parsing
2643         /// Params:
2644         ///   Fn = the action call back
2645         mixin SetActionFn!Action;
2646     }
2647 
2648     private mixin template SetActionFn(Fn) {
2649         public Self action(Fn fn, bool immediately = false) {
2650             this.immediately = immediately;
2651             enum len = Parameters!(fn).length;
2652             auto listener = () {
2653                 static if (len == 0) {
2654                     fn();
2655                 }
2656                 else {
2657                     this.opts = this.opts is null ?
2658                         this._options
2659                         .filter!(opt => opt.settled)
2660                         .map!(opt => tuple(opt.name, opt.get))
2661                         .assocArray : this.opts;
2662                     this.args = this.args.empty ?
2663                         this._arguments
2664                         .filter!(arg => arg.settled)
2665                         .map!(arg => arg.get)
2666                         .array : this.args;
2667                     OptsWrap wopts = OptsWrap(this.opts);
2668                     static if (len == 1) {
2669                         fn(wopts);
2670                     }
2671                     else {
2672                         auto nlen = len - 1;
2673                         ArgWrap[] wargs;
2674                         if (this.args.length >= nlen) {
2675                             wargs = this.args[0 .. nlen].map!((return a) => ArgWrap(a)).array;
2676                         }
2677                         else {
2678                             wargs = this.args.map!(a => ArgWrap(a)).array;
2679                             ulong less_num = nlen - this.args.length;
2680                             foreach (_; 0 .. less_num) {
2681                                 wargs ~= ArgWrap(null);
2682                             }
2683                         }
2684                         static if (len == 2) {
2685                             fn(wopts, wargs[0]);
2686                         }
2687                         static if (len == 3) {
2688                             fn(wopts, wargs[0], wargs[1]);
2689                         }
2690                         static if (len == 4) {
2691                             fn(wopts, wargs[0], wargs[1], wargs[2]);
2692                         }
2693                         static if (len == 5) {
2694                             fn(wopts, wargs[0], wargs[1], wargs[2], wargs[3]);
2695                         }
2696                         static if (len == 6) {
2697                             fn(wopts, wargs[0], wargs[1], wargs[2], wargs[3], wargs[4]);
2698                         }
2699                     }
2700                 }
2701                 this._exitSuccessfully();
2702             };
2703             this._actionHandler = listener;
2704             this.on("action:" ~ this._name, () {
2705                 if (this._actionHandler)
2706                     this._actionHandler();
2707             });
2708             return this;
2709         }
2710     }
2711 
2712     /// get the options' value map wrap by `OptsWrap`. remember use it after parsing or at the action callabck
2713     inout(OptsWrap) getOpts() inout {
2714         return inout OptsWrap(this.opts);
2715     }
2716 
2717     /// get the array of arguments' value wrap by `ArgWrap`. remember use it after parsing or at the action callabck
2718     ArgWrap[] getArgs() const {
2719         auto len = this._arguments.length;
2720         ArgWrap[] wargs;
2721         if (this.args.length >= len) {
2722             wargs = this.args[0 .. len].map!((return a) => ArgWrap(a)).array;
2723         }
2724         else {
2725             wargs = this.args.map!(a => ArgWrap(a)).array;
2726             ulong less_num = len - this.args.length;
2727             foreach (_; 0 .. less_num) {
2728                 wargs ~= ArgWrap(null);
2729             }
2730         }
2731         return wargs;
2732     }
2733 
2734     /// set the alias of command
2735     Self aliasName(string aliasStr) {
2736         Command command = this;
2737         if (this._commands.length != 0 && this._commands[$ - 1]._execHandler) {
2738             command = this._commands[$ - 1];
2739         }
2740         if (aliasStr == command._name)
2741             this.error(format!"cannot add alias `%s` to command `%s` as they cannot be same"(
2742                     aliasStr, command
2743                     ._name
2744             ));
2745         auto matchingCommand = this.parent ? this._findCommand(aliasStr) : null;
2746         if (matchingCommand) {
2747             auto exitCmdNames = [matchingCommand.name()];
2748             exitCmdNames ~= matchingCommand.aliasNames;
2749             auto namesStr = exitCmdNames.join("|");
2750             this.error(
2751                 format!"cannot add alias %s to command %s as already have command %s"(
2752                     aliasStr, this.name, namesStr));
2753         }
2754         command._aliasNames ~= aliasStr;
2755         return this;
2756     }
2757 
2758     /// get the first alias of command
2759     string aliasName() const {
2760         if (this._aliasNames.length == 0) {
2761             this.error("the num of alias names cannot be zero");
2762         }
2763         return this._aliasNames[0];
2764     }
2765 
2766     /// set a sequence of aliases of command
2767     Self aliasNames(string[] aliasStrs) {
2768         aliasStrs.each!(str => this.aliasName(str));
2769         return this;
2770     }
2771 
2772     /// get the sequence of aliases of command
2773     const(string[]) aliasNames() const {
2774         return this._aliasNames;
2775     }
2776 
2777     inout(Argument) _findArgument(string name) inout {
2778         auto validate = (inout Argument arg) { return name == arg._name; };
2779         auto tmp = this._arguments.find!validate;
2780         return tmp.empty ? null : tmp[0];
2781     }
2782 
2783     inout(Command) _findCommand(string name) inout {
2784         auto validate = (inout Command cmd) {
2785             auto result = cmd._name == name || cmd._aliasNames.any!(n => n == name);
2786             return result;
2787         };
2788         auto tmp = this._commands.find!validate;
2789         return tmp.empty ? null : tmp[0];
2790     }
2791 
2792     inout(Option) _findOption(string flag) inout {
2793         auto tmp = this._options.find!(opt => opt.isFlag(flag) || flag == opt.name);
2794         return tmp.empty ? null : tmp[0];
2795     }
2796 
2797     inout(NegateOption) _findNegateOption(string flag) inout {
2798         auto tmp = this._negates.find!(opt => opt.isFlag(flag) || flag == opt.name);
2799         return tmp.empty ? null : tmp[0];
2800     }
2801 
2802     /// add argument for command
2803     Self addArgument(Argument argument) {
2804         auto args = this._arguments;
2805         Argument prev_arg = args.length ? args[$ - 1] : null;
2806         if (prev_arg && prev_arg.variadic) {
2807             this.error(format!"cannot add argument `%s` after the variadic argument `%s`"(
2808                     argument._name, prev_arg
2809                     ._name
2810             ));
2811         }
2812         if (prev_arg && prev_arg.isOptional && argument.isRequired) {
2813             this.error(format!"cannot add required argument `%s` after the optional argument `%s`"(
2814                     argument._name, prev_arg
2815                     ._name
2816             ));
2817         }
2818         _registerArgument(argument);
2819         return this;
2820     }
2821 
2822     /// define the argument for command
2823     Self argument(T)(string name, string desc = "") if (isArgValueType!T) {
2824         auto arg = createArgument!T(name, desc);
2825         this.addArgument(arg);
2826         return this;
2827     }
2828 
2829     /// define the argument with default value for command, used for no-variadic argument
2830     Self argument(T)(string name, string desc, T val) if (isBaseArgValueType!T) {
2831         auto arg = createArgument!T(name, desc);
2832         arg.defaultVal(val);
2833         this.addArgument(arg);
2834         return this;
2835     }
2836 
2837     /// define the argument with default value for command, used for variadic argument
2838     Self argument(T)(string name, string desc, T val, T[] rest...)
2839             if (isBaseArgValueType!T && !is(T == bool)) {
2840         auto arg = createArgument!T(name, desc);
2841         arg.defaultVal(val, rest);
2842         this.addArgument(arg);
2843         return this;
2844     }
2845 
2846     /// define the argument with default value for command, used for variadic argument
2847     Self argument(T : U[], U)(string name, string desc, T defaultVal)
2848             if (!is(U == bool) && isBaseArgValueType!U) {
2849         assert(defaultVal.length >= 1);
2850         auto arg = createArgument!T(name, desc);
2851         arg.defaultVal(defaultVal);
2852         this.addArgument(arg);
2853         return this;
2854     }
2855 
2856     /// define manly arguments for command
2857     Self arguments(Args...)(string names) {
2858         enum args_num = Args.length;
2859         auto arg_strs = names.strip().split(" ");
2860         assert(args_num == arg_strs.length);
2861         static foreach (index, T; Args) {
2862             this.argument!T(arg_strs[index]);
2863         }
2864         return this;
2865     }
2866 
2867     /// configure the output of command
2868     Self configureOutput(OutputConfiguration config) {
2869         this._outputConfiguration = config;
2870         return this;
2871     }
2872 
2873     /// get the output cofiguration of command
2874     inout(OutputConfiguration) configureOutput() inout {
2875         return this._outputConfiguration;
2876     }
2877 
2878     /// whether show help info when parsing occur error, default: `true`
2879     Self showHelpAfterError(bool displayHelp = true) {
2880         this._showHelpAfterError = displayHelp;
2881         return this;
2882     }
2883 
2884     /// whether show suggestion info when parsing occur error, default: `true`
2885     Self showSuggestionAfterError(bool displaySuggestion = true) {
2886         this._showSuggestionAfterError = displaySuggestion;
2887         return this;
2888     }
2889 
2890     /// whether allow combine flag mode like `ls -al`, default: `true`
2891     Self comineFlagAndOptionValue(bool combine) {
2892         this._combineFlagAndOptionalValue = combine;
2893         return this;
2894     }
2895 
2896     /// whether allow excess argument, default: `true`
2897     Self allowExcessArguments(bool allow) {
2898         this._allowExcessArguments = allow;
2899         return this;
2900     }
2901 
2902     /// whether allow variadic options' final values merge from different source, default: `true`
2903     Self allowVariadicMerge(bool allow) {
2904         this._allowVariadicMerge = allow;
2905         return this;
2906     }
2907 
2908     /// whether allow expose options' values to its sub commands, default: `false`
2909     Self allowExposeOptionValue(bool allow) {
2910         this._allowExposeOptionValue = allow;
2911         return this;
2912     }
2913 
2914     /// whether allow pass through options' flags(config, help, version options are not included)
2915     /// behind its sub commands, default: `false`
2916     Self passThrough(bool allow = true) {
2917         this._passThroughOptionValue = allow;
2918         return this;
2919     }
2920 
2921     /// import the option flag as new flag from parent command, so that this new flag can be pased as the
2922     /// the option flag of parent command. this member function aims at avoidind flag conflict between
2923     /// the command and its parent command, after using `passThrough` on parent command.
2924     /// the parent command must exist and parent command must have used `passThrough` and the `flag` must
2925     /// be the flag of one of the parent command's option or negate option.
2926     /// remeber that when parsing the new flag, the option's new flag is prior to the negate option ones.
2927     /// when parsing command line option flags, imported flag is prior to exported one, and the export one is 
2928     /// prior to the ordinary pass-through option flag.
2929     /// `isNegate` decide whether import negate option or non-negate one, default: `false`
2930     /// Params:
2931     ///   flag = the flag(or name) of one of the parent command's option or negate option
2932     ///   aliasFlags = the new flags
2933     /// Returns: `Self` for chain call
2934     Self importAs(bool isNegate = false)(string flag, string[] aliasFlags...) {
2935         if (!this.parent || !this.parent._passThroughOptionValue)
2936             this.error(format("cannot use member function `Command.importAs` in command `%s` for 
2937                 the parent command is not found or its parent 
2938                 command has not used member function `Command.passThrough`", this._name));
2939         assert(aliasFlags.length);
2940         static if (isNegate) {
2941             auto flg = imExAsImpl!(NegateOption)(flag, aliasFlags);
2942             enum string word = "negate ";
2943         }
2944         else {
2945             auto flg = imExAsImpl!(Option)(flag, aliasFlags);
2946             enum string word = "";
2947         }
2948         if (!flg)
2949             this.error(format("cannot find the %soption `%s` in `Command.importAs` in command `%s`",
2950                     word, flag, this._name));
2951         return this;
2952     }
2953 
2954     /// import the neagte option flag, see `Command.importAs`
2955     alias importNAs = importAs!true;
2956 
2957     /// export the option flag as new flag to sub command, so that this new flag can be pased as the
2958     /// the option flag of parent command. this member function aims at avoidind flag conflict between
2959     /// the command and its sub command, after using `passThrough` on this command.
2960     /// this function would be automatically call `Command.passThrough`.
2961     /// the `flag` must be the flag of one of the command's option or negate option.
2962     /// remeber that when parsing the new flag, the option's new flag is prior to the negate option ones.
2963     /// when parsing command line option flags, imported flag is prior to exported one, and the export one is 
2964     /// prior to the ordinary pass-through option flag.
2965     /// `isNegate` decide whether export negate option or non-negate one, default: `false`
2966     /// Params:
2967     ///   flag = the flag(or name) of one of the command's option or negate option
2968     ///   aliasFlags = the new flags
2969     /// Returns: `Self` for chain call
2970     Self exportAs(bool isNegate = false)(string flag, string[] aliasFlags...) {
2971         assert(aliasFlags.length);
2972         if (!this._passThroughOptionValue)
2973             this.passThrough();
2974         static if (isNegate) {
2975             auto flg = imExAsImpl!(NegateOption, true)(flag, aliasFlags);
2976             enum string word = "negate ";
2977         }
2978         else {
2979             auto flg = imExAsImpl!(Option, true)(flag, aliasFlags);
2980             enum string word = "";
2981         }
2982         if (!flg)
2983             this.error(format("cannot find the %soption `%s` in `Command.exportAs` in command `%s`",
2984                     word, flag, this._name));
2985         return this;
2986     }
2987 
2988     /// export the neagte option flag, see `Command.exportAs`
2989     alias exportNAs = exportAs!true;
2990 
2991     private bool imExAsImpl(T, bool isExport = false)(string flag, string[] aliasFlags...)
2992             if (is(T == Option) || is(T == NegateOption)) {
2993         static if (is(T == Option)) {
2994             alias find_opt(alias cmd) = cmd._findOption;
2995             static if (isExport)
2996                 alias _map = this._export_map;
2997             else
2998                 alias _map = this._import_map;
2999         }
3000         else {
3001             alias find_opt(alias cmd) = cmd._findNegateOption;
3002             static if (isExport)
3003                 alias _map = this._export_n_map;
3004             else
3005                 alias _map = this._import_n_map;
3006         }
3007         if (T opt = find_opt!(this)(flag)) {
3008             auto tmp = aliasFlags.uniq;
3009             if (_map is null) {
3010                 _map = tmp.map!(f => tuple(f, opt)).assocArray;
3011                 return true;
3012             }
3013             tmp.filter!(f => _map.byKey.count(f) == 0)
3014                 .each!((f) { _map[f] = opt; });
3015             return true;
3016         }
3017         return false;
3018     }
3019 
3020 package:
3021     void _exitErr(string msg, string code = "") const {
3022         this._outputConfiguration.writeErr("ERROR:\t" ~ msg ~ " " ~ code ~ "\n");
3023         if (this._showHelpAfterError) {
3024             this._outputConfiguration.writeErr("\n");
3025             this.outputHelp(true);
3026         }
3027         debug this.error();
3028         this._exit(1);
3029     }
3030 
3031     void _exitSuccessfully() const {
3032         _exit(0);
3033     }
3034 
3035     void _exit(ubyte exitCode) const {
3036         import core.stdc.stdlib : exit;
3037 
3038         exit(exitCode);
3039     }
3040 
3041 public:
3042     /// invoke error when parsing command line
3043     /// Params:
3044     ///   msg = the error message
3045     ///   code = the code to tag the error
3046     void parsingError(string msg = "", string code = "command.error") const {
3047         this._exitErr(msg, code);
3048     }
3049 
3050     /// invoke error when configuring command
3051     /// Params:
3052     ///   msg = the error message
3053     ///   code = the code to tag the error
3054     void error(string msg = "", string code = "command.error") const {
3055         throw new CMDLineError(msg, 1, code);
3056     }
3057 
3058     /// get the usage of command
3059     string usage() const {
3060         if (this._usage == "") {
3061             string[] args_str = _arguments.map!(arg => arg.readableArgName).array;
3062             foreach (string key; this._argToOptNames) {
3063                 auto opt = _findOption(key);
3064                 args_str ~= "[(" ~ opt.name ~ ")]";
3065             }
3066             string[] seed = [
3067             ];
3068             return "" ~ (
3069                 seed ~
3070                     (_options.length || _addImplicitHelpOption ? "[options]" : [
3071                         ]) ~
3072                     (_commands.length ? "[command]" : [
3073                         ]) ~
3074                     (_arguments.length || this._argToOptNames.length ? args_str : [
3075                         ])
3076             ).join(" ");
3077         }
3078         return this._usage;
3079     }
3080 
3081     /// set the usage of command
3082     /// Params:
3083     ///   str = the usage string. if `str` is `""`, then will automatically generate the usage for command
3084     /// Returns: `Self` for chain call
3085     Self usage(string str) {
3086         Command command = this;
3087         if (this._commands.length != 0 && this
3088             ._commands[$ - 1]
3089             ._execHandler) {
3090             command = this._commands[$ - 1];
3091         }
3092         if (str == "") {
3093             string[] args_str = _arguments.map!(arg => arg.readableArgName).array;
3094             foreach (string key; this._argToOptNames) {
3095                 auto opt = _findOption(key);
3096                 args_str ~= "[(" ~ opt.name ~ ")]";
3097             }
3098             string[] seed = [
3099             ];
3100             command._usage = "" ~ (
3101                 seed ~
3102                     (_options.length || _addImplicitHelpOption ? "[options]" : [
3103                         ]) ~
3104                     (_commands.length ? "[command]" : [
3105                         ]) ~
3106                     (_arguments.length || this._argToOptNames.length ? args_str : [
3107                         ])
3108             ).join(" ");
3109         }
3110         else
3111             command._usage = str;
3112         return this;
3113     }
3114 
3115     /// get sub command by name
3116     alias findCommand = _findCommand;
3117     /// get option by name, short flag and long flag
3118     alias findOption = _findOption;
3119     /// get negate option by name, short flag and long flag
3120     alias findNOption = _findNegateOption;
3121     /// get argument by name
3122     alias findArgument = _findArgument;
3123 }
3124 
3125 unittest {
3126     auto cmd = new Command("cmdline");
3127     assert(cmd._allowExcessArguments);
3128     cmd.description("this is test");
3129     assert("description: this is test" == cmd.description);
3130     cmd.description("this is test", [
3131             "first": "1st",
3132             "second": "2nd"
3133         ]);
3134     assert(
3135         cmd._argsDescription == [
3136             "first": "1st",
3137             "second": "2nd"
3138         ]);
3139     cmd.setVersion("0.0.1");
3140     // cmd.emit("command:version");
3141     // cmd.emit("option:version");
3142 }
3143 
3144 /// create a command by name
3145 Command createCommand(string name) {
3146     return new Command(name);
3147 }
3148 
3149 /// create a command by the flag that contains its name and arguments with description
3150 /// see `Command.command(Args...)(string nameAndArgs, string desc = "")`
3151 Command createCommand(Args...)(string nameAndArgs, string desc = "") {
3152     auto caputures = matchFirst(nameAndArgs, PTN_CMDNAMEANDARGS);
3153     string name = caputures[1], _args = caputures[2];
3154     assert(name != "");
3155     auto cmd = createCommand(name);
3156     cmd.description(desc);
3157     if (_args != "")
3158         cmd.arguments!Args(_args);
3159     return cmd;
3160 }
3161 
3162 /// the output type used in command
3163 class OutputConfiguration {
3164     void function(string str) writeOut = (
3165         string str) => stdout.write(
3166         str);
3167     void function(
3168         string str) writeErr = (
3169         string str) => stderr.write(
3170         str);
3171     int function() getOutHelpWidth = &_getOutHelpWidth;
3172     int function() getErrHelpWidth = &_getOutHelpWidth;
3173     void outputError(alias fn)(string str) {
3174         fn(str);
3175     }
3176 }
3177 
3178 version (Windows) {
3179     package int _getOutHelpWidth() {
3180         HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
3181         CONSOLE_SCREEN_BUFFER_INFO csbi;
3182         GetConsoleScreenBufferInfo(hConsole, &csbi);
3183         return csbi.srWindow.Right - csbi.srWindow.Left + 1;
3184     }
3185 }
3186 else version (Posix) {
3187     package int _getOutHelpWidth() {
3188         Tuple!(int, string) result = executeShell("stty size");
3189         string tmp = result[1].strip;
3190         return tmp.split(" ")[1].to!int;
3191     }
3192 }
3193 
3194 /// the wrap of option value map
3195 struct OptsWrap {
3196     private OptionVariant[string] innerValue;
3197 
3198     /// enable getting option value wrapped by `ArgWrap` by name using call form
3199     ArgWrap opCall(string member) const {
3200         auto ptr = member in innerValue;
3201         if (ptr) {
3202             return ArgWrap(*ptr);
3203         }
3204         else
3205             return ArgWrap(null);
3206     }
3207 
3208     package this(inout OptionVariant[string] v) inout {
3209         innerValue = v;
3210     }
3211 }
3212 
3213 /// the wrap of option and argument value
3214 struct ArgWrap {
3215     private ArgNullable innerValue;
3216 
3217     package this(in ArgVariant value) {
3218         this.innerValue = value;
3219     }
3220 
3221     package this(typeof(null) n) {
3222         this.innerValue = n;
3223     }
3224 
3225     /// test wheter the inner value is valid
3226     @property
3227     bool isValid() inout {
3228         return !innerValue.isNull;
3229     }
3230 
3231     /// enable implicitly convert to `bool` type representing `this.isValid`
3232     alias isValid this;
3233 
3234     /// get the innner type, remember use it after test whether it is valid
3235     T get(T)() const if (isArgValueType!T) {
3236         bool is_type = testType!T(this.innerValue);
3237         if (!is_type) {
3238             throw new CMDLineError("the inner type is not " ~ T.stringof);
3239         }
3240         return cast(T) this.innerValue.get!T;
3241     }
3242 
3243     /// test whether the type is the innner vlalue type
3244     bool verifyType(T)() const {
3245         return testType!T(this.innerValue);
3246     }
3247 
3248     /// enable the assign oparator with innner type that allow by `ArgWrap`
3249     auto opAssign(T)(T value) if (isArgValueType!T || is(T == typeof(null))) {
3250         this.innerValue = value;
3251         return this;
3252     }
3253 
3254     /// enbale the explicity cast that can get the inner value
3255     T opCast(T)() const if (isArgValueType!T) {
3256         return this.get!T;
3257     }
3258 }
3259 
3260 unittest {
3261     writeln(_getOutHelpWidth());
3262 
3263     OutputConfiguration outputConfig = new OutputConfiguration;
3264     assert(outputConfig.getOutHelpWidth() == _getOutHelpWidth());
3265 }