| // Written in the D programming language. | 
 |  | 
 | /** | 
 | Processing of command line options. | 
 |  | 
 | The getopt module implements a `getopt` function, which adheres to | 
 | the POSIX syntax for command line options. GNU extensions are | 
 | supported in the form of long options introduced by a double dash | 
 | ("--"). Support for bundling of command line options, as was the case | 
 | with the more traditional single-letter approach, is provided but not | 
 | enabled by default. | 
 |  | 
 | Copyright: Copyright Andrei Alexandrescu 2008 - 2015. | 
 | License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). | 
 | Authors:   $(HTTP erdani.org, Andrei Alexandrescu) | 
 | Credits:   This module and its documentation are inspired by Perl's | 
 |            $(HTTPS perldoc.perl.org/Getopt/Long.html, Getopt::Long) module. The syntax of | 
 |            D's `getopt` is simpler than its Perl counterpart because $(D | 
 |            getopt) infers the expected parameter types from the static types of | 
 |            the passed-in pointers. | 
 | Source:    $(PHOBOSSRC std/getopt.d) | 
 | */ | 
 | /* | 
 |          Copyright Andrei Alexandrescu 2008 - 2015. | 
 | Distributed under the Boost Software License, Version 1.0. | 
 |    (See accompanying file LICENSE_1_0.txt or copy at | 
 |          http://www.boost.org/LICENSE_1_0.txt) | 
 | */ | 
 | module std.getopt; | 
 |  | 
 | import std.exception : basicExceptionCtors; | 
 | import std.traits; | 
 |  | 
 | /** | 
 | Thrown on one of the following conditions: | 
 | $(UL | 
 |   $(LI An unrecognized command-line argument is passed, and | 
 |        `std.getopt.config.passThrough` was not present.) | 
 |   $(LI A command-line option was not found, and | 
 |        `std.getopt.config.required` was present.) | 
 |   $(LI A callback option is missing a value.) | 
 | ) | 
 | */ | 
 | class GetOptException : Exception | 
 | { | 
 |     mixin basicExceptionCtors; | 
 | } | 
 |  | 
 | static assert(is(typeof(new GetOptException("message")))); | 
 | static assert(is(typeof(new GetOptException("message", Exception.init)))); | 
 |  | 
 | /** | 
 |    Parse and remove command line options from a string array. | 
 |  | 
 |    Synopsis: | 
 |  | 
 | --------- | 
 | import std.getopt; | 
 |  | 
 | string data = "file.dat"; | 
 | int length = 24; | 
 | bool verbose; | 
 | enum Color { no, yes }; | 
 | Color color; | 
 |  | 
 | void main(string[] args) | 
 | { | 
 |   auto helpInformation = getopt( | 
 |     args, | 
 |     "length",  &length,    // numeric | 
 |     "file",    &data,      // string | 
 |     "verbose", &verbose,   // flag | 
 |     "color", "Information about this color", &color);    // enum | 
 |   ... | 
 |  | 
 |   if (helpInformation.helpWanted) | 
 |   { | 
 |     defaultGetoptPrinter("Some information about the program.", | 
 |       helpInformation.options); | 
 |   } | 
 | } | 
 | --------- | 
 |  | 
 |  The `getopt` function takes a reference to the command line | 
 |  (as received by `main`) as its first argument, and an | 
 |  unbounded number of pairs of strings and pointers. Each string is an | 
 |  option meant to "fill" the value referenced by the pointer to its | 
 |  right (the "bound" pointer). The option string in the call to | 
 |  `getopt` should not start with a dash. | 
 |  | 
 |  In all cases, the command-line options that were parsed and used by | 
 |  `getopt` are removed from `args`. Whatever in the | 
 |  arguments did not look like an option is left in `args` for | 
 |  further processing by the program. Values that were unaffected by the | 
 |  options are not touched, so a common idiom is to initialize options | 
 |  to their defaults and then invoke `getopt`. If a | 
 |  command-line argument is recognized as an option with a parameter and | 
 |  the parameter cannot be parsed properly (e.g., a number is expected | 
 |  but not present), a `ConvException` exception is thrown. | 
 |  If `std.getopt.config.passThrough` was not passed to `getopt` | 
 |  and an unrecognized command-line argument is found, or if a required | 
 |  argument is missing a `GetOptException` is thrown. | 
 |  | 
 |  Depending on the type of the pointer being bound, `getopt` | 
 |  recognizes the following kinds of options: | 
 |  | 
 |  $(OL | 
 |     $(LI $(I Boolean options). A lone argument sets the option to `true`. | 
 |     Additionally $(B true) or $(B false) can be set within the option separated | 
 |     with an "=" sign: | 
 |  | 
 | --------- | 
 |   bool verbose = false, debugging = true; | 
 |   getopt(args, "verbose", &verbose, "debug", &debugging); | 
 | --------- | 
 |  | 
 |     To set `verbose` to `true`, invoke the program with either | 
 |     `--verbose` or `--verbose=true`. | 
 |  | 
 |     To set `debugging` to `false`, invoke the program with | 
 |     `--debugging=false`. | 
 |     ) | 
 |  | 
 |     $(LI $(I Numeric options.) If an option is bound to a numeric type, a | 
 |     number is expected as the next option, or right within the option separated | 
 |     with an "=" sign: | 
 |  | 
 | --------- | 
 |   uint timeout; | 
 |   getopt(args, "timeout", &timeout); | 
 | --------- | 
 |  | 
 |     To set `timeout` to `5`, invoke the program with either | 
 |     `--timeout=5` or $(D --timeout 5). | 
 |     ) | 
 |  | 
 |     $(LI $(I Incremental options.) If an option name has a "+" suffix and is | 
 |     bound to a numeric type, then the option's value tracks the number of times | 
 |     the option occurred on the command line: | 
 |  | 
 | --------- | 
 |   uint paranoid; | 
 |   getopt(args, "paranoid+", ¶noid); | 
 | --------- | 
 |  | 
 |     Invoking the program with "--paranoid --paranoid --paranoid" will set $(D | 
 |     paranoid) to 3. Note that an incremental option never expects a parameter, | 
 |     e.g., in the command line "--paranoid 42 --paranoid", the "42" does not set | 
 |     `paranoid` to 42; instead, `paranoid` is set to 2 and "42" is not | 
 |     considered as part of the normal program arguments. | 
 |     ) | 
 |  | 
 |     $(LI $(I Enum options.) If an option is bound to an enum, an enum symbol as | 
 |     a string is expected as the next option, or right within the option | 
 |     separated with an "=" sign: | 
 |  | 
 | --------- | 
 |   enum Color { no, yes }; | 
 |   Color color; // default initialized to Color.no | 
 |   getopt(args, "color", &color); | 
 | --------- | 
 |  | 
 |     To set `color` to `Color.yes`, invoke the program with either | 
 |     `--color=yes` or $(D --color yes). | 
 |     ) | 
 |  | 
 |     $(LI $(I String options.) If an option is bound to a string, a string is | 
 |     expected as the next option, or right within the option separated with an | 
 |     "=" sign: | 
 |  | 
 | --------- | 
 | string outputFile; | 
 | getopt(args, "output", &outputFile); | 
 | --------- | 
 |  | 
 |     Invoking the program with "--output=myfile.txt" or "--output myfile.txt" | 
 |     will set `outputFile` to "myfile.txt". If you want to pass a string | 
 |     containing spaces, you need to use the quoting that is appropriate to your | 
 |     shell, e.g. --output='my file.txt'. | 
 |     ) | 
 |  | 
 |     $(LI $(I Array options.) If an option is bound to an array, a new element | 
 |     is appended to the array each time the option occurs: | 
 |  | 
 | --------- | 
 | string[] outputFiles; | 
 | getopt(args, "output", &outputFiles); | 
 | --------- | 
 |  | 
 |     Invoking the program with "--output=myfile.txt --output=yourfile.txt" or | 
 |     "--output myfile.txt --output yourfile.txt" will set `outputFiles` to | 
 |     $(D [ "myfile.txt", "yourfile.txt" ]). | 
 |  | 
 |     Alternatively you can set $(LREF arraySep) to allow multiple elements in | 
 |     one parameter. | 
 |  | 
 | --------- | 
 | string[] outputFiles; | 
 | arraySep = ",";  // defaults to "", meaning one element per parameter | 
 | getopt(args, "output", &outputFiles); | 
 | --------- | 
 |  | 
 |     With the above code you can invoke the program with | 
 |     "--output=myfile.txt,yourfile.txt", or "--output myfile.txt,yourfile.txt".) | 
 |  | 
 |     $(LI $(I Hash options.) If an option is bound to an associative array, a | 
 |     string of the form "name=value" is expected as the next option, or right | 
 |     within the option separated with an "=" sign: | 
 |  | 
 | --------- | 
 | double[string] tuningParms; | 
 | getopt(args, "tune", &tuningParms); | 
 | --------- | 
 |  | 
 |     Invoking the program with e.g. "--tune=alpha=0.5 --tune beta=0.6" will set | 
 |     `tuningParms` to [ "alpha" : 0.5, "beta" : 0.6 ]. | 
 |  | 
 |     Alternatively you can set $(LREF arraySep) as the element separator: | 
 |  | 
 | --------- | 
 | double[string] tuningParms; | 
 | arraySep = ",";  // defaults to "", meaning one element per parameter | 
 | getopt(args, "tune", &tuningParms); | 
 | --------- | 
 |  | 
 |     With the above code you can invoke the program with | 
 |     "--tune=alpha=0.5,beta=0.6", or "--tune alpha=0.5,beta=0.6". | 
 |  | 
 |     In general, the keys and values can be of any parsable types. | 
 |     ) | 
 |  | 
 |     $(LI $(I Callback options.) An option can be bound to a function or | 
 |     delegate with the signature $(D void function()), $(D void function(string | 
 |     option)), $(D void function(string option, string value)), or their | 
 |     delegate equivalents. | 
 |  | 
 |     $(UL | 
 |         $(LI If the callback doesn't take any arguments, the callback is | 
 |         invoked whenever the option is seen. | 
 |         ) | 
 |  | 
 |         $(LI If the callback takes one string argument, the option string | 
 |         (without the leading dash(es)) is passed to the callback.  After that, | 
 |         the option string is considered handled and removed from the options | 
 |         array. | 
 |  | 
 | --------- | 
 | void main(string[] args) | 
 | { | 
 |   uint verbosityLevel = 1; | 
 |   void myHandler(string option) | 
 |   { | 
 |     if (option == "quiet") | 
 |     { | 
 |       verbosityLevel = 0; | 
 |     } | 
 |     else | 
 |     { | 
 |       assert(option == "verbose"); | 
 |       verbosityLevel = 2; | 
 |     } | 
 |   } | 
 |   getopt(args, "verbose", &myHandler, "quiet", &myHandler); | 
 | } | 
 | --------- | 
 |  | 
 |         ) | 
 |  | 
 |         $(LI If the callback takes two string arguments, the option string is | 
 |         handled as an option with one argument, and parsed accordingly. The | 
 |         option and its value are passed to the callback. After that, whatever | 
 |         was passed to the callback is considered handled and removed from the | 
 |         list. | 
 |  | 
 | --------- | 
 | int main(string[] args) | 
 | { | 
 |   uint verbosityLevel = 1; | 
 |   bool handlerFailed = false; | 
 |   void myHandler(string option, string value) | 
 |   { | 
 |     switch (value) | 
 |     { | 
 |       case "quiet": verbosityLevel = 0; break; | 
 |       case "verbose": verbosityLevel = 2; break; | 
 |       case "shouting": verbosityLevel = verbosityLevel.max; break; | 
 |       default : | 
 |         stderr.writeln("Unknown verbosity level ", value); | 
 |         handlerFailed = true; | 
 |         break; | 
 |     } | 
 |   } | 
 |   getopt(args, "verbosity", &myHandler); | 
 |   return handlerFailed ? 1 : 0; | 
 | } | 
 | --------- | 
 |         ) | 
 |     )) | 
 | ) | 
 |  | 
 | Options_with_multiple_names: | 
 | Sometimes option synonyms are desirable, e.g. "--verbose", | 
 | "--loquacious", and "--garrulous" should have the same effect. Such | 
 | alternate option names can be included in the option specification, | 
 | using "|" as a separator: | 
 |  | 
 | --------- | 
 | bool verbose; | 
 | getopt(args, "verbose|loquacious|garrulous", &verbose); | 
 | --------- | 
 |  | 
 | Case: | 
 | By default options are case-insensitive. You can change that behavior | 
 | by passing `getopt` the `caseSensitive` directive like this: | 
 |  | 
 | --------- | 
 | bool foo, bar; | 
 | getopt(args, | 
 |     std.getopt.config.caseSensitive, | 
 |     "foo", &foo, | 
 |     "bar", &bar); | 
 | --------- | 
 |  | 
 | In the example above, "--foo" and "--bar" are recognized, but "--Foo", "--Bar", | 
 | "--FOo", "--bAr", etc. are rejected. | 
 | The directive is active until the end of `getopt`, or until the | 
 | converse directive `caseInsensitive` is encountered: | 
 |  | 
 | --------- | 
 | bool foo, bar; | 
 | getopt(args, | 
 |     std.getopt.config.caseSensitive, | 
 |     "foo", &foo, | 
 |     std.getopt.config.caseInsensitive, | 
 |     "bar", &bar); | 
 | --------- | 
 |  | 
 | The option "--Foo" is rejected due to $(D | 
 | std.getopt.config.caseSensitive), but not "--Bar", "--bAr" | 
 | etc. because the directive $(D | 
 | std.getopt.config.caseInsensitive) turned sensitivity off before | 
 | option "bar" was parsed. | 
 |  | 
 | Short_versus_long_options: | 
 | Traditionally, programs accepted single-letter options preceded by | 
 | only one dash (e.g. `-t`). `getopt` accepts such parameters | 
 | seamlessly. When used with a double-dash (e.g. `--t`), a | 
 | single-letter option behaves the same as a multi-letter option. When | 
 | used with a single dash, a single-letter option is accepted. | 
 |  | 
 | To set `timeout` to `5`, use either of the following: `--timeout=5`, | 
 | `--timeout 5`, `--t=5`, `--t 5`, `-t5`, or `-t 5`. Forms such as | 
 | `-timeout=5` will be not accepted. | 
 |  | 
 | For more details about short options, refer also to the next section. | 
 |  | 
 | Bundling: | 
 | Single-letter options can be bundled together, i.e. "-abc" is the same as | 
 | $(D "-a -b -c"). By default, this option is turned off. You can turn it on | 
 | with the `std.getopt.config.bundling` directive: | 
 |  | 
 | --------- | 
 | bool foo, bar; | 
 | getopt(args, | 
 |     std.getopt.config.bundling, | 
 |     "foo|f", &foo, | 
 |     "bar|b", &bar); | 
 | --------- | 
 |  | 
 | In case you want to only enable bundling for some of the parameters, | 
 | bundling can be turned off with `std.getopt.config.noBundling`. | 
 |  | 
 | Required: | 
 | An option can be marked as required. If that option is not present in the | 
 | arguments an exception will be thrown. | 
 |  | 
 | --------- | 
 | bool foo, bar; | 
 | getopt(args, | 
 |     std.getopt.config.required, | 
 |     "foo|f", &foo, | 
 |     "bar|b", &bar); | 
 | --------- | 
 |  | 
 | Only the option directly following `std.getopt.config.required` is | 
 | required. | 
 |  | 
 | Passing_unrecognized_options_through: | 
 | If an application needs to do its own processing of whichever arguments | 
 | `getopt` did not understand, it can pass the | 
 | `std.getopt.config.passThrough` directive to `getopt`: | 
 |  | 
 | --------- | 
 | bool foo, bar; | 
 | getopt(args, | 
 |     std.getopt.config.passThrough, | 
 |     "foo", &foo, | 
 |     "bar", &bar); | 
 | --------- | 
 |  | 
 | An unrecognized option such as "--baz" will be found untouched in | 
 | `args` after `getopt` returns. | 
 |  | 
 | Help_Information_Generation: | 
 | If an option string is followed by another string, this string serves as a | 
 | description for this option. The `getopt` function returns a struct of type | 
 | `GetoptResult`. This return value contains information about all passed options | 
 | as well a $(D bool GetoptResult.helpWanted) flag indicating whether information | 
 | about these options was requested. The `getopt` function always adds an option for | 
 | `--help|-h` to set the flag if the option is seen on the command line. | 
 |  | 
 | Options_Terminator: | 
 | A lone double-dash terminates `getopt` gathering. It is used to | 
 | separate program options from other parameters (e.g., options to be passed | 
 | to another program). Invoking the example above with $(D "--foo -- --bar") | 
 | parses foo but leaves "--bar" in `args`. The double-dash itself is | 
 | removed from the argument array unless the `std.getopt.config.keepEndOfOptions` | 
 | directive is given. | 
 | */ | 
 | GetoptResult getopt(T...)(ref string[] args, T opts) | 
 | { | 
 |     import std.exception : enforce; | 
 |     enforce(args.length, | 
 |             "Invalid arguments string passed: program name missing"); | 
 |     configuration cfg; | 
 |     GetoptResult rslt; | 
 |  | 
 |     GetOptException excep; | 
 |     void[][string] visitedLongOpts, visitedShortOpts; | 
 |     getoptImpl(args, cfg, rslt, excep, visitedLongOpts, visitedShortOpts, opts); | 
 |  | 
 |     if (!rslt.helpWanted && excep !is null) | 
 |     { | 
 |         throw excep; | 
 |     } | 
 |  | 
 |     return rslt; | 
 | } | 
 |  | 
 | /// | 
 | @safe unittest | 
 | { | 
 |     auto args = ["prog", "--foo", "-b"]; | 
 |  | 
 |     bool foo; | 
 |     bool bar; | 
 |     auto rslt = getopt(args, "foo|f", "Some information about foo.", &foo, "bar|b", | 
 |         "Some help message about bar.", &bar); | 
 |  | 
 |     if (rslt.helpWanted) | 
 |     { | 
 |         defaultGetoptPrinter("Some information about the program.", | 
 |             rslt.options); | 
 |     } | 
 | } | 
 |  | 
 | /** | 
 |    Configuration options for `getopt`. | 
 |  | 
 |    You can pass them to `getopt` in any position, except in between an option | 
 |    string and its bound pointer. | 
 | */ | 
 | enum config { | 
 |     /// Turn case sensitivity on | 
 |     caseSensitive, | 
 |     /// Turn case sensitivity off (default) | 
 |     caseInsensitive, | 
 |     /// Turn bundling on | 
 |     bundling, | 
 |     /// Turn bundling off (default) | 
 |     noBundling, | 
 |     /// Pass unrecognized arguments through | 
 |     passThrough, | 
 |     /// Signal unrecognized arguments as errors (default) | 
 |     noPassThrough, | 
 |     /// Stop at first argument that does not look like an option | 
 |     stopOnFirstNonOption, | 
 |     /// Do not erase the endOfOptions separator from args | 
 |     keepEndOfOptions, | 
 |     /// Make the next option a required option | 
 |     required | 
 | } | 
 |  | 
 | /** The result of the `getopt` function. | 
 |  | 
 | `helpWanted` is set if the option `--help` or `-h` was passed to the option parser. | 
 | */ | 
 | struct GetoptResult { | 
 |     bool helpWanted; /// Flag indicating if help was requested | 
 |     Option[] options; /// All possible options | 
 | } | 
 |  | 
 | /** Information about an option. | 
 | */ | 
 | struct Option { | 
 |     string optShort; /// The short symbol for this option | 
 |     string optLong; /// The long symbol for this option | 
 |     string help; /// The description of this option | 
 |     bool required; /// If a option is required, not passing it will result in an error | 
 | } | 
 |  | 
 | private pure Option splitAndGet(string opt) @trusted nothrow | 
 | { | 
 |     import std.array : split; | 
 |     auto sp = split(opt, "|"); | 
 |     Option ret; | 
 |     if (sp.length > 1) | 
 |     { | 
 |         ret.optShort = "-" ~ (sp[0].length < sp[1].length ? | 
 |             sp[0] : sp[1]); | 
 |         ret.optLong = "--" ~ (sp[0].length > sp[1].length ? | 
 |             sp[0] : sp[1]); | 
 |     } | 
 |     else if (sp[0].length > 1) | 
 |     { | 
 |         ret.optLong = "--" ~ sp[0]; | 
 |     } | 
 |     else | 
 |     { | 
 |         ret.optShort = "-" ~ sp[0]; | 
 |     } | 
 |  | 
 |     return ret; | 
 | } | 
 |  | 
 | @safe unittest | 
 | { | 
 |     auto oshort = splitAndGet("f"); | 
 |     assert(oshort.optShort == "-f"); | 
 |     assert(oshort.optLong == ""); | 
 |  | 
 |     auto olong = splitAndGet("foo"); | 
 |     assert(olong.optShort == ""); | 
 |     assert(olong.optLong == "--foo"); | 
 |  | 
 |     auto oshortlong = splitAndGet("f|foo"); | 
 |     assert(oshortlong.optShort == "-f"); | 
 |     assert(oshortlong.optLong == "--foo"); | 
 |  | 
 |     auto olongshort = splitAndGet("foo|f"); | 
 |     assert(olongshort.optShort == "-f"); | 
 |     assert(olongshort.optLong == "--foo"); | 
 | } | 
 |  | 
 | /* | 
 | This function verifies that the variadic parameters passed in getOpt | 
 | follow this pattern: | 
 |  | 
 |   [config override], option, [description], receiver, | 
 |  | 
 |  - config override: a config value, optional | 
 |  - option:          a string or a char | 
 |  - description:     a string, optional | 
 |  - receiver:        a pointer or a callable | 
 | */ | 
 | private template optionValidator(A...) | 
 | { | 
 |     import std.format : format; | 
 |  | 
 |     enum fmt = "getopt validator: %s (at position %d)"; | 
 |     enum isReceiver(T) = is(T == U*, U) || (is(T == function)) || (is(T == delegate)); | 
 |     enum isOptionStr(T) = isSomeString!T || isSomeChar!T; | 
 |  | 
 |     auto validator() | 
 |     { | 
 |         string msg; | 
 |         static if (A.length > 0) | 
 |         { | 
 |             static if (isReceiver!(A[0])) | 
 |             { | 
 |                 msg = format(fmt, "first argument must be a string or a config", 0); | 
 |             } | 
 |             else static if (!isOptionStr!(A[0]) && !is(A[0] == config)) | 
 |             { | 
 |                 msg = format(fmt, "invalid argument type: " ~ A[0].stringof, 0); | 
 |             } | 
 |             else | 
 |             { | 
 |                 static foreach (i; 1 .. A.length) | 
 |                 { | 
 |                     static if (!isReceiver!(A[i]) && !isOptionStr!(A[i]) && | 
 |                         !(is(A[i] == config))) | 
 |                     { | 
 |                         msg = format(fmt, "invalid argument type: " ~ A[i].stringof, i); | 
 |                         goto end; | 
 |                     } | 
 |                     else static if (isReceiver!(A[i]) && !isOptionStr!(A[i-1])) | 
 |                     { | 
 |                         msg = format(fmt, "a receiver can not be preceeded by a receiver", i); | 
 |                         goto end; | 
 |                     } | 
 |                     else static if (i > 1 && isOptionStr!(A[i]) && isOptionStr!(A[i-1]) | 
 |                         && isSomeString!(A[i-2])) | 
 |                     { | 
 |                         msg = format(fmt, "a string can not be preceeded by two strings", i); | 
 |                         goto end; | 
 |                     } | 
 |                 } | 
 |             } | 
 |             static if (!isReceiver!(A[$-1]) && !is(A[$-1] == config)) | 
 |             { | 
 |                 msg = format(fmt, "last argument must be a receiver or a config", | 
 |                     A.length -1); | 
 |             } | 
 |         } | 
 |     end: | 
 |         return msg; | 
 |     } | 
 |     enum message = validator; | 
 |     alias optionValidator = message; | 
 | } | 
 |  | 
 | private auto getoptTo(R)(string option, string value, | 
 |         size_t idx, string file = __FILE__, size_t line = __LINE__) | 
 | { | 
 |     import std.conv : to, ConvException; | 
 |     import std.format : format; | 
 |     try | 
 |     { | 
 |         return to!R(value); | 
 |     } | 
 |     catch (ConvException e) | 
 |     { | 
 |         throw new ConvException(format("Argument '%s' at position '%u' could " | 
 |             ~ "not be converted to type '%s' as required by option '%s'.", | 
 |             value, idx, R.stringof, option), e, file, line); | 
 |     } | 
 | } | 
 |  | 
 | @safe pure unittest | 
 | { | 
 |     alias P = void*; | 
 |     alias S = string; | 
 |     alias A = char; | 
 |     alias C = config; | 
 |     alias F = void function(); | 
 |  | 
 |     static assert(optionValidator!(S,P) == ""); | 
 |     static assert(optionValidator!(S,F) == ""); | 
 |     static assert(optionValidator!(A,P) == ""); | 
 |     static assert(optionValidator!(A,F) == ""); | 
 |  | 
 |     static assert(optionValidator!(C,S,P) == ""); | 
 |     static assert(optionValidator!(C,S,F) == ""); | 
 |     static assert(optionValidator!(C,A,P) == ""); | 
 |     static assert(optionValidator!(C,A,F) == ""); | 
 |  | 
 |     static assert(optionValidator!(C,S,S,P) == ""); | 
 |     static assert(optionValidator!(C,S,S,F) == ""); | 
 |     static assert(optionValidator!(C,A,S,P) == ""); | 
 |     static assert(optionValidator!(C,A,S,F) == ""); | 
 |  | 
 |     static assert(optionValidator!(C,S,S,P) == ""); | 
 |     static assert(optionValidator!(C,S,S,P,C,S,F) == ""); | 
 |     static assert(optionValidator!(C,S,P,C,S,S,F) == ""); | 
 |  | 
 |     static assert(optionValidator!(C,A,P,A,S,F) == ""); | 
 |     static assert(optionValidator!(C,A,P,C,A,S,F) == ""); | 
 |  | 
 |     static assert(optionValidator!(P,S,S) != ""); | 
 |     static assert(optionValidator!(P,P,S) != ""); | 
 |     static assert(optionValidator!(P,F,S,P) != ""); | 
 |     static assert(optionValidator!(C,C,S) != ""); | 
 |     static assert(optionValidator!(S,S,P,S,S,P,S) != ""); | 
 |     static assert(optionValidator!(S,S,P,P) != ""); | 
 |     static assert(optionValidator!(S,S,S,P) != ""); | 
 |  | 
 |     static assert(optionValidator!(C,A,S,P,C,A,F) == ""); | 
 |     static assert(optionValidator!(C,A,P,C,A,S,F) == ""); | 
 | } | 
 |  | 
 | // https://issues.dlang.org/show_bug.cgi?id=15914 | 
 | @safe unittest | 
 | { | 
 |     import std.exception : assertThrown; | 
 |     bool opt; | 
 |     string[] args = ["program", "-a"]; | 
 |     getopt(args, config.passThrough, 'a', &opt); | 
 |     assert(opt); | 
 |     opt = false; | 
 |     args = ["program", "-a"]; | 
 |     getopt(args, 'a', &opt); | 
 |     assert(opt); | 
 |     opt = false; | 
 |     args = ["program", "-a"]; | 
 |     getopt(args, 'a', "help string", &opt); | 
 |     assert(opt); | 
 |     opt = false; | 
 |     args = ["program", "-a"]; | 
 |     getopt(args, config.caseSensitive, 'a', "help string", &opt); | 
 |     assert(opt); | 
 |  | 
 |     assertThrown(getopt(args, "", "forgot to put a string", &opt)); | 
 | } | 
 |  | 
 | private void getoptImpl(T...)(ref string[] args, ref configuration cfg, | 
 |     ref GetoptResult rslt, ref GetOptException excep, | 
 |     void[][string] visitedLongOpts, void[][string] visitedShortOpts, T opts) | 
 | { | 
 |     enum validationMessage = optionValidator!T; | 
 |     static assert(validationMessage == "", validationMessage); | 
 |  | 
 |     import std.algorithm.mutation : remove; | 
 |     import std.conv : to; | 
 |     import std.uni : toLower; | 
 |     static if (opts.length) | 
 |     { | 
 |         static if (is(typeof(opts[0]) : config)) | 
 |         { | 
 |             // it's a configuration flag, act on it | 
 |             setConfig(cfg, opts[0]); | 
 |             return getoptImpl(args, cfg, rslt, excep, visitedLongOpts, | 
 |                 visitedShortOpts, opts[1 .. $]); | 
 |         } | 
 |         else | 
 |         { | 
 |             // it's an option string | 
 |             auto option = to!string(opts[0]); | 
 |             if (option.length == 0) | 
 |             { | 
 |                 excep = new GetOptException("An option name may not be an empty string", excep); | 
 |                 return; | 
 |             } | 
 |             Option optionHelp = splitAndGet(option); | 
 |             optionHelp.required = cfg.required; | 
 |  | 
 |             if (optionHelp.optLong.length) | 
 |             { | 
 |                 auto name = optionHelp.optLong; | 
 |                 if (!cfg.caseSensitive) | 
 |                     name = name.toLower(); | 
 |                 assert(name !in visitedLongOpts, | 
 |                     "Long option " ~ optionHelp.optLong ~ " is multiply defined"); | 
 |  | 
 |                 visitedLongOpts[optionHelp.optLong] = []; | 
 |             } | 
 |  | 
 |             if (optionHelp.optShort.length) | 
 |             { | 
 |                 auto name = optionHelp.optShort; | 
 |                 if (!cfg.caseSensitive) | 
 |                     name = name.toLower(); | 
 |                 assert(name !in visitedShortOpts, | 
 |                     "Short option " ~ optionHelp.optShort | 
 |                     ~ " is multiply defined"); | 
 |  | 
 |                 visitedShortOpts[optionHelp.optShort] = []; | 
 |             } | 
 |  | 
 |             static if (is(typeof(opts[1]) : string)) | 
 |             { | 
 |                 alias receiver = opts[2]; | 
 |                 optionHelp.help = opts[1]; | 
 |                 immutable lowSliceIdx = 3; | 
 |             } | 
 |             else | 
 |             { | 
 |                 alias receiver = opts[1]; | 
 |                 immutable lowSliceIdx = 2; | 
 |             } | 
 |  | 
 |             rslt.options ~= optionHelp; | 
 |  | 
 |             bool incremental; | 
 |             // Handle options of the form --blah+ | 
 |             if (option.length && option[$ - 1] == autoIncrementChar) | 
 |             { | 
 |                 option = option[0 .. $ - 1]; | 
 |                 incremental = true; | 
 |             } | 
 |  | 
 |             bool optWasHandled = handleOption(option, receiver, args, cfg, incremental); | 
 |  | 
 |             if (cfg.required && !optWasHandled) | 
 |             { | 
 |                 excep = new GetOptException("Required option " | 
 |                     ~ option ~ " was not supplied", excep); | 
 |             } | 
 |             cfg.required = false; | 
 |  | 
 |             getoptImpl(args, cfg, rslt, excep, visitedLongOpts, | 
 |                 visitedShortOpts, opts[lowSliceIdx .. $]); | 
 |         } | 
 |     } | 
 |     else | 
 |     { | 
 |         // no more options to look for, potentially some arguments left | 
 |         for (size_t i = 1; i < args.length;) | 
 |         { | 
 |             auto a = args[i]; | 
 |             if (endOfOptions.length && a == endOfOptions) | 
 |             { | 
 |                 // Consume the "--" if keepEndOfOptions is not specified | 
 |                 if (!cfg.keepEndOfOptions) | 
 |                     args = args.remove(i); | 
 |                 break; | 
 |             } | 
 |             if (a.length < 2 || a[0] != optionChar) | 
 |             { | 
 |                 // not an option | 
 |                 if (cfg.stopOnFirstNonOption) break; | 
 |                 ++i; | 
 |                 continue; | 
 |             } | 
 |             if (a == "--help" || a == "-h") | 
 |             { | 
 |                 rslt.helpWanted = true; | 
 |                 args = args.remove(i); | 
 |                 continue; | 
 |             } | 
 |             if (!cfg.passThrough) | 
 |             { | 
 |                 throw new GetOptException("Unrecognized option "~a, excep); | 
 |             } | 
 |             ++i; | 
 |         } | 
 |  | 
 |         Option helpOpt; | 
 |         helpOpt.optShort = "-h"; | 
 |         helpOpt.optLong = "--help"; | 
 |         helpOpt.help = "This help information."; | 
 |         rslt.options ~= helpOpt; | 
 |     } | 
 | } | 
 |  | 
 | private bool handleOption(R)(string option, R receiver, ref string[] args, | 
 |     ref configuration cfg, bool incremental) | 
 | { | 
 |     import std.algorithm.iteration : map, splitter; | 
 |     import std.ascii : isAlpha; | 
 |     import std.conv : text, to; | 
 |     // Scan arguments looking for a match for this option | 
 |     bool ret = false; | 
 |     for (size_t i = 1; i < args.length; ) | 
 |     { | 
 |         auto a = args[i]; | 
 |         if (endOfOptions.length && a == endOfOptions) break; | 
 |         if (cfg.stopOnFirstNonOption && (!a.length || a[0] != optionChar)) | 
 |         { | 
 |             // first non-option is end of options | 
 |             break; | 
 |         } | 
 |         // Unbundle bundled arguments if necessary | 
 |         if (cfg.bundling && a.length > 2 && a[0] == optionChar && | 
 |                 a[1] != optionChar) | 
 |         { | 
 |             string[] expanded; | 
 |             foreach (j, dchar c; a[1 .. $]) | 
 |             { | 
 |                 // If the character is not alpha, stop right there. This allows | 
 |                 // e.g. -j100 to work as "pass argument 100 to option -j". | 
 |                 if (!isAlpha(c)) | 
 |                 { | 
 |                     if (c == '=') | 
 |                         j++; | 
 |                     expanded ~= a[j + 1 .. $]; | 
 |                     break; | 
 |                 } | 
 |                 expanded ~= text(optionChar, c); | 
 |             } | 
 |             args = args[0 .. i] ~ expanded ~ args[i + 1 .. $]; | 
 |             continue; | 
 |         } | 
 |  | 
 |         string val; | 
 |         if (!optMatch(a, option, val, cfg)) | 
 |         { | 
 |             ++i; | 
 |             continue; | 
 |         } | 
 |  | 
 |         ret = true; | 
 |  | 
 |         // found it | 
 |         // from here on, commit to eat args[i] | 
 |         // (and potentially args[i + 1] too, but that comes later) | 
 |         args = args[0 .. i] ~ args[i + 1 .. $]; | 
 |  | 
 |         static if (is(typeof(*receiver))) | 
 |             alias Target = typeof(*receiver); | 
 |         else | 
 |             // delegate | 
 |             alias Target = void; | 
 |  | 
 |         static if (is(Target == bool)) | 
 |         { | 
 |             if (val.length) | 
 |             { | 
 |                 // parse '--b=true/false' | 
 |                 *receiver = getoptTo!(Target)(option, val, i); | 
 |             } | 
 |             else | 
 |             { | 
 |                 // no argument means set it to true | 
 |                 *receiver = true; | 
 |             } | 
 |         } | 
 |         else | 
 |         { | 
 |             import std.exception : enforce; | 
 |             // non-boolean option, which might include an argument | 
 |             enum isCallbackWithLessThanTwoParameters = | 
 |                 (is(R == delegate) || is(Target == function)) && | 
 |                 !is(typeof(receiver("", ""))); | 
 |             if (!isCallbackWithLessThanTwoParameters && !(val.length) && !incremental) | 
 |             { | 
 |                 // Eat the next argument too.  Check to make sure there's one | 
 |                 // to be eaten first, though. | 
 |                 enforce!GetOptException(i < args.length, | 
 |                         "Missing value for argument " ~ a ~ "."); | 
 |                 val = args[i]; | 
 |                 args = args[0 .. i] ~ args[i + 1 .. $]; | 
 |             } | 
 |             static if (is(Target == enum) || | 
 |                     is(Target == string)) | 
 |             { | 
 |                 *receiver = getoptTo!Target(option, val, i); | 
 |             } | 
 |             else static if (is(Target : real)) | 
 |             { | 
 |                 // numeric receiver | 
 |                 if (incremental) | 
 |                 { | 
 |                     ++*receiver; | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     *receiver = getoptTo!Target(option, val, i); | 
 |                 } | 
 |             } | 
 |             else static if (is(Target == string)) | 
 |             { | 
 |                 // string receiver | 
 |                 *receiver = getoptTo!(Target)(option, val, i); | 
 |             } | 
 |             else static if (is(R == delegate) || | 
 |                     is(Target == function)) | 
 |             { | 
 |                 static if (is(typeof(receiver("", "")) : void)) | 
 |                 { | 
 |                     // option with argument | 
 |                     receiver(option, val); | 
 |                 } | 
 |                 else static if (is(typeof(receiver("")) : void)) | 
 |                 { | 
 |                     alias RType = typeof(receiver("")); | 
 |                     static assert(is(RType : void), | 
 |                             "Invalid receiver return type " ~ RType.stringof); | 
 |                     // boolean-style receiver | 
 |                     receiver(option); | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     alias RType = typeof(receiver()); | 
 |                     static assert(is(RType : void), | 
 |                             "Invalid receiver return type " ~ RType.stringof); | 
 |                     // boolean-style receiver without argument | 
 |                     receiver(); | 
 |                 } | 
 |             } | 
 |             else static if (isArray!(Target)) | 
 |             { | 
 |                 // array receiver | 
 |                 import std.range : ElementEncodingType; | 
 |                 alias E = ElementEncodingType!(Target); | 
 |  | 
 |                 if (arraySep == "") | 
 |                 { | 
 |                     *receiver ~= getoptTo!E(option, val, i); | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     foreach (elem; val.splitter(arraySep)) | 
 |                     { | 
 |                         *receiver ~= getoptTo!E(option, elem, i); | 
 |                     } | 
 |                 } | 
 |             } | 
 |             else static if (isAssociativeArray!(Target)) | 
 |             { | 
 |                 // hash receiver | 
 |                 alias K = typeof(receiver.keys[0]); | 
 |                 alias V = typeof(receiver.values[0]); | 
 |  | 
 |                 import std.range : only; | 
 |                 import std.string : indexOf; | 
 |                 import std.typecons : Tuple, tuple; | 
 |  | 
 |                 static Tuple!(K, V) getter(string input) | 
 |                 { | 
 |                     auto j = indexOf(input, assignChar); | 
 |                     enforce!GetOptException(j != -1, "Could not find '" | 
 |                         ~ to!string(assignChar) ~ "' in argument '" ~ input ~ "'."); | 
 |                     auto key = input[0 .. j]; | 
 |                     auto value = input[j + 1 .. $]; | 
 |                     return tuple(getoptTo!K("", key, 0), getoptTo!V("", value, 0)); | 
 |                 } | 
 |  | 
 |                 static void setHash(Range)(R receiver, Range range) | 
 |                 { | 
 |                     foreach (k, v; range.map!getter) | 
 |                         (*receiver)[k] = v; | 
 |                 } | 
 |  | 
 |                 if (arraySep == "") | 
 |                     setHash(receiver, val.only); | 
 |                 else | 
 |                     setHash(receiver, val.splitter(arraySep)); | 
 |             } | 
 |             else | 
 |                 static assert(false, "getopt does not know how to handle the type " ~ R.stringof); | 
 |         } | 
 |     } | 
 |  | 
 |     return ret; | 
 | } | 
 |  | 
 | // https://issues.dlang.org/show_bug.cgi?id=17574 | 
 | @safe unittest | 
 | { | 
 |     import std.algorithm.searching : startsWith; | 
 |  | 
 |     try | 
 |     { | 
 |         string[string] mapping; | 
 |         immutable as = arraySep; | 
 |         arraySep = ","; | 
 |         scope (exit) | 
 |             arraySep = as; | 
 |         string[] args = ["testProgram", "-m", "a=b,c=\"d,e,f\""]; | 
 |         args.getopt("m", &mapping); | 
 |         assert(false, "Exception not thrown"); | 
 |     } | 
 |     catch (GetOptException goe) | 
 |         assert(goe.msg.startsWith("Could not find")); | 
 | } | 
 |  | 
 | // https://issues.dlang.org/show_bug.cgi?id=5316 - arrays with arraySep | 
 | @safe unittest | 
 | { | 
 |     import std.conv; | 
 |  | 
 |     arraySep = ","; | 
 |     scope (exit) arraySep = ""; | 
 |  | 
 |     string[] names; | 
 |     auto args = ["program.name", "-nfoo,bar,baz"]; | 
 |     getopt(args, "name|n", &names); | 
 |     assert(names == ["foo", "bar", "baz"], to!string(names)); | 
 |  | 
 |     names = names.init; | 
 |     args = ["program.name", "-n", "foo,bar,baz"]; | 
 |     getopt(args, "name|n", &names); | 
 |     assert(names == ["foo", "bar", "baz"], to!string(names)); | 
 |  | 
 |     names = names.init; | 
 |     args = ["program.name", "--name=foo,bar,baz"]; | 
 |     getopt(args, "name|n", &names); | 
 |     assert(names == ["foo", "bar", "baz"], to!string(names)); | 
 |  | 
 |     names = names.init; | 
 |     args = ["program.name", "--name", "foo,bar,baz"]; | 
 |     getopt(args, "name|n", &names); | 
 |     assert(names == ["foo", "bar", "baz"], to!string(names)); | 
 | } | 
 |  | 
 | // https://issues.dlang.org/show_bug.cgi?id=5316 - associative arrays with arraySep | 
 | @safe unittest | 
 | { | 
 |     import std.conv; | 
 |  | 
 |     arraySep = ","; | 
 |     scope (exit) arraySep = ""; | 
 |  | 
 |     int[string] values; | 
 |     values = values.init; | 
 |     auto args = ["program.name", "-vfoo=0,bar=1,baz=2"]; | 
 |     getopt(args, "values|v", &values); | 
 |     assert(values == ["foo":0, "bar":1, "baz":2], to!string(values)); | 
 |  | 
 |     values = values.init; | 
 |     args = ["program.name", "-v", "foo=0,bar=1,baz=2"]; | 
 |     getopt(args, "values|v", &values); | 
 |     assert(values == ["foo":0, "bar":1, "baz":2], to!string(values)); | 
 |  | 
 |     values = values.init; | 
 |     args = ["program.name", "--values=foo=0,bar=1,baz=2"]; | 
 |     getopt(args, "values|t", &values); | 
 |     assert(values == ["foo":0, "bar":1, "baz":2], to!string(values)); | 
 |  | 
 |     values = values.init; | 
 |     args = ["program.name", "--values", "foo=0,bar=1,baz=2"]; | 
 |     getopt(args, "values|v", &values); | 
 |     assert(values == ["foo":0, "bar":1, "baz":2], to!string(values)); | 
 | } | 
 |  | 
 | // https://github.com/dlang/phobos/issues/10680 | 
 | @safe unittest | 
 | { | 
 |     arraySep = ","; | 
 |     scope(exit) arraySep = ""; | 
 |     const(string)[] s; | 
 |     string[] args = ["program.name", "-s", "a", "-s", "b", "-s", "c,d,e"]; | 
 |     getopt(args, "values|s", &s); | 
 |     assert(s == ["a", "b", "c", "d", "e"]); | 
 | } | 
 |  | 
 |  | 
 | /** | 
 |    The option character (default '-'). | 
 |  | 
 |    Defaults to '-' but it can be assigned to prior to calling `getopt`. | 
 |  */ | 
 | dchar optionChar = '-'; | 
 |  | 
 | /** | 
 |    The string that conventionally marks the end of all options (default '--'). | 
 |  | 
 |    Defaults to "--" but can be assigned to prior to calling `getopt`. Assigning an | 
 |    empty string to `endOfOptions` effectively disables it. | 
 |  */ | 
 | string endOfOptions = "--"; | 
 |  | 
 | /** | 
 |    The assignment character used in options with parameters (default '='). | 
 |  | 
 |    Defaults to '=' but can be assigned to prior to calling `getopt`. | 
 |  */ | 
 | dchar assignChar = '='; | 
 |  | 
 | /** | 
 |    When set to "", parameters to array and associative array receivers are | 
 |    treated as an individual argument. That is, only one argument is appended or | 
 |    inserted per appearance of the option switch. If `arraySep` is set to | 
 |    something else, then each parameter is first split by the separator, and the | 
 |    individual pieces are treated as arguments to the same option. | 
 |  | 
 |    Defaults to "" but can be assigned to prior to calling `getopt`. | 
 |  */ | 
 | string arraySep = ""; | 
 |  | 
 | private enum autoIncrementChar = '+'; | 
 |  | 
 | private struct configuration | 
 | { | 
 |     import std.bitmanip : bitfields; | 
 |     mixin(bitfields!( | 
 |                 bool, "caseSensitive",  1, | 
 |                 bool, "bundling", 1, | 
 |                 bool, "passThrough", 1, | 
 |                 bool, "stopOnFirstNonOption", 1, | 
 |                 bool, "keepEndOfOptions", 1, | 
 |                 bool, "required", 1, | 
 |                 ubyte, "", 2)); | 
 | } | 
 |  | 
 | private bool optMatch(string arg, scope string optPattern, ref string value, | 
 |     configuration cfg) @safe | 
 | { | 
 |     import std.algorithm.iteration : splitter; | 
 |     import std.string : indexOf; | 
 |     import std.uni : icmp; | 
 |     //writeln("optMatch:\n  ", arg, "\n  ", optPattern, "\n  ", value); | 
 |     //scope(success) writeln("optMatch result: ", value); | 
 |     if (arg.length < 2 || arg[0] != optionChar) return false; | 
 |     // yank the leading '-' | 
 |     arg = arg[1 .. $]; | 
 |     immutable isLong = arg.length > 1 && arg[0] == optionChar; | 
 |     //writeln("isLong: ", isLong); | 
 |     // yank the second '-' if present | 
 |     if (isLong) arg = arg[1 .. $]; | 
 |     immutable eqPos = indexOf(arg, assignChar); | 
 |     if (isLong && eqPos >= 0) | 
 |     { | 
 |         // argument looks like --opt=value | 
 |         value = arg[eqPos + 1 .. $]; | 
 |         arg = arg[0 .. eqPos]; | 
 |     } | 
 |     else | 
 |     { | 
 |         if (!isLong && eqPos == 1) | 
 |         { | 
 |             // argument looks like -o=value | 
 |             value = arg[2 .. $]; | 
 |             arg = arg[0 .. 1]; | 
 |         } | 
 |         else | 
 |         if (!isLong && !cfg.bundling) | 
 |         { | 
 |             // argument looks like -ovalue and there's no bundling | 
 |             value = arg[1 .. $]; | 
 |             arg = arg[0 .. 1]; | 
 |         } | 
 |         else | 
 |         { | 
 |             // argument looks like --opt, or -oxyz with bundling | 
 |             value = null; | 
 |         } | 
 |     } | 
 |     //writeln("Arg: ", arg, " pattern: ", optPattern, " value: ", value); | 
 |     // Split the option | 
 |     foreach (v; splitter(optPattern, "|")) | 
 |     { | 
 |         //writeln("Trying variant: ", v, " against ", arg); | 
 |         if (arg == v || (!cfg.caseSensitive && icmp(arg, v) == 0)) | 
 |             return true; | 
 |         if (cfg.bundling && !isLong && v.length == 1 | 
 |                 && indexOf(arg, v) >= 0) | 
 |         { | 
 |             //writeln("success"); | 
 |             return true; | 
 |         } | 
 |     } | 
 |     return false; | 
 | } | 
 |  | 
 | private void setConfig(ref configuration cfg, config option) @safe pure nothrow @nogc | 
 | { | 
 |     final switch (option) | 
 |     { | 
 |     case config.caseSensitive: cfg.caseSensitive = true; break; | 
 |     case config.caseInsensitive: cfg.caseSensitive = false; break; | 
 |     case config.bundling: cfg.bundling = true; break; | 
 |     case config.noBundling: cfg.bundling = false; break; | 
 |     case config.passThrough: cfg.passThrough = true; break; | 
 |     case config.noPassThrough: cfg.passThrough = false; break; | 
 |     case config.required: cfg.required = true; break; | 
 |     case config.stopOnFirstNonOption: | 
 |         cfg.stopOnFirstNonOption = true; break; | 
 |     case config.keepEndOfOptions: | 
 |         cfg.keepEndOfOptions = true; break; | 
 |     } | 
 | } | 
 |  | 
 | @safe unittest | 
 | { | 
 |     import std.conv; | 
 |     import std.math.operations : isClose; | 
 |  | 
 |     uint paranoid = 2; | 
 |     string[] args = ["program.name", "--paranoid", "--paranoid", "--paranoid"]; | 
 |     getopt(args, "paranoid+", ¶noid); | 
 |     assert(paranoid == 5, to!(string)(paranoid)); | 
 |  | 
 |     enum Color { no, yes } | 
 |     Color color; | 
 |     args = ["program.name", "--color=yes",]; | 
 |     getopt(args, "color", &color); | 
 |     assert(color, to!(string)(color)); | 
 |  | 
 |     color = Color.no; | 
 |     args = ["program.name", "--color", "yes",]; | 
 |     getopt(args, "color", &color); | 
 |     assert(color, to!(string)(color)); | 
 |  | 
 |     string data = "file.dat"; | 
 |     int length = 24; | 
 |     bool verbose = false; | 
 |     args = ["program.name", "--length=5", "--file", "dat.file", "--verbose"]; | 
 |     getopt( | 
 |         args, | 
 |         "length",  &length, | 
 |         "file",    &data, | 
 |         "verbose", &verbose); | 
 |     assert(args.length == 1); | 
 |     assert(data == "dat.file"); | 
 |     assert(length == 5); | 
 |     assert(verbose); | 
 |  | 
 |     // | 
 |     string[] outputFiles; | 
 |     args = ["program.name", "--output=myfile.txt", "--output", "yourfile.txt"]; | 
 |     getopt(args, "output", &outputFiles); | 
 |     assert(outputFiles.length == 2 | 
 |            && outputFiles[0] == "myfile.txt" && outputFiles[1] == "yourfile.txt"); | 
 |  | 
 |     outputFiles = []; | 
 |     arraySep = ","; | 
 |     args = ["program.name", "--output", "myfile.txt,yourfile.txt"]; | 
 |     getopt(args, "output", &outputFiles); | 
 |     assert(outputFiles.length == 2 | 
 |            && outputFiles[0] == "myfile.txt" && outputFiles[1] == "yourfile.txt"); | 
 |     arraySep = ""; | 
 |  | 
 |     foreach (testArgs; | 
 |         [["program.name", "--tune=alpha=0.5", "--tune", "beta=0.6"], | 
 |          ["program.name", "--tune=alpha=0.5,beta=0.6"], | 
 |          ["program.name", "--tune", "alpha=0.5,beta=0.6"]]) | 
 |     { | 
 |         arraySep = ","; | 
 |         double[string] tuningParms; | 
 |         getopt(testArgs, "tune", &tuningParms); | 
 |         assert(testArgs.length == 1); | 
 |         assert(tuningParms.length == 2); | 
 |         assert(isClose(tuningParms["alpha"], 0.5)); | 
 |         assert(isClose(tuningParms["beta"], 0.6)); | 
 |         arraySep = ""; | 
 |     } | 
 |  | 
 |     uint verbosityLevel = 1; | 
 |     void myHandler(string option) | 
 |     { | 
 |         if (option == "quiet") | 
 |         { | 
 |             verbosityLevel = 0; | 
 |         } | 
 |         else | 
 |         { | 
 |             assert(option == "verbose"); | 
 |             verbosityLevel = 2; | 
 |         } | 
 |     } | 
 |     args = ["program.name", "--quiet"]; | 
 |     getopt(args, "verbose", &myHandler, "quiet", &myHandler); | 
 |     assert(verbosityLevel == 0); | 
 |     args = ["program.name", "--verbose"]; | 
 |     getopt(args, "verbose", &myHandler, "quiet", &myHandler); | 
 |     assert(verbosityLevel == 2); | 
 |  | 
 |     verbosityLevel = 1; | 
 |     void myHandler2(string option, string value) | 
 |     { | 
 |         assert(option == "verbose"); | 
 |         verbosityLevel = 2; | 
 |     } | 
 |     args = ["program.name", "--verbose", "2"]; | 
 |     getopt(args, "verbose", &myHandler2); | 
 |     assert(verbosityLevel == 2); | 
 |  | 
 |     verbosityLevel = 1; | 
 |     void myHandler3() | 
 |     { | 
 |         verbosityLevel = 2; | 
 |     } | 
 |     args = ["program.name", "--verbose"]; | 
 |     getopt(args, "verbose", &myHandler3); | 
 |     assert(verbosityLevel == 2); | 
 |  | 
 |     bool foo, bar; | 
 |     args = ["program.name", "--foo", "--bAr"]; | 
 |     getopt(args, | 
 |         std.getopt.config.caseSensitive, | 
 |         std.getopt.config.passThrough, | 
 |         "foo", &foo, | 
 |         "bar", &bar); | 
 |     assert(args[1] == "--bAr"); | 
 |  | 
 |     // test stopOnFirstNonOption | 
 |  | 
 |     args = ["program.name", "--foo", "nonoption", "--bar"]; | 
 |     foo = bar = false; | 
 |     getopt(args, | 
 |         std.getopt.config.stopOnFirstNonOption, | 
 |         "foo", &foo, | 
 |         "bar", &bar); | 
 |     assert(foo && !bar && args[1] == "nonoption" && args[2] == "--bar"); | 
 |  | 
 |     args = ["program.name", "--foo", "nonoption", "--zab"]; | 
 |     foo = bar = false; | 
 |     getopt(args, | 
 |         std.getopt.config.stopOnFirstNonOption, | 
 |         "foo", &foo, | 
 |         "bar", &bar); | 
 |     assert(foo && !bar && args[1] == "nonoption" && args[2] == "--zab"); | 
 |  | 
 |     args = ["program.name", "--fb1", "--fb2=true", "--tb1=false"]; | 
 |     bool fb1, fb2; | 
 |     bool tb1 = true; | 
 |     getopt(args, "fb1", &fb1, "fb2", &fb2, "tb1", &tb1); | 
 |     assert(fb1 && fb2 && !tb1); | 
 |  | 
 |     // test keepEndOfOptions | 
 |  | 
 |     args = ["program.name", "--foo", "nonoption", "--bar", "--", "--baz"]; | 
 |     getopt(args, | 
 |         std.getopt.config.keepEndOfOptions, | 
 |         "foo", &foo, | 
 |         "bar", &bar); | 
 |     assert(args == ["program.name", "nonoption", "--", "--baz"]); | 
 |  | 
 |     // Ensure old behavior without the keepEndOfOptions | 
 |  | 
 |     args = ["program.name", "--foo", "nonoption", "--bar", "--", "--baz"]; | 
 |     getopt(args, | 
 |         "foo", &foo, | 
 |         "bar", &bar); | 
 |     assert(args == ["program.name", "nonoption", "--baz"]); | 
 |  | 
 |     // test function callbacks | 
 |  | 
 |     static class MyEx : Exception | 
 |     { | 
 |         this() { super(""); } | 
 |         this(string option) { this(); this.option = option; } | 
 |         this(string option, string value) { this(option); this.value = value; } | 
 |  | 
 |         string option; | 
 |         string value; | 
 |     } | 
 |  | 
 |     static void myStaticHandler1() { throw new MyEx(); } | 
 |     args = ["program.name", "--verbose"]; | 
 |     try { getopt(args, "verbose", &myStaticHandler1); assert(0); } | 
 |     catch (MyEx ex) { assert(ex.option is null && ex.value is null); } | 
 |  | 
 |     static void myStaticHandler2(string option) { throw new MyEx(option); } | 
 |     args = ["program.name", "--verbose"]; | 
 |     try { getopt(args, "verbose", &myStaticHandler2); assert(0); } | 
 |     catch (MyEx ex) { assert(ex.option == "verbose" && ex.value is null); } | 
 |  | 
 |     static void myStaticHandler3(string option, string value) { throw new MyEx(option, value); } | 
 |     args = ["program.name", "--verbose", "2"]; | 
 |     try { getopt(args, "verbose", &myStaticHandler3); assert(0); } | 
 |     catch (MyEx ex) { assert(ex.option == "verbose" && ex.value == "2"); } | 
 |  | 
 |     // check that GetOptException is thrown if the value is missing | 
 |     args = ["program.name", "--verbose"]; | 
 |     try { getopt(args, "verbose", &myStaticHandler3); assert(0); } | 
 |     catch (GetOptException e) {} | 
 |     catch (Exception e) { assert(0); } | 
 | } | 
 |  | 
 | @safe unittest // @safe std.getopt.config option use | 
 | { | 
 |     long x = 0; | 
 |     string[] args = ["program", "--inc-x", "--inc-x"]; | 
 |     getopt(args, | 
 |            std.getopt.config.caseSensitive, | 
 |            "inc-x", "Add one to x", delegate void() { x++; }); | 
 |     assert(x == 2); | 
 | } | 
 |  | 
 | // https://issues.dlang.org/show_bug.cgi?id=2142 | 
 | @safe unittest | 
 | { | 
 |     bool f_linenum, f_filename; | 
 |     string[] args = [ "", "-nl" ]; | 
 |     getopt | 
 |         ( | 
 |             args, | 
 |             std.getopt.config.bundling, | 
 |             //std.getopt.config.caseSensitive, | 
 |             "linenum|l", &f_linenum, | 
 |             "filename|n", &f_filename | 
 |         ); | 
 |     assert(f_linenum); | 
 |     assert(f_filename); | 
 | } | 
 |  | 
 | // https://issues.dlang.org/show_bug.cgi?id=6887 | 
 | @safe unittest | 
 | { | 
 |     string[] p; | 
 |     string[] args = ["", "-pa"]; | 
 |     getopt(args, "p", &p); | 
 |     assert(p.length == 1); | 
 |     assert(p[0] == "a"); | 
 | } | 
 |  | 
 | // https://issues.dlang.org/show_bug.cgi?id=6888 | 
 | @safe unittest | 
 | { | 
 |     int[string] foo; | 
 |     auto args = ["", "-t", "a=1"]; | 
 |     getopt(args, "t", &foo); | 
 |     assert(foo == ["a":1]); | 
 | } | 
 |  | 
 | // https://issues.dlang.org/show_bug.cgi?id=9583 | 
 | @safe unittest | 
 | { | 
 |     int opt; | 
 |     auto args = ["prog", "--opt=123", "--", "--a", "--b", "--c"]; | 
 |     getopt(args, "opt", &opt); | 
 |     assert(args == ["prog", "--a", "--b", "--c"]); | 
 | } | 
 |  | 
 | @safe unittest | 
 | { | 
 |     string foo, bar; | 
 |     auto args = ["prog", "-thello", "-dbar=baz"]; | 
 |     getopt(args, "t", &foo, "d", &bar); | 
 |     assert(foo == "hello"); | 
 |     assert(bar == "bar=baz"); | 
 |  | 
 |     // From https://issues.dlang.org/show_bug.cgi?id=5762 | 
 |     string a; | 
 |     args = ["prog", "-a-0x12"]; | 
 |     getopt(args, config.bundling, "a|addr", &a); | 
 |     assert(a == "-0x12", a); | 
 |     args = ["prog", "--addr=-0x12"]; | 
 |     getopt(args, config.bundling, "a|addr", &a); | 
 |     assert(a == "-0x12"); | 
 |  | 
 |     // From https://issues.dlang.org/show_bug.cgi?id=11764 | 
 |     args = ["main", "-test"]; | 
 |     bool opt; | 
 |     args.getopt(config.passThrough, "opt", &opt); | 
 |     assert(args == ["main", "-test"]); | 
 |  | 
 |     // From https://issues.dlang.org/show_bug.cgi?id=15220 | 
 |     args = ["main", "-o=str"]; | 
 |     string o; | 
 |     args.getopt("o", &o); | 
 |     assert(o == "str"); | 
 |  | 
 |     args = ["main", "-o=str"]; | 
 |     o = null; | 
 |     args.getopt(config.bundling, "o", &o); | 
 |     assert(o == "str"); | 
 | } | 
 |  | 
 | // https://issues.dlang.org/show_bug.cgi?id=5228 | 
 | @safe unittest | 
 | { | 
 |     import std.conv; | 
 |     import std.exception; | 
 |  | 
 |     auto args = ["prog", "--foo=bar"]; | 
 |     int abc; | 
 |     assertThrown!GetOptException(getopt(args, "abc", &abc)); | 
 |  | 
 |     args = ["prog", "--abc=string"]; | 
 |     assertThrown!ConvException(getopt(args, "abc", &abc)); | 
 | } | 
 |  | 
 | // https://issues.dlang.org/show_bug.cgi?id=7693 | 
 | @safe unittest | 
 | { | 
 |     import std.exception; | 
 |  | 
 |     enum Foo { | 
 |         bar, | 
 |         baz | 
 |     } | 
 |  | 
 |     auto args = ["prog", "--foo=barZZZ"]; | 
 |     Foo foo; | 
 |     assertThrown(getopt(args, "foo", &foo)); | 
 |     args = ["prog", "--foo=bar"]; | 
 |     assertNotThrown(getopt(args, "foo", &foo)); | 
 |     args = ["prog", "--foo", "barZZZ"]; | 
 |     assertThrown(getopt(args, "foo", &foo)); | 
 |     args = ["prog", "--foo", "baz"]; | 
 |     assertNotThrown(getopt(args, "foo", &foo)); | 
 | } | 
 |  | 
 | // Same as https://issues.dlang.org/show_bug.cgi?id=7693 only for `bool` | 
 | @safe unittest | 
 | { | 
 |     import std.exception; | 
 |  | 
 |     auto args = ["prog", "--foo=truefoobar"]; | 
 |     bool foo; | 
 |     assertThrown(getopt(args, "foo", &foo)); | 
 |     args = ["prog", "--foo"]; | 
 |     getopt(args, "foo", &foo); | 
 |     assert(foo); | 
 | } | 
 |  | 
 | @safe unittest | 
 | { | 
 |     bool foo; | 
 |     auto args = ["prog", "--foo"]; | 
 |     getopt(args, "foo", &foo); | 
 |     assert(foo); | 
 | } | 
 |  | 
 | @safe unittest | 
 | { | 
 |     bool foo; | 
 |     bool bar; | 
 |     auto args = ["prog", "--foo", "-b"]; | 
 |     getopt(args, config.caseInsensitive,"foo|f", "Some foo", &foo, | 
 |         config.caseSensitive, "bar|b", "Some bar", &bar); | 
 |     assert(foo); | 
 |     assert(bar); | 
 | } | 
 |  | 
 | @safe unittest | 
 | { | 
 |     bool foo; | 
 |     bool bar; | 
 |     auto args = ["prog", "-b", "--foo", "-z"]; | 
 |     getopt(args, config.caseInsensitive, config.required, "foo|f", "Some foo", | 
 |         &foo, config.caseSensitive, "bar|b", "Some bar", &bar, | 
 |         config.passThrough); | 
 |     assert(foo); | 
 |     assert(bar); | 
 | } | 
 |  | 
 | @safe unittest | 
 | { | 
 |     import std.exception; | 
 |  | 
 |     bool foo; | 
 |     bool bar; | 
 |     auto args = ["prog", "-b", "-z"]; | 
 |     assertThrown(getopt(args, config.caseInsensitive, config.required, "foo|f", | 
 |         "Some foo", &foo, config.caseSensitive, "bar|b", "Some bar", &bar, | 
 |         config.passThrough)); | 
 | } | 
 |  | 
 | @safe unittest | 
 | { | 
 |     import std.exception; | 
 |  | 
 |     bool foo; | 
 |     bool bar; | 
 |     auto args = ["prog", "--foo", "-z"]; | 
 |     assertNotThrown(getopt(args, config.caseInsensitive, config.required, | 
 |         "foo|f", "Some foo", &foo, config.caseSensitive, "bar|b", "Some bar", | 
 |         &bar, config.passThrough)); | 
 |     assert(foo); | 
 |     assert(!bar); | 
 | } | 
 |  | 
 | @safe unittest | 
 | { | 
 |     bool foo; | 
 |     auto args = ["prog", "-f"]; | 
 |     auto r = getopt(args, config.caseInsensitive, "help|f", "Some foo", &foo); | 
 |     assert(foo); | 
 |     assert(!r.helpWanted); | 
 | } | 
 |  | 
 | @safe unittest // implicit help option without config.passThrough | 
 | { | 
 |     string[] args = ["program", "--help"]; | 
 |     auto r = getopt(args); | 
 |     assert(r.helpWanted); | 
 | } | 
 |  | 
 | // std.getopt: implicit help option breaks the next argument | 
 | // https://issues.dlang.org/show_bug.cgi?id=13316 | 
 | @safe unittest | 
 | { | 
 |     string[] args = ["program", "--help", "--", "something"]; | 
 |     getopt(args); | 
 |     assert(args == ["program", "something"]); | 
 |  | 
 |     args = ["program", "--help", "--"]; | 
 |     getopt(args); | 
 |     assert(args == ["program"]); | 
 |  | 
 |     bool b; | 
 |     args = ["program", "--help", "nonoption", "--option"]; | 
 |     getopt(args, config.stopOnFirstNonOption, "option", &b); | 
 |     assert(args == ["program", "nonoption", "--option"]); | 
 | } | 
 |  | 
 | // std.getopt: endOfOptions broken when it doesn't look like an option | 
 | // https://issues.dlang.org/show_bug.cgi?id=13317 | 
 | @safe unittest | 
 | { | 
 |     auto endOfOptionsBackup = endOfOptions; | 
 |     scope(exit) endOfOptions = endOfOptionsBackup; | 
 |     endOfOptions = "endofoptions"; | 
 |     string[] args = ["program", "endofoptions", "--option"]; | 
 |     bool b = false; | 
 |     getopt(args, "option", &b); | 
 |     assert(!b); | 
 |     assert(args == ["program", "--option"]); | 
 | } | 
 |  | 
 | // make std.getopt ready for DIP 1000 | 
 | // https://issues.dlang.org/show_bug.cgi?id=20480 | 
 | @safe unittest | 
 | { | 
 |     string[] args = ["test", "--foo", "42", "--bar", "BAR"]; | 
 |     int foo; | 
 |     string bar; | 
 |     getopt(args, "foo", &foo, "bar", "bar help", &bar); | 
 |     assert(foo == 42); | 
 |     assert(bar == "BAR"); | 
 | } | 
 |  | 
 | /** This function prints the passed `Option`s and text in an aligned manner on `stdout`. | 
 |  | 
 | The passed text will be printed first, followed by a newline, then the short | 
 | and long version of every option will be printed. The short and long version | 
 | will be aligned to the longest option of every `Option` passed. If the option | 
 | is required, then "Required:" will be printed after the long version of the | 
 | `Option`. If a help message is present it will be printed next. The format is | 
 | illustrated by this code: | 
 |  | 
 | ------------ | 
 | foreach (it; opt) | 
 | { | 
 |     writefln("%*s %*s%s%s", lengthOfLongestShortOption, it.optShort, | 
 |         lengthOfLongestLongOption, it.optLong, | 
 |         it.required ? " Required: " : " ", it.help); | 
 | } | 
 | ------------ | 
 |  | 
 | Params: | 
 |     text = The text to printed at the beginning of the help output. | 
 |     opt = The `Option` extracted from the `getopt` parameter. | 
 | */ | 
 | void defaultGetoptPrinter(string text, Option[] opt) @safe | 
 | { | 
 |     import std.stdio : stdout; | 
 |     // stdout global __gshared is trusted with a locked text writer | 
 |     auto w = (() @trusted => stdout.lockingTextWriter())(); | 
 |  | 
 |     defaultGetoptFormatter(w, text, opt); | 
 | } | 
 |  | 
 | /** This function writes the passed text and `Option` into an output range | 
 | in the manner described in the documentation of function | 
 | `defaultGetoptPrinter`, unless the style option is used. | 
 |  | 
 | Params: | 
 |     output = The output range used to write the help information. | 
 |     text = The text to print at the beginning of the help output. | 
 |     opt = The `Option` extracted from the `getopt` parameter. | 
 |     style = The manner in which to display the output of each `Option.` | 
 | */ | 
 | void defaultGetoptFormatter(Output)(Output output, string text, Option[] opt, string style = "%*s %*s%*s%s\n") | 
 | { | 
 |     import std.algorithm.comparison : min, max; | 
 |     import std.format.write : formattedWrite; | 
 |  | 
 |     output.formattedWrite("%s\n", text); | 
 |  | 
 |     size_t ls, ll; | 
 |     bool hasRequired = false; | 
 |     foreach (it; opt) | 
 |     { | 
 |         ls = max(ls, it.optShort.length); | 
 |         ll = max(ll, it.optLong.length); | 
 |  | 
 |         hasRequired = hasRequired || it.required; | 
 |     } | 
 |  | 
 |     string re = " Required: "; | 
 |  | 
 |     foreach (it; opt) | 
 |     { | 
 |         output.formattedWrite(style, ls, it.optShort, ll, it.optLong, | 
 |             hasRequired ? re.length : 1, it.required ? re : " ", it.help); | 
 |     } | 
 | } | 
 |  | 
 | @safe unittest | 
 | { | 
 |     import std.conv; | 
 |  | 
 |     import std.array; | 
 |     import std.string; | 
 |     bool a; | 
 |     auto args = ["prog", "--foo"]; | 
 |     auto t = getopt(args, "foo|f", "Help", &a); | 
 |     string s; | 
 |     auto app = appender!string(); | 
 |     defaultGetoptFormatter(app, "Some Text", t.options); | 
 |  | 
 |     string helpMsg = app.data; | 
 |     //writeln(helpMsg); | 
 |     assert(helpMsg.length); | 
 |     assert(helpMsg.count("\n") == 3, to!string(helpMsg.count("\n")) ~ " " | 
 |         ~ helpMsg); | 
 |     assert(helpMsg.indexOf("--foo") != -1); | 
 |     assert(helpMsg.indexOf("-f") != -1); | 
 |     assert(helpMsg.indexOf("-h") != -1); | 
 |     assert(helpMsg.indexOf("--help") != -1); | 
 |     assert(helpMsg.indexOf("Help") != -1); | 
 |  | 
 |     string wanted = "Some Text\n-f  --foo Help\n-h --help This help " | 
 |         ~ "information.\n"; | 
 |     assert(wanted == helpMsg); | 
 | } | 
 |  | 
 | @safe unittest | 
 | { | 
 |     import std.array ; | 
 |     import std.conv; | 
 |     import std.string; | 
 |     bool a; | 
 |     auto args = ["prog", "--foo"]; | 
 |     auto t = getopt(args, config.required, "foo|f", "Help", &a); | 
 |     string s; | 
 |     auto app = appender!string(); | 
 |     defaultGetoptFormatter(app, "Some Text", t.options); | 
 |  | 
 |     string helpMsg = app.data; | 
 |     //writeln(helpMsg); | 
 |     assert(helpMsg.length); | 
 |     assert(helpMsg.count("\n") == 3, to!string(helpMsg.count("\n")) ~ " " | 
 |         ~ helpMsg); | 
 |     assert(helpMsg.indexOf("Required:") != -1); | 
 |     assert(helpMsg.indexOf("--foo") != -1); | 
 |     assert(helpMsg.indexOf("-f") != -1); | 
 |     assert(helpMsg.indexOf("-h") != -1); | 
 |     assert(helpMsg.indexOf("--help") != -1); | 
 |     assert(helpMsg.indexOf("Help") != -1); | 
 |  | 
 |     string wanted = "Some Text\n-f  --foo Required: Help\n-h --help " | 
 |         ~ "          This help information.\n"; | 
 |     assert(wanted == helpMsg, helpMsg ~ wanted); | 
 | } | 
 |  | 
 | // https://issues.dlang.org/show_bug.cgi?id=14724 | 
 | @safe unittest | 
 | { | 
 |     bool a; | 
 |     auto args = ["prog", "--help"]; | 
 |     GetoptResult rslt; | 
 |     try | 
 |     { | 
 |         rslt = getopt(args, config.required, "foo|f", "bool a", &a); | 
 |     } | 
 |     catch (Exception e) | 
 |     { | 
 |         enum errorMsg = "If the request for help was passed required options" ~ | 
 |                 "must not be set."; | 
 |         assert(false, errorMsg); | 
 |     } | 
 |  | 
 |     assert(rslt.helpWanted); | 
 | } | 
 |  | 
 | // throw on duplicate options | 
 | @system unittest | 
 | { | 
 |     import core.exception : AssertError; | 
 |     import std.exception : assertNotThrown, assertThrown; | 
 |     auto args = ["prog", "--abc", "1"]; | 
 |     int abc, def; | 
 |     assertThrown!AssertError(getopt(args, "abc", &abc, "abc", &abc)); | 
 |     assertThrown!AssertError(getopt(args, "abc|a", &abc, "def|a", &def)); | 
 |     assertNotThrown!AssertError(getopt(args, "abc", &abc, "def", &def)); | 
 |  | 
 |     // https://issues.dlang.org/show_bug.cgi?id=23940 | 
 |     assertThrown!AssertError(getopt(args, | 
 |             "abc", &abc, "ABC", &def)); | 
 |     assertThrown!AssertError(getopt(args, config.caseInsensitive, | 
 |             "abc", &abc, "ABC", &def)); | 
 |     assertNotThrown!AssertError(getopt(args, config.caseSensitive, | 
 |             "abc", &abc, "ABC", &def)); | 
 | } | 
 |  | 
 | // https://issues.dlang.org/show_bug.cgi?id=17327 repeated option use | 
 | @safe unittest | 
 | { | 
 |     long num = 0; | 
 |  | 
 |     string[] args = ["program", "--num", "3"]; | 
 |     getopt(args, "n|num", &num); | 
 |     assert(num == 3); | 
 |  | 
 |     args = ["program", "--num", "3", "--num", "5"]; | 
 |     getopt(args, "n|num", &num); | 
 |     assert(num == 5); | 
 |  | 
 |     args = ["program", "--n", "3", "--num", "5", "-n", "-7"]; | 
 |     getopt(args, "n|num", &num); | 
 |     assert(num == -7); | 
 |  | 
 |     void add1() { num++; } | 
 |     void add2(string option) { num += 2; } | 
 |     void addN(string option, string value) | 
 |     { | 
 |         import std.conv : to; | 
 |         num += value.to!long; | 
 |     } | 
 |  | 
 |     num = 0; | 
 |     args = ["program", "--add1", "--add2", "--add1", "--add", "5", "--add2", "--add", "10"]; | 
 |     getopt(args, | 
 |            "add1", "Add 1 to num", &add1, | 
 |            "add2", "Add 2 to num", &add2, | 
 |            "add", "Add N to num", &addN,); | 
 |     assert(num == 21); | 
 |  | 
 |     bool flag = false; | 
 |     args = ["program", "--flag"]; | 
 |     getopt(args, "f|flag", "Boolean", &flag); | 
 |     assert(flag); | 
 |  | 
 |     flag = false; | 
 |     args = ["program", "-f", "-f"]; | 
 |     getopt(args, "f|flag", "Boolean", &flag); | 
 |     assert(flag); | 
 |  | 
 |     flag = false; | 
 |     args = ["program", "--flag=true", "--flag=false"]; | 
 |     getopt(args, "f|flag", "Boolean", &flag); | 
 |     assert(!flag); | 
 |  | 
 |     flag = false; | 
 |     args = ["program", "--flag=true", "--flag=false", "-f"]; | 
 |     getopt(args, "f|flag", "Boolean", &flag); | 
 |     assert(flag); | 
 | } | 
 |  | 
 | @system unittest  // Delegates as callbacks | 
 | { | 
 |     alias TwoArgOptionHandler = void delegate(string option, string value) @safe; | 
 |  | 
 |     TwoArgOptionHandler makeAddNHandler(ref long dest) | 
 |     { | 
 |         void addN(ref long dest, string n) | 
 |         { | 
 |             import std.conv : to; | 
 |             dest += n.to!long; | 
 |         } | 
 |  | 
 |         return (option, value) => addN(dest, value); | 
 |     } | 
 |  | 
 |     long x = 0; | 
 |     long y = 0; | 
 |  | 
 |     string[] args = | 
 |         ["program", "--x-plus-1", "--x-plus-1", "--x-plus-5", "--x-plus-n", "10", | 
 |          "--y-plus-n", "25", "--y-plus-7", "--y-plus-n", "15", "--y-plus-3"]; | 
 |  | 
 |     getopt(args, | 
 |            "x-plus-1", "Add one to x", delegate void() { x += 1; }, | 
 |            "x-plus-5", "Add five to x", delegate void(string option) { x += 5; }, | 
 |            "x-plus-n", "Add NUM to x", makeAddNHandler(x), | 
 |            "y-plus-7", "Add seven to y", delegate void() { y += 7; }, | 
 |            "y-plus-3", "Add three to y", delegate void(string option) { y += 3; }, | 
 |            "y-plus-n", "Add NUM to x", makeAddNHandler(y),); | 
 |  | 
 |     assert(x == 17); | 
 |     assert(y == 50); | 
 | } | 
 |  | 
 | // Hyphens at the start of option values; | 
 | // https://issues.dlang.org/show_bug.cgi?id=17650 | 
 | @safe unittest | 
 | { | 
 |     auto args = ["program", "-m", "-5", "-n", "-50", "-c", "-", "-f", "-"]; | 
 |  | 
 |     int m; | 
 |     int n; | 
 |     char c; | 
 |     string f; | 
 |  | 
 |     getopt(args, | 
 |            "m|mm", "integer", &m, | 
 |            "n|nn", "integer", &n, | 
 |            "c|cc", "character", &c, | 
 |            "f|file", "filename or hyphen for stdin", &f); | 
 |  | 
 |     assert(m == -5); | 
 |     assert(n == -50); | 
 |     assert(c == '-'); | 
 |     assert(f == "-"); | 
 | } | 
 |  | 
 | // Hyphen at the option value; | 
 | // https://issues.dlang.org/show_bug.cgi?id=22394 | 
 | @safe unittest | 
 | { | 
 |     auto args = ["program", "-"]; | 
 |  | 
 |     getopt(args); | 
 |  | 
 |     assert(args == ["program", "-"]); | 
 | } | 
 |  | 
 | @safe unittest | 
 | { | 
 |     import std.conv; | 
 |  | 
 |     import std.array; | 
 |     import std.string; | 
 |     bool a; | 
 |     auto args = ["prog", "--foo"]; | 
 |     auto t = getopt(args, "foo|f", "Help", &a); | 
 |     string s; | 
 |     auto app = appender!string(); | 
 |     defaultGetoptFormatter(app, "Some Text", t.options, "\t\t%*s %*s%*s\n%s\n"); | 
 |  | 
 |     string helpMsg = app.data; | 
 |     //writeln(helpMsg); | 
 |     assert(helpMsg.length); | 
 |     assert(helpMsg.count("\n") == 5, to!string(helpMsg.count("\n")) ~ " " | 
 |         ~ helpMsg); | 
 |     assert(helpMsg.indexOf("--foo") != -1); | 
 |     assert(helpMsg.indexOf("-f") != -1); | 
 |     assert(helpMsg.indexOf("-h") != -1); | 
 |     assert(helpMsg.indexOf("--help") != -1); | 
 |     assert(helpMsg.indexOf("Help") != -1); | 
 |  | 
 |     string wanted = "Some Text\n\t\t-f  --foo \nHelp\n\t\t-h --help \nThis help " | 
 |         ~ "information.\n"; | 
 |     assert(wanted == helpMsg); | 
 | } | 
 |  | 
 |  | 
 | @safe unittest | 
 | { | 
 |     import std.conv : ConvException; | 
 |     import std.string : indexOf; | 
 |  | 
 |     enum UniqueIdentifer { | 
 |         a, | 
 |         b | 
 |     } | 
 |  | 
 |     UniqueIdentifer a; | 
 |  | 
 |     auto args = ["prog", "--foo", "HELLO"]; | 
 |     try | 
 |     { | 
 |         auto t = getopt(args, "foo|f", &a); | 
 |         assert(false, "Must not be reached, as \"HELLO\" cannot be converted" | 
 |             ~ " to enum A."); | 
 |     } | 
 |     catch (ConvException e) | 
 |     { | 
 |         string str = () @trusted { return e.toString(); }(); | 
 |         assert(str.indexOf("HELLO") != -1); | 
 |         assert(str.indexOf("UniqueIdentifer") != -1); | 
 |         assert(str.indexOf("foo") != -1); | 
 |     } | 
 | } | 
 |  | 
 | @safe unittest | 
 | { | 
 |     import std.conv : ConvException; | 
 |     import std.string : indexOf; | 
 |  | 
 |     int a; | 
 |  | 
 |     auto args = ["prog", "--foo", "HELLO"]; | 
 |     try | 
 |     { | 
 |         auto t = getopt(args, "foo|f", &a); | 
 |         assert(false, "Must not be reached, as \"HELLO\" cannot be converted" | 
 |             ~ " to an int"); | 
 |     } | 
 |     catch (ConvException e) | 
 |     { | 
 |         string str = () @trusted { return e.toString(); }(); | 
 |         assert(str.indexOf("HELLO") != -1); | 
 |         assert(str.indexOf("int") != -1); | 
 |         assert(str.indexOf("foo") != -1); | 
 |     } | 
 |  | 
 |     args = ["prog", "--foo", "1337"]; | 
 |     getopt(args, "foo|f", &a); | 
 |     assert(a == 1337); | 
 | } |