1 // Written in the D programming language.
4 Processing of command line options.
6 The getopt module implements a `getopt` function, which adheres to
7 the POSIX syntax for command line options. GNU extensions are
8 supported in the form of long options introduced by a double dash
9 ("--"). Support for bundling of command line options, as was the case
10 with the more traditional single-letter approach, is provided but not
13 Copyright: Copyright Andrei Alexandrescu 2008 - 2015.
14 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
15 Authors: $(HTTP erdani.org, Andrei Alexandrescu)
16 Credits: This module and its documentation are inspired by Perl's
17 $(HTTPS perldoc.perl.org/Getopt/Long.html, Getopt::Long) module. The syntax of
18 D's `getopt` is simpler than its Perl counterpart because $(D
19 getopt) infers the expected parameter types from the static types of
20 the passed-in pointers.
21 Source: $(PHOBOSSRC std/getopt.d)
24 Copyright Andrei Alexandrescu 2008 - 2015.
25 Distributed under the Boost Software License, Version 1.0.
26 (See accompanying file LICENSE_1_0.txt or copy at
27 http://www.boost.org/LICENSE_1_0.txt)
31 import std
.exception
: basicExceptionCtors
;
35 Thrown on one of the following conditions:
37 $(LI An unrecognized command-line argument is passed, and
38 `std.getopt.config.passThrough` was not present.)
39 $(LI A command-line option was not found, and
40 `std.getopt.config.required` was present.)
41 $(LI A callback option is missing a value.)
44 class GetOptException
: Exception
46 mixin basicExceptionCtors
;
49 static assert(is(typeof(new GetOptException("message"))));
50 static assert(is(typeof(new GetOptException("message", Exception
.init
))));
53 Parse and remove command line options from a string array.
60 string data = "file.dat";
63 enum Color { no, yes };
66 void main(string[] args)
68 auto helpInformation = getopt(
70 "length", &length, // numeric
71 "file", &data, // string
72 "verbose", &verbose, // flag
73 "color", "Information about this color", &color); // enum
76 if (helpInformation.helpWanted)
78 defaultGetoptPrinter("Some information about the program.",
79 helpInformation.options);
84 The `getopt` function takes a reference to the command line
85 (as received by `main`) as its first argument, and an
86 unbounded number of pairs of strings and pointers. Each string is an
87 option meant to "fill" the value referenced by the pointer to its
88 right (the "bound" pointer). The option string in the call to
89 `getopt` should not start with a dash.
91 In all cases, the command-line options that were parsed and used by
92 `getopt` are removed from `args`. Whatever in the
93 arguments did not look like an option is left in `args` for
94 further processing by the program. Values that were unaffected by the
95 options are not touched, so a common idiom is to initialize options
96 to their defaults and then invoke `getopt`. If a
97 command-line argument is recognized as an option with a parameter and
98 the parameter cannot be parsed properly (e.g., a number is expected
99 but not present), a `ConvException` exception is thrown.
100 If `std.getopt.config.passThrough` was not passed to `getopt`
101 and an unrecognized command-line argument is found, or if a required
102 argument is missing a `GetOptException` is thrown.
104 Depending on the type of the pointer being bound, `getopt`
105 recognizes the following kinds of options:
108 $(LI $(I Boolean options). A lone argument sets the option to `true`.
109 Additionally $(B true) or $(B false) can be set within the option separated
113 bool verbose = false, debugging = true;
114 getopt(args, "verbose", &verbose, "debug", &debugging);
117 To set `verbose` to `true`, invoke the program with either
118 `--verbose` or `--verbose=true`.
120 To set `debugging` to `false`, invoke the program with
124 $(LI $(I Numeric options.) If an option is bound to a numeric type, a
125 number is expected as the next option, or right within the option separated
130 getopt(args, "timeout", &timeout);
133 To set `timeout` to `5`, invoke the program with either
134 `--timeout=5` or $(D --timeout 5).
137 $(LI $(I Incremental options.) If an option name has a "+" suffix and is
138 bound to a numeric type, then the option's value tracks the number of times
139 the option occurred on the command line:
143 getopt(args, "paranoid+", ¶noid);
146 Invoking the program with "--paranoid --paranoid --paranoid" will set $(D
147 paranoid) to 3. Note that an incremental option never expects a parameter,
148 e.g., in the command line "--paranoid 42 --paranoid", the "42" does not set
149 `paranoid` to 42; instead, `paranoid` is set to 2 and "42" is not
150 considered as part of the normal program arguments.
153 $(LI $(I Enum options.) If an option is bound to an enum, an enum symbol as
154 a string is expected as the next option, or right within the option
155 separated with an "=" sign:
158 enum Color { no, yes };
159 Color color; // default initialized to Color.no
160 getopt(args, "color", &color);
163 To set `color` to `Color.yes`, invoke the program with either
164 `--color=yes` or $(D --color yes).
167 $(LI $(I String options.) If an option is bound to a string, a string is
168 expected as the next option, or right within the option separated with an
173 getopt(args, "output", &outputFile);
176 Invoking the program with "--output=myfile.txt" or "--output myfile.txt"
177 will set `outputFile` to "myfile.txt". If you want to pass a string
178 containing spaces, you need to use the quoting that is appropriate to your
179 shell, e.g. --output='my file.txt'.
182 $(LI $(I Array options.) If an option is bound to an array, a new element
183 is appended to the array each time the option occurs:
186 string[] outputFiles;
187 getopt(args, "output", &outputFiles);
190 Invoking the program with "--output=myfile.txt --output=yourfile.txt" or
191 "--output myfile.txt --output yourfile.txt" will set `outputFiles` to
192 $(D [ "myfile.txt", "yourfile.txt" ]).
194 Alternatively you can set $(LREF arraySep) to allow multiple elements in
198 string[] outputFiles;
199 arraySep = ","; // defaults to "", meaning one element per parameter
200 getopt(args, "output", &outputFiles);
203 With the above code you can invoke the program with
204 "--output=myfile.txt,yourfile.txt", or "--output myfile.txt,yourfile.txt".)
206 $(LI $(I Hash options.) If an option is bound to an associative array, a
207 string of the form "name=value" is expected as the next option, or right
208 within the option separated with an "=" sign:
211 double[string] tuningParms;
212 getopt(args, "tune", &tuningParms);
215 Invoking the program with e.g. "--tune=alpha=0.5 --tune beta=0.6" will set
216 `tuningParms` to [ "alpha" : 0.5, "beta" : 0.6 ].
218 Alternatively you can set $(LREF arraySep) as the element separator:
221 double[string] tuningParms;
222 arraySep = ","; // defaults to "", meaning one element per parameter
223 getopt(args, "tune", &tuningParms);
226 With the above code you can invoke the program with
227 "--tune=alpha=0.5,beta=0.6", or "--tune alpha=0.5,beta=0.6".
229 In general, the keys and values can be of any parsable types.
232 $(LI $(I Callback options.) An option can be bound to a function or
233 delegate with the signature $(D void function()), $(D void function(string
234 option)), $(D void function(string option, string value)), or their
235 delegate equivalents.
238 $(LI If the callback doesn't take any arguments, the callback is
239 invoked whenever the option is seen.
242 $(LI If the callback takes one string argument, the option string
243 (without the leading dash(es)) is passed to the callback. After that,
244 the option string is considered handled and removed from the options
248 void main(string[] args)
250 uint verbosityLevel = 1;
251 void myHandler(string option)
253 if (option == "quiet")
259 assert(option == "verbose");
263 getopt(args, "verbose", &myHandler, "quiet", &myHandler);
269 $(LI If the callback takes two string arguments, the option string is
270 handled as an option with one argument, and parsed accordingly. The
271 option and its value are passed to the callback. After that, whatever
272 was passed to the callback is considered handled and removed from the
276 int main(string[] args)
278 uint verbosityLevel = 1;
279 bool handlerFailed = false;
280 void myHandler(string option, string value)
284 case "quiet": verbosityLevel = 0; break;
285 case "verbose": verbosityLevel = 2; break;
286 case "shouting": verbosityLevel = verbosityLevel.max; break;
288 stderr.writeln("Unknown verbosity level ", value);
289 handlerFailed = true;
293 getopt(args, "verbosity", &myHandler);
294 return handlerFailed ? 1 : 0;
301 Options_with_multiple_names:
302 Sometimes option synonyms are desirable, e.g. "--verbose",
303 "--loquacious", and "--garrulous" should have the same effect. Such
304 alternate option names can be included in the option specification,
305 using "|" as a separator:
309 getopt(args, "verbose|loquacious|garrulous", &verbose);
313 By default options are case-insensitive. You can change that behavior
314 by passing `getopt` the `caseSensitive` directive like this:
319 std.getopt.config.caseSensitive,
324 In the example above, "--foo" and "--bar" are recognized, but "--Foo", "--Bar",
325 "--FOo", "--bAr", etc. are rejected.
326 The directive is active until the end of `getopt`, or until the
327 converse directive `caseInsensitive` is encountered:
332 std.getopt.config.caseSensitive,
334 std.getopt.config.caseInsensitive,
338 The option "--Foo" is rejected due to $(D
339 std.getopt.config.caseSensitive), but not "--Bar", "--bAr"
340 etc. because the directive $(D
341 std.getopt.config.caseInsensitive) turned sensitivity off before
342 option "bar" was parsed.
344 Short_versus_long_options:
345 Traditionally, programs accepted single-letter options preceded by
346 only one dash (e.g. `-t`). `getopt` accepts such parameters
347 seamlessly. When used with a double-dash (e.g. `--t`), a
348 single-letter option behaves the same as a multi-letter option. When
349 used with a single dash, a single-letter option is accepted.
351 To set `timeout` to `5`, use either of the following: `--timeout=5`,
352 `--timeout 5`, `--t=5`, `--t 5`, `-t5`, or `-t 5`. Forms such as
353 `-timeout=5` will be not accepted.
355 For more details about short options, refer also to the next section.
358 Single-letter options can be bundled together, i.e. "-abc" is the same as
359 $(D "-a -b -c"). By default, this option is turned off. You can turn it on
360 with the `std.getopt.config.bundling` directive:
365 std.getopt.config.bundling,
370 In case you want to only enable bundling for some of the parameters,
371 bundling can be turned off with `std.getopt.config.noBundling`.
374 An option can be marked as required. If that option is not present in the
375 arguments an exception will be thrown.
380 std.getopt.config.required,
385 Only the option directly following `std.getopt.config.required` is
388 Passing_unrecognized_options_through:
389 If an application needs to do its own processing of whichever arguments
390 `getopt` did not understand, it can pass the
391 `std.getopt.config.passThrough` directive to `getopt`:
396 std.getopt.config.passThrough,
401 An unrecognized option such as "--baz" will be found untouched in
402 `args` after `getopt` returns.
404 Help_Information_Generation:
405 If an option string is followed by another string, this string serves as a
406 description for this option. The `getopt` function returns a struct of type
407 `GetoptResult`. This return value contains information about all passed options
408 as well a $(D bool GetoptResult.helpWanted) flag indicating whether information
409 about these options was requested. The `getopt` function always adds an option for
410 `--help|-h` to set the flag if the option is seen on the command line.
413 A lone double-dash terminates `getopt` gathering. It is used to
414 separate program options from other parameters (e.g., options to be passed
415 to another program). Invoking the example above with $(D "--foo -- --bar")
416 parses foo but leaves "--bar" in `args`. The double-dash itself is
417 removed from the argument array unless the `std.getopt.config.keepEndOfOptions`
420 GetoptResult
getopt(T
...)(ref string
[] args
, T opts
)
422 import std
.exception
: enforce
;
424 "Invalid arguments string passed: program name missing");
428 GetOptException excep
;
429 void[][string
] visitedLongOpts
, visitedShortOpts
;
430 getoptImpl(args
, cfg
, rslt
, excep
, visitedLongOpts
, visitedShortOpts
, opts
);
432 if (!rslt
.helpWanted
&& excep
!is null)
443 auto args
= ["prog", "--foo", "-b"];
447 auto rslt
= getopt(args
, "foo|f", "Some information about foo.", &foo
, "bar|b",
448 "Some help message about bar.", &bar
);
452 defaultGetoptPrinter("Some information about the program.",
458 Configuration options for `getopt`.
460 You can pass them to `getopt` in any position, except in between an option
461 string and its bound pointer.
464 /// Turn case sensitivity on
466 /// Turn case sensitivity off (default)
470 /// Turn bundling off (default)
472 /// Pass unrecognized arguments through
474 /// Signal unrecognized arguments as errors (default)
476 /// Stop at first argument that does not look like an option
477 stopOnFirstNonOption
,
478 /// Do not erase the endOfOptions separator from args
480 /// Make the next option a required option
484 /** The result of the `getopt` function.
486 `helpWanted` is set if the option `--help` or `-h` was passed to the option parser.
488 struct GetoptResult
{
489 bool helpWanted
; /// Flag indicating if help was requested
490 Option
[] options
; /// All possible options
493 /** Information about an option.
496 string optShort
; /// The short symbol for this option
497 string optLong
; /// The long symbol for this option
498 string help
; /// The description of this option
499 bool required
; /// If a option is required, not passing it will result in an error
502 private pure Option
splitAndGet(string opt
) @trusted nothrow
504 import std
.array
: split
;
505 auto sp
= split(opt
, "|");
509 ret.optShort
= "-" ~ (sp
[0].length
< sp
[1].length ?
511 ret.optLong
= "--" ~ (sp
[0].length
> sp
[1].length ?
514 else if (sp
[0].length
> 1)
516 ret.optLong
= "--" ~ sp
[0];
520 ret.optShort
= "-" ~ sp
[0];
528 auto oshort
= splitAndGet("f");
529 assert(oshort
.optShort
== "-f");
530 assert(oshort
.optLong
== "");
532 auto olong
= splitAndGet("foo");
533 assert(olong
.optShort
== "");
534 assert(olong
.optLong
== "--foo");
536 auto oshortlong
= splitAndGet("f|foo");
537 assert(oshortlong
.optShort
== "-f");
538 assert(oshortlong
.optLong
== "--foo");
540 auto olongshort
= splitAndGet("foo|f");
541 assert(olongshort
.optShort
== "-f");
542 assert(olongshort
.optLong
== "--foo");
546 This function verifies that the variadic parameters passed in getOpt
549 [config override], option, [description], receiver,
551 - config override: a config value, optional
552 - option: a string or a char
553 - description: a string, optional
554 - receiver: a pointer or a callable
556 private template optionValidator(A
...)
558 import std
.format
: format
;
560 enum fmt
= "getopt validator: %s (at position %d)";
561 enum isReceiver(T
) = is(T
== U
*, U
) ||
(is(T
== function)) ||
(is(T
== delegate));
562 enum isOptionStr(T
) = isSomeString
!T || isSomeChar
!T
;
567 static if (A
.length
> 0)
569 static if (isReceiver
!(A
[0]))
571 msg
= format(fmt
, "first argument must be a string or a config", 0);
573 else static if (!isOptionStr
!(A
[0]) && !is(A
[0] == config
))
575 msg
= format(fmt
, "invalid argument type: " ~ A
[0].stringof
, 0);
579 static foreach (i
; 1 .. A
.length
)
581 static if (!isReceiver
!(A
[i
]) && !isOptionStr
!(A
[i
]) &&
582 !(is(A
[i
] == config
)))
584 msg
= format(fmt
, "invalid argument type: " ~ A
[i
].stringof
, i
);
587 else static if (isReceiver
!(A
[i
]) && !isOptionStr
!(A
[i
-1]))
589 msg
= format(fmt
, "a receiver can not be preceeded by a receiver", i
);
592 else static if (i
> 1 && isOptionStr
!(A
[i
]) && isOptionStr
!(A
[i
-1])
593 && isSomeString
!(A
[i
-2]))
595 msg
= format(fmt
, "a string can not be preceeded by two strings", i
);
600 static if (!isReceiver
!(A
[$-1]) && !is(A
[$-1] == config
))
602 msg
= format(fmt
, "last argument must be a receiver or a config",
609 enum message
= validator
;
610 alias optionValidator
= message
;
619 alias F
= void function();
621 static assert(optionValidator
!(S
,P
) == "");
622 static assert(optionValidator
!(S
,F
) == "");
623 static assert(optionValidator
!(A
,P
) == "");
624 static assert(optionValidator
!(A
,F
) == "");
626 static assert(optionValidator
!(C
,S
,P
) == "");
627 static assert(optionValidator
!(C
,S
,F
) == "");
628 static assert(optionValidator
!(C
,A
,P
) == "");
629 static assert(optionValidator
!(C
,A
,F
) == "");
631 static assert(optionValidator
!(C
,S
,S
,P
) == "");
632 static assert(optionValidator
!(C
,S
,S
,F
) == "");
633 static assert(optionValidator
!(C
,A
,S
,P
) == "");
634 static assert(optionValidator
!(C
,A
,S
,F
) == "");
636 static assert(optionValidator
!(C
,S
,S
,P
) == "");
637 static assert(optionValidator
!(C
,S
,S
,P
,C
,S
,F
) == "");
638 static assert(optionValidator
!(C
,S
,P
,C
,S
,S
,F
) == "");
640 static assert(optionValidator
!(C
,A
,P
,A
,S
,F
) == "");
641 static assert(optionValidator
!(C
,A
,P
,C
,A
,S
,F
) == "");
643 static assert(optionValidator
!(P
,S
,S
) != "");
644 static assert(optionValidator
!(P
,P
,S
) != "");
645 static assert(optionValidator
!(P
,F
,S
,P
) != "");
646 static assert(optionValidator
!(C
,C
,S
) != "");
647 static assert(optionValidator
!(S
,S
,P
,S
,S
,P
,S
) != "");
648 static assert(optionValidator
!(S
,S
,P
,P
) != "");
649 static assert(optionValidator
!(S
,S
,S
,P
) != "");
651 static assert(optionValidator
!(C
,A
,S
,P
,C
,A
,F
) == "");
652 static assert(optionValidator
!(C
,A
,P
,C
,A
,S
,F
) == "");
655 // https://issues.dlang.org/show_bug.cgi?id=15914
658 import std
.exception
: assertThrown
;
660 string
[] args
= ["program", "-a"];
661 getopt(args
, config
.passThrough
, 'a', &opt
);
664 args
= ["program", "-a"];
665 getopt(args
, 'a', &opt
);
668 args
= ["program", "-a"];
669 getopt(args
, 'a', "help string", &opt
);
672 args
= ["program", "-a"];
673 getopt(args
, config
.caseSensitive
, 'a', "help string", &opt
);
676 assertThrown(getopt(args
, "", "forgot to put a string", &opt
));
679 private void getoptImpl(T
...)(ref string
[] args
, ref configuration cfg
,
680 ref GetoptResult rslt
, ref GetOptException excep
,
681 void[][string
] visitedLongOpts
, void[][string
] visitedShortOpts
, T opts
)
683 enum validationMessage
= optionValidator
!T
;
684 static assert(validationMessage
== "", validationMessage
);
686 import std
.algorithm
.mutation
: remove
;
687 import std
.conv
: to
;
688 static if (opts
.length
)
690 static if (is(typeof(opts
[0]) : config
))
692 // it's a configuration flag, act on it
693 setConfig(cfg
, opts
[0]);
694 return getoptImpl(args
, cfg
, rslt
, excep
, visitedLongOpts
,
695 visitedShortOpts
, opts
[1 .. $]);
699 // it's an option string
700 auto option
= to
!string(opts
[0]);
701 if (option
.length
== 0)
703 excep
= new GetOptException("An option name may not be an empty string", excep
);
706 Option optionHelp
= splitAndGet(option
);
707 optionHelp
.required
= cfg
.required
;
709 if (optionHelp
.optLong
.length
)
711 assert(optionHelp
.optLong
!in visitedLongOpts
,
712 "Long option " ~ optionHelp
.optLong
~ " is multiply defined");
714 visitedLongOpts
[optionHelp
.optLong
] = [];
717 if (optionHelp
.optShort
.length
)
719 assert(optionHelp
.optShort
!in visitedShortOpts
,
720 "Short option " ~ optionHelp
.optShort
721 ~ " is multiply defined");
723 visitedShortOpts
[optionHelp
.optShort
] = [];
726 static if (is(typeof(opts
[1]) : string
))
728 alias receiver
= opts
[2];
729 optionHelp
.help
= opts
[1];
730 immutable lowSliceIdx
= 3;
734 alias receiver
= opts
[1];
735 immutable lowSliceIdx
= 2;
738 rslt
.options
~= optionHelp
;
741 // Handle options of the form --blah+
742 if (option
.length
&& option
[$ - 1] == autoIncrementChar
)
744 option
= option
[0 .. $ - 1];
748 bool optWasHandled
= handleOption(option
, receiver
, args
, cfg
, incremental
);
750 if (cfg
.required
&& !optWasHandled
)
752 excep
= new GetOptException("Required option "
753 ~ option
~ " was not supplied", excep
);
755 cfg
.required
= false;
757 getoptImpl(args
, cfg
, rslt
, excep
, visitedLongOpts
,
758 visitedShortOpts
, opts
[lowSliceIdx
.. $]);
763 // no more options to look for, potentially some arguments left
764 for (size_t i
= 1; i
< args
.length
;)
767 if (endOfOptions
.length
&& a
== endOfOptions
)
769 // Consume the "--" if keepEndOfOptions is not specified
770 if (!cfg
.keepEndOfOptions
)
771 args
= args
.remove(i
);
774 if (a
.length
< 2 || a
[0] != optionChar
)
777 if (cfg
.stopOnFirstNonOption
) break;
781 if (a
== "--help" || a
== "-h")
783 rslt
.helpWanted
= true;
784 args
= args
.remove(i
);
787 if (!cfg
.passThrough
)
789 throw new GetOptException("Unrecognized option "~a
, excep
);
795 helpOpt
.optShort
= "-h";
796 helpOpt
.optLong
= "--help";
797 helpOpt
.help
= "This help information.";
798 rslt
.options
~= helpOpt
;
802 private bool handleOption(R
)(string option
, R receiver
, ref string
[] args
,
803 ref configuration cfg
, bool incremental
)
805 import std
.algorithm
.iteration
: map
, splitter
;
806 import std
.ascii
: isAlpha
;
807 import std
.conv
: text
, to
;
808 // Scan arguments looking for a match for this option
810 for (size_t i
= 1; i
< args
.length
; )
813 if (endOfOptions
.length
&& a
== endOfOptions
) break;
814 if (cfg
.stopOnFirstNonOption
&& (!a
.length || a
[0] != optionChar
))
816 // first non-option is end of options
819 // Unbundle bundled arguments if necessary
820 if (cfg
.bundling
&& a
.length
> 2 && a
[0] == optionChar
&&
824 foreach (j
, dchar c
; a
[1 .. $])
826 // If the character is not alpha, stop right there. This allows
827 // e.g. -j100 to work as "pass argument 100 to option -j".
832 expanded
~= a
[j
+ 1 .. $];
835 expanded
~= text(optionChar
, c
);
837 args
= args
[0 .. i
] ~ expanded
~ args
[i
+ 1 .. $];
842 if (!optMatch(a
, option
, val
, cfg
))
851 // from here on, commit to eat args[i]
852 // (and potentially args[i + 1] too, but that comes later)
853 args
= args
[0 .. i
] ~ args
[i
+ 1 .. $];
855 static if (is(typeof(*receiver
) == bool))
859 // parse '--b=true/false'
860 *receiver
= to
!(typeof(*receiver
))(val
);
864 // no argument means set it to true
870 import std
.exception
: enforce
;
871 // non-boolean option, which might include an argument
872 enum isCallbackWithLessThanTwoParameters
=
873 (is(typeof(receiver
) == delegate) ||
is(typeof(*receiver
) == function)) &&
874 !is(typeof(receiver("", "")));
875 if (!isCallbackWithLessThanTwoParameters
&& !(val
.length
) && !incremental
)
877 // Eat the next argument too. Check to make sure there's one
878 // to be eaten first, though.
879 enforce
!GetOptException(i
< args
.length
,
880 "Missing value for argument " ~ a
~ ".");
882 args
= args
[0 .. i
] ~ args
[i
+ 1 .. $];
884 static if (is(typeof(*receiver
) == enum))
886 *receiver
= to
!(typeof(*receiver
))(val
);
888 else static if (is(typeof(*receiver
) : real))
891 if (incremental
) ++*receiver
;
892 else *receiver
= to
!(typeof(*receiver
))(val
);
894 else static if (is(typeof(*receiver
) == string
))
897 *receiver
= to
!(typeof(*receiver
))(val
);
899 else static if (is(typeof(receiver
) == delegate) ||
900 is(typeof(*receiver
) == function))
902 static if (is(typeof(receiver("", "")) : void))
904 // option with argument
905 receiver(option
, val
);
907 else static if (is(typeof(receiver("")) : void))
909 alias RType
= typeof(receiver(""));
910 static assert(is(RType
: void),
911 "Invalid receiver return type " ~ RType
.stringof
);
912 // boolean-style receiver
917 alias RType
= typeof(receiver());
918 static assert(is(RType
: void),
919 "Invalid receiver return type " ~ RType
.stringof
);
920 // boolean-style receiver without argument
924 else static if (isArray
!(typeof(*receiver
)))
927 import std
.range
: ElementEncodingType
;
928 alias E
= ElementEncodingType
!(typeof(*receiver
));
932 *receiver
~= to
!E(val
);
936 foreach (elem
; val
.splitter(arraySep
).map
!(a
=> to
!E(a
))())
940 else static if (isAssociativeArray
!(typeof(*receiver
)))
943 alias K
= typeof(receiver
.keys
[0]);
944 alias V
= typeof(receiver
.values
[0]);
946 import std
.range
: only
;
947 import std
.string
: indexOf
;
948 import std
.typecons
: Tuple
, tuple
;
950 static Tuple
!(K
, V
) getter(string input
)
952 auto j
= indexOf(input
, assignChar
);
953 enforce
!GetOptException(j
!= -1, "Could not find '"
954 ~ to
!string(assignChar
) ~ "' in argument '" ~ input
~ "'.");
955 auto key
= input
[0 .. j
];
956 auto value
= input
[j
+ 1 .. $];
957 return tuple(to
!K(key
), to
!V(value
));
960 static void setHash(Range
)(R receiver
, Range range
)
962 foreach (k
, v
; range
.map
!getter
)
967 setHash(receiver
, val
.only
);
969 setHash(receiver
, val
.splitter(arraySep
));
972 static assert(false, "getopt does not know how to handle the type " ~ typeof(receiver
).stringof
);
979 // https://issues.dlang.org/show_bug.cgi?id=17574
982 import std
.algorithm
.searching
: startsWith
;
986 string
[string
] mapping
;
987 immutable as
= arraySep
;
991 string
[] args
= ["testProgram", "-m", "a=b,c=\"d,e,f\""];
992 args
.getopt("m", &mapping
);
993 assert(false, "Exception not thrown");
995 catch (GetOptException goe
)
996 assert(goe
.msg
.startsWith("Could not find"));
999 // https://issues.dlang.org/show_bug.cgi?id=5316 - arrays with arraySep
1005 scope (exit
) arraySep
= "";
1008 auto args
= ["program.name", "-nfoo,bar,baz"];
1009 getopt(args
, "name|n", &names
);
1010 assert(names
== ["foo", "bar", "baz"], to
!string(names
));
1013 args
= ["program.name", "-n", "foo,bar,baz"];
1014 getopt(args
, "name|n", &names
);
1015 assert(names
== ["foo", "bar", "baz"], to
!string(names
));
1018 args
= ["program.name", "--name=foo,bar,baz"];
1019 getopt(args
, "name|n", &names
);
1020 assert(names
== ["foo", "bar", "baz"], to
!string(names
));
1023 args
= ["program.name", "--name", "foo,bar,baz"];
1024 getopt(args
, "name|n", &names
);
1025 assert(names
== ["foo", "bar", "baz"], to
!string(names
));
1028 // https://issues.dlang.org/show_bug.cgi?id=5316 - associative arrays with arraySep
1034 scope (exit
) arraySep
= "";
1037 values
= values
.init
;
1038 auto args
= ["program.name", "-vfoo=0,bar=1,baz=2"];
1039 getopt(args
, "values|v", &values
);
1040 assert(values
== ["foo":0, "bar":1, "baz":2], to
!string(values
));
1042 values
= values
.init
;
1043 args
= ["program.name", "-v", "foo=0,bar=1,baz=2"];
1044 getopt(args
, "values|v", &values
);
1045 assert(values
== ["foo":0, "bar":1, "baz":2], to
!string(values
));
1047 values
= values
.init
;
1048 args
= ["program.name", "--values=foo=0,bar=1,baz=2"];
1049 getopt(args
, "values|t", &values
);
1050 assert(values
== ["foo":0, "bar":1, "baz":2], to
!string(values
));
1052 values
= values
.init
;
1053 args
= ["program.name", "--values", "foo=0,bar=1,baz=2"];
1054 getopt(args
, "values|v", &values
);
1055 assert(values
== ["foo":0, "bar":1, "baz":2], to
!string(values
));
1059 The option character (default '-').
1061 Defaults to '-' but it can be assigned to prior to calling `getopt`.
1063 dchar optionChar
= '-';
1066 The string that conventionally marks the end of all options (default '--').
1068 Defaults to "--" but can be assigned to prior to calling `getopt`. Assigning an
1069 empty string to `endOfOptions` effectively disables it.
1071 string endOfOptions
= "--";
1074 The assignment character used in options with parameters (default '=').
1076 Defaults to '=' but can be assigned to prior to calling `getopt`.
1078 dchar assignChar
= '=';
1081 When set to "", parameters to array and associative array receivers are
1082 treated as an individual argument. That is, only one argument is appended or
1083 inserted per appearance of the option switch. If `arraySep` is set to
1084 something else, then each parameter is first split by the separator, and the
1085 individual pieces are treated as arguments to the same option.
1087 Defaults to "" but can be assigned to prior to calling `getopt`.
1089 string arraySep
= "";
1091 private enum autoIncrementChar
= '+';
1093 private struct configuration
1095 import std
.bitmanip
: bitfields
;
1097 bool, "caseSensitive", 1,
1098 bool, "bundling", 1,
1099 bool, "passThrough", 1,
1100 bool, "stopOnFirstNonOption", 1,
1101 bool, "keepEndOfOptions", 1,
1102 bool, "required", 1,
1106 private bool optMatch(string arg
, scope string optPattern
, ref string value
,
1107 configuration cfg
) @safe
1109 import std
.algorithm
.iteration
: splitter
;
1110 import std
.string
: indexOf
;
1111 import std
.uni
: icmp
;
1112 //writeln("optMatch:\n ", arg, "\n ", optPattern, "\n ", value);
1113 //scope(success) writeln("optMatch result: ", value);
1114 if (arg
.length
< 2 || arg
[0] != optionChar
) return false;
1115 // yank the leading '-'
1117 immutable isLong
= arg
.length
> 1 && arg
[0] == optionChar
;
1118 //writeln("isLong: ", isLong);
1119 // yank the second '-' if present
1120 if (isLong
) arg
= arg
[1 .. $];
1121 immutable eqPos
= indexOf(arg
, assignChar
);
1122 if (isLong
&& eqPos
>= 0)
1124 // argument looks like --opt=value
1125 value
= arg
[eqPos
+ 1 .. $];
1126 arg
= arg
[0 .. eqPos
];
1130 if (!isLong
&& eqPos
== 1)
1132 // argument looks like -o=value
1133 value
= arg
[2 .. $];
1137 if (!isLong
&& !cfg
.bundling
)
1139 // argument looks like -ovalue and there's no bundling
1140 value
= arg
[1 .. $];
1145 // argument looks like --opt, or -oxyz with bundling
1149 //writeln("Arg: ", arg, " pattern: ", optPattern, " value: ", value);
1151 foreach (v
; splitter(optPattern
, "|"))
1153 //writeln("Trying variant: ", v, " against ", arg);
1154 if (arg
== v ||
(!cfg
.caseSensitive
&& icmp(arg
, v
) == 0))
1156 if (cfg
.bundling
&& !isLong
&& v
.length
== 1
1157 && indexOf(arg
, v
) >= 0)
1159 //writeln("success");
1166 private void setConfig(ref configuration cfg
, config option
) @safe pure nothrow @nogc
1168 final switch (option
)
1170 case config
.caseSensitive
: cfg
.caseSensitive
= true; break;
1171 case config
.caseInsensitive
: cfg
.caseSensitive
= false; break;
1172 case config
.bundling
: cfg
.bundling
= true; break;
1173 case config
.noBundling
: cfg
.bundling
= false; break;
1174 case config
.passThrough
: cfg
.passThrough
= true; break;
1175 case config
.noPassThrough
: cfg
.passThrough
= false; break;
1176 case config
.required
: cfg
.required
= true; break;
1177 case config
.stopOnFirstNonOption
:
1178 cfg
.stopOnFirstNonOption
= true; break;
1179 case config
.keepEndOfOptions
:
1180 cfg
.keepEndOfOptions
= true; break;
1187 import std
.math
.operations
: isClose
;
1190 string
[] args
= ["program.name", "--paranoid", "--paranoid", "--paranoid"];
1191 getopt(args
, "paranoid+", ¶noid
);
1192 assert(paranoid
== 5, to
!(string
)(paranoid
));
1194 enum Color
{ no
, yes
}
1196 args
= ["program.name", "--color=yes",];
1197 getopt(args
, "color", &color
);
1198 assert(color
, to
!(string
)(color
));
1201 args
= ["program.name", "--color", "yes",];
1202 getopt(args
, "color", &color
);
1203 assert(color
, to
!(string
)(color
));
1205 string data
= "file.dat";
1207 bool verbose
= false;
1208 args
= ["program.name", "--length=5", "--file", "dat.file", "--verbose"];
1213 "verbose", &verbose
);
1214 assert(args
.length
== 1);
1215 assert(data
== "dat.file");
1216 assert(length
== 5);
1220 string
[] outputFiles
;
1221 args
= ["program.name", "--output=myfile.txt", "--output", "yourfile.txt"];
1222 getopt(args
, "output", &outputFiles
);
1223 assert(outputFiles
.length
== 2
1224 && outputFiles
[0] == "myfile.txt" && outputFiles
[1] == "yourfile.txt");
1228 args
= ["program.name", "--output", "myfile.txt,yourfile.txt"];
1229 getopt(args
, "output", &outputFiles
);
1230 assert(outputFiles
.length
== 2
1231 && outputFiles
[0] == "myfile.txt" && outputFiles
[1] == "yourfile.txt");
1235 [["program.name", "--tune=alpha=0.5", "--tune", "beta=0.6"],
1236 ["program.name", "--tune=alpha=0.5,beta=0.6"],
1237 ["program.name", "--tune", "alpha=0.5,beta=0.6"]])
1240 double[string
] tuningParms
;
1241 getopt(testArgs
, "tune", &tuningParms
);
1242 assert(testArgs
.length
== 1);
1243 assert(tuningParms
.length
== 2);
1244 assert(isClose(tuningParms
["alpha"], 0.5));
1245 assert(isClose(tuningParms
["beta"], 0.6));
1249 uint verbosityLevel
= 1;
1250 void myHandler(string option
)
1252 if (option
== "quiet")
1258 assert(option
== "verbose");
1262 args
= ["program.name", "--quiet"];
1263 getopt(args
, "verbose", &myHandler
, "quiet", &myHandler
);
1264 assert(verbosityLevel
== 0);
1265 args
= ["program.name", "--verbose"];
1266 getopt(args
, "verbose", &myHandler
, "quiet", &myHandler
);
1267 assert(verbosityLevel
== 2);
1270 void myHandler2(string option
, string value
)
1272 assert(option
== "verbose");
1275 args
= ["program.name", "--verbose", "2"];
1276 getopt(args
, "verbose", &myHandler2
);
1277 assert(verbosityLevel
== 2);
1284 args
= ["program.name", "--verbose"];
1285 getopt(args
, "verbose", &myHandler3
);
1286 assert(verbosityLevel
== 2);
1289 args
= ["program.name", "--foo", "--bAr"];
1291 std
.getopt
.config
.caseSensitive
,
1292 std
.getopt
.config
.passThrough
,
1295 assert(args
[1] == "--bAr");
1297 // test stopOnFirstNonOption
1299 args
= ["program.name", "--foo", "nonoption", "--bar"];
1302 std
.getopt
.config
.stopOnFirstNonOption
,
1305 assert(foo
&& !bar
&& args
[1] == "nonoption" && args
[2] == "--bar");
1307 args
= ["program.name", "--foo", "nonoption", "--zab"];
1310 std
.getopt
.config
.stopOnFirstNonOption
,
1313 assert(foo
&& !bar
&& args
[1] == "nonoption" && args
[2] == "--zab");
1315 args
= ["program.name", "--fb1", "--fb2=true", "--tb1=false"];
1318 getopt(args
, "fb1", &fb1
, "fb2", &fb2
, "tb1", &tb1
);
1319 assert(fb1
&& fb2
&& !tb1
);
1321 // test keepEndOfOptions
1323 args
= ["program.name", "--foo", "nonoption", "--bar", "--", "--baz"];
1325 std
.getopt
.config
.keepEndOfOptions
,
1328 assert(args
== ["program.name", "nonoption", "--", "--baz"]);
1330 // Ensure old behavior without the keepEndOfOptions
1332 args
= ["program.name", "--foo", "nonoption", "--bar", "--", "--baz"];
1336 assert(args
== ["program.name", "nonoption", "--baz"]);
1338 // test function callbacks
1340 static class MyEx
: Exception
1342 this() { super(""); }
1343 this(string option
) { this(); this.option
= option
; }
1344 this(string option
, string value
) { this(option
); this.value
= value
; }
1350 static void myStaticHandler1() { throw new MyEx(); }
1351 args
= ["program.name", "--verbose"];
1352 try { getopt(args
, "verbose", &myStaticHandler1
); assert(0); }
1353 catch (MyEx ex
) { assert(ex
.option
is null && ex
.value
is null); }
1355 static void myStaticHandler2(string option
) { throw new MyEx(option
); }
1356 args
= ["program.name", "--verbose"];
1357 try { getopt(args
, "verbose", &myStaticHandler2
); assert(0); }
1358 catch (MyEx ex
) { assert(ex
.option
== "verbose" && ex
.value
is null); }
1360 static void myStaticHandler3(string option
, string value
) { throw new MyEx(option
, value
); }
1361 args
= ["program.name", "--verbose", "2"];
1362 try { getopt(args
, "verbose", &myStaticHandler3
); assert(0); }
1363 catch (MyEx ex
) { assert(ex
.option
== "verbose" && ex
.value
== "2"); }
1365 // check that GetOptException is thrown if the value is missing
1366 args
= ["program.name", "--verbose"];
1367 try { getopt(args
, "verbose", &myStaticHandler3
); assert(0); }
1368 catch (GetOptException e
) {}
1369 catch (Exception e
) { assert(0); }
1372 @safe unittest // @safe std.getopt.config option use
1375 string
[] args
= ["program", "--inc-x", "--inc-x"];
1377 std
.getopt
.config
.caseSensitive
,
1378 "inc-x", "Add one to x", delegate void() { x
++; });
1382 // https://issues.dlang.org/show_bug.cgi?id=2142
1385 bool f_linenum
, f_filename
;
1386 string
[] args
= [ "", "-nl" ];
1390 std
.getopt
.config
.bundling
,
1391 //std.getopt.config.caseSensitive,
1392 "linenum|l", &f_linenum
,
1393 "filename|n", &f_filename
1399 // https://issues.dlang.org/show_bug.cgi?id=6887
1403 string
[] args
= ["", "-pa"];
1404 getopt(args
, "p", &p
);
1405 assert(p
.length
== 1);
1406 assert(p
[0] == "a");
1409 // https://issues.dlang.org/show_bug.cgi?id=6888
1413 auto args
= ["", "-t", "a=1"];
1414 getopt(args
, "t", &foo
);
1415 assert(foo
== ["a":1]);
1418 // https://issues.dlang.org/show_bug.cgi?id=9583
1422 auto args
= ["prog", "--opt=123", "--", "--a", "--b", "--c"];
1423 getopt(args
, "opt", &opt
);
1424 assert(args
== ["prog", "--a", "--b", "--c"]);
1430 auto args
= ["prog", "-thello", "-dbar=baz"];
1431 getopt(args
, "t", &foo
, "d", &bar
);
1432 assert(foo
== "hello");
1433 assert(bar
== "bar=baz");
1435 // From https://issues.dlang.org/show_bug.cgi?id=5762
1437 args
= ["prog", "-a-0x12"];
1438 getopt(args
, config
.bundling
, "a|addr", &a
);
1439 assert(a
== "-0x12", a
);
1440 args
= ["prog", "--addr=-0x12"];
1441 getopt(args
, config
.bundling
, "a|addr", &a
);
1442 assert(a
== "-0x12");
1444 // From https://issues.dlang.org/show_bug.cgi?id=11764
1445 args
= ["main", "-test"];
1447 args
.getopt(config
.passThrough
, "opt", &opt
);
1448 assert(args
== ["main", "-test"]);
1450 // From https://issues.dlang.org/show_bug.cgi?id=15220
1451 args
= ["main", "-o=str"];
1453 args
.getopt("o", &o
);
1456 args
= ["main", "-o=str"];
1458 args
.getopt(config
.bundling
, "o", &o
);
1462 // https://issues.dlang.org/show_bug.cgi?id=5228
1466 import std
.exception
;
1468 auto args
= ["prog", "--foo=bar"];
1470 assertThrown
!GetOptException(getopt(args
, "abc", &abc
));
1472 args
= ["prog", "--abc=string"];
1473 assertThrown
!ConvException(getopt(args
, "abc", &abc
));
1476 // https://issues.dlang.org/show_bug.cgi?id=7693
1479 import std
.exception
;
1486 auto args
= ["prog", "--foo=barZZZ"];
1488 assertThrown(getopt(args
, "foo", &foo
));
1489 args
= ["prog", "--foo=bar"];
1490 assertNotThrown(getopt(args
, "foo", &foo
));
1491 args
= ["prog", "--foo", "barZZZ"];
1492 assertThrown(getopt(args
, "foo", &foo
));
1493 args
= ["prog", "--foo", "baz"];
1494 assertNotThrown(getopt(args
, "foo", &foo
));
1497 // Same as https://issues.dlang.org/show_bug.cgi?id=7693 only for `bool`
1500 import std
.exception
;
1502 auto args
= ["prog", "--foo=truefoobar"];
1504 assertThrown(getopt(args
, "foo", &foo
));
1505 args
= ["prog", "--foo"];
1506 getopt(args
, "foo", &foo
);
1513 auto args
= ["prog", "--foo"];
1514 getopt(args
, "foo", &foo
);
1522 auto args
= ["prog", "--foo", "-b"];
1523 getopt(args
, config
.caseInsensitive
,"foo|f", "Some foo", &foo
,
1524 config
.caseSensitive
, "bar|b", "Some bar", &bar
);
1533 auto args
= ["prog", "-b", "--foo", "-z"];
1534 getopt(args
, config
.caseInsensitive
, config
.required
, "foo|f", "Some foo",
1535 &foo
, config
.caseSensitive
, "bar|b", "Some bar", &bar
,
1536 config
.passThrough
);
1543 import std
.exception
;
1547 auto args
= ["prog", "-b", "-z"];
1548 assertThrown(getopt(args
, config
.caseInsensitive
, config
.required
, "foo|f",
1549 "Some foo", &foo
, config
.caseSensitive
, "bar|b", "Some bar", &bar
,
1550 config
.passThrough
));
1555 import std
.exception
;
1559 auto args
= ["prog", "--foo", "-z"];
1560 assertNotThrown(getopt(args
, config
.caseInsensitive
, config
.required
,
1561 "foo|f", "Some foo", &foo
, config
.caseSensitive
, "bar|b", "Some bar",
1562 &bar
, config
.passThrough
));
1570 auto args
= ["prog", "-f"];
1571 auto r
= getopt(args
, config
.caseInsensitive
, "help|f", "Some foo", &foo
);
1573 assert(!r
.helpWanted
);
1576 @safe unittest // implicit help option without config.passThrough
1578 string
[] args
= ["program", "--help"];
1579 auto r
= getopt(args
);
1580 assert(r
.helpWanted
);
1583 // std.getopt: implicit help option breaks the next argument
1584 // https://issues.dlang.org/show_bug.cgi?id=13316
1587 string
[] args
= ["program", "--help", "--", "something"];
1589 assert(args
== ["program", "something"]);
1591 args
= ["program", "--help", "--"];
1593 assert(args
== ["program"]);
1596 args
= ["program", "--help", "nonoption", "--option"];
1597 getopt(args
, config
.stopOnFirstNonOption
, "option", &b
);
1598 assert(args
== ["program", "nonoption", "--option"]);
1601 // std.getopt: endOfOptions broken when it doesn't look like an option
1602 // https://issues.dlang.org/show_bug.cgi?id=13317
1605 auto endOfOptionsBackup
= endOfOptions
;
1606 scope(exit
) endOfOptions
= endOfOptionsBackup
;
1607 endOfOptions
= "endofoptions";
1608 string
[] args
= ["program", "endofoptions", "--option"];
1610 getopt(args
, "option", &b
);
1612 assert(args
== ["program", "--option"]);
1615 // make std.getopt ready for DIP 1000
1616 // https://issues.dlang.org/show_bug.cgi?id=20480
1619 string
[] args
= ["test", "--foo", "42", "--bar", "BAR"];
1622 getopt(args
, "foo", &foo
, "bar", "bar help", &bar
);
1624 assert(bar
== "BAR");
1627 /** This function prints the passed `Option`s and text in an aligned manner on `stdout`.
1629 The passed text will be printed first, followed by a newline, then the short
1630 and long version of every option will be printed. The short and long version
1631 will be aligned to the longest option of every `Option` passed. If the option
1632 is required, then "Required:" will be printed after the long version of the
1633 `Option`. If a help message is present it will be printed next. The format is
1634 illustrated by this code:
1639 writefln("%*s %*s%s%s", lengthOfLongestShortOption, it.optShort,
1640 lengthOfLongestLongOption, it.optLong,
1641 it.required ? " Required: " : " ", it.help);
1646 text = The text to printed at the beginning of the help output.
1647 opt = The `Option` extracted from the `getopt` parameter.
1649 void defaultGetoptPrinter(string text
, Option
[] opt
) @safe
1651 import std
.stdio
: stdout
;
1652 // stdout global __gshared is trusted with a locked text writer
1653 auto w
= (() @trusted => stdout
.lockingTextWriter())();
1655 defaultGetoptFormatter(w
, text
, opt
);
1658 /** This function writes the passed text and `Option` into an output range
1659 in the manner described in the documentation of function
1660 `defaultGetoptPrinter`, unless the style option is used.
1663 output = The output range used to write the help information.
1664 text = The text to print at the beginning of the help output.
1665 opt = The `Option` extracted from the `getopt` parameter.
1666 style = The manner in which to display the output of each `Option.`
1668 void defaultGetoptFormatter(Output
)(Output output
, string text
, Option
[] opt
, string style
= "%*s %*s%*s%s\n")
1670 import std
.algorithm
.comparison
: min
, max
;
1671 import std
.format
.write
: formattedWrite
;
1673 output
.formattedWrite("%s\n", text
);
1676 bool hasRequired
= false;
1679 ls
= max(ls
, it
.optShort
.length
);
1680 ll
= max(ll
, it
.optLong
.length
);
1682 hasRequired
= hasRequired || it
.required
;
1685 string re
= " Required: ";
1689 output
.formattedWrite(style
, ls
, it
.optShort
, ll
, it
.optLong
,
1690 hasRequired ? re
.length
: 1, it
.required ? re
: " ", it
.help
);
1701 auto args
= ["prog", "--foo"];
1702 auto t
= getopt(args
, "foo|f", "Help", &a
);
1704 auto app
= appender
!string();
1705 defaultGetoptFormatter(app
, "Some Text", t
.options
);
1707 string helpMsg
= app
.data
;
1709 assert(helpMsg
.length
);
1710 assert(helpMsg
.count("\n") == 3, to
!string(helpMsg
.count("\n")) ~ " "
1712 assert(helpMsg
.indexOf("--foo") != -1);
1713 assert(helpMsg
.indexOf("-f") != -1);
1714 assert(helpMsg
.indexOf("-h") != -1);
1715 assert(helpMsg
.indexOf("--help") != -1);
1716 assert(helpMsg
.indexOf("Help") != -1);
1718 string wanted
= "Some Text\n-f --foo Help\n-h --help This help "
1720 assert(wanted
== helpMsg
);
1729 auto args
= ["prog", "--foo"];
1730 auto t
= getopt(args
, config
.required
, "foo|f", "Help", &a
);
1732 auto app
= appender
!string();
1733 defaultGetoptFormatter(app
, "Some Text", t
.options
);
1735 string helpMsg
= app
.data
;
1737 assert(helpMsg
.length
);
1738 assert(helpMsg
.count("\n") == 3, to
!string(helpMsg
.count("\n")) ~ " "
1740 assert(helpMsg
.indexOf("Required:") != -1);
1741 assert(helpMsg
.indexOf("--foo") != -1);
1742 assert(helpMsg
.indexOf("-f") != -1);
1743 assert(helpMsg
.indexOf("-h") != -1);
1744 assert(helpMsg
.indexOf("--help") != -1);
1745 assert(helpMsg
.indexOf("Help") != -1);
1747 string wanted
= "Some Text\n-f --foo Required: Help\n-h --help "
1748 ~ " This help information.\n";
1749 assert(wanted
== helpMsg
, helpMsg
~ wanted
);
1752 // https://issues.dlang.org/show_bug.cgi?id=14724
1756 auto args
= ["prog", "--help"];
1760 rslt
= getopt(args
, config
.required
, "foo|f", "bool a", &a
);
1764 enum errorMsg
= "If the request for help was passed required options" ~
1766 assert(false, errorMsg
);
1769 assert(rslt
.helpWanted
);
1772 // throw on duplicate options
1775 import core
.exception
: AssertError
;
1776 import std
.exception
: assertNotThrown
, assertThrown
;
1777 auto args
= ["prog", "--abc", "1"];
1779 assertThrown
!AssertError(getopt(args
, "abc", &abc
, "abc", &abc
));
1780 assertThrown
!AssertError(getopt(args
, "abc|a", &abc
, "def|a", &def
));
1781 assertNotThrown
!AssertError(getopt(args
, "abc", &abc
, "def", &def
));
1784 // https://issues.dlang.org/show_bug.cgi?id=17327 repeated option use
1789 string
[] args
= ["program", "--num", "3"];
1790 getopt(args
, "n|num", &num
);
1793 args
= ["program", "--num", "3", "--num", "5"];
1794 getopt(args
, "n|num", &num
);
1797 args
= ["program", "--n", "3", "--num", "5", "-n", "-7"];
1798 getopt(args
, "n|num", &num
);
1801 void add1() { num
++; }
1802 void add2(string option
) { num
+= 2; }
1803 void addN(string option
, string value
)
1805 import std
.conv
: to
;
1806 num
+= value
.to
!long;
1810 args
= ["program", "--add1", "--add2", "--add1", "--add", "5", "--add2", "--add", "10"];
1812 "add1", "Add 1 to num", &add1
,
1813 "add2", "Add 2 to num", &add2
,
1814 "add", "Add N to num", &addN
,);
1818 args
= ["program", "--flag"];
1819 getopt(args
, "f|flag", "Boolean", &flag
);
1823 args
= ["program", "-f", "-f"];
1824 getopt(args
, "f|flag", "Boolean", &flag
);
1828 args
= ["program", "--flag=true", "--flag=false"];
1829 getopt(args
, "f|flag", "Boolean", &flag
);
1833 args
= ["program", "--flag=true", "--flag=false", "-f"];
1834 getopt(args
, "f|flag", "Boolean", &flag
);
1838 @system unittest // Delegates as callbacks
1840 alias TwoArgOptionHandler
= void delegate(string option
, string value
) @safe;
1842 TwoArgOptionHandler
makeAddNHandler(ref long dest
)
1844 void addN(ref long dest
, string n
)
1846 import std
.conv
: to
;
1850 return (option
, value
) => addN(dest
, value
);
1857 ["program", "--x-plus-1", "--x-plus-1", "--x-plus-5", "--x-plus-n", "10",
1858 "--y-plus-n", "25", "--y-plus-7", "--y-plus-n", "15", "--y-plus-3"];
1861 "x-plus-1", "Add one to x", delegate void() { x
+= 1; },
1862 "x-plus-5", "Add five to x", delegate void(string option
) { x
+= 5; },
1863 "x-plus-n", "Add NUM to x", makeAddNHandler(x
),
1864 "y-plus-7", "Add seven to y", delegate void() { y
+= 7; },
1865 "y-plus-3", "Add three to y", delegate void(string option
) { y
+= 3; },
1866 "y-plus-n", "Add NUM to x", makeAddNHandler(y
),);
1872 // Hyphens at the start of option values;
1873 // https://issues.dlang.org/show_bug.cgi?id=17650
1876 auto args
= ["program", "-m", "-5", "-n", "-50", "-c", "-", "-f", "-"];
1884 "m|mm", "integer", &m
,
1885 "n|nn", "integer", &n
,
1886 "c|cc", "character", &c
,
1887 "f|file", "filename or hyphen for stdin", &f
);
1895 // Hyphen at the option value;
1896 // https://issues.dlang.org/show_bug.cgi?id=22394
1899 auto args
= ["program", "-"];
1903 assert(args
== ["program", "-"]);
1913 auto args
= ["prog", "--foo"];
1914 auto t
= getopt(args
, "foo|f", "Help", &a
);
1916 auto app
= appender
!string();
1917 defaultGetoptFormatter(app
, "Some Text", t
.options
, "\t\t%*s %*s%*s\n%s\n");
1919 string helpMsg
= app
.data
;
1921 assert(helpMsg
.length
);
1922 assert(helpMsg
.count("\n") == 5, to
!string(helpMsg
.count("\n")) ~ " "
1924 assert(helpMsg
.indexOf("--foo") != -1);
1925 assert(helpMsg
.indexOf("-f") != -1);
1926 assert(helpMsg
.indexOf("-h") != -1);
1927 assert(helpMsg
.indexOf("--help") != -1);
1928 assert(helpMsg
.indexOf("Help") != -1);
1930 string wanted
= "Some Text\n\t\t-f --foo \nHelp\n\t\t-h --help \nThis help "
1932 assert(wanted
== helpMsg
);