tango.text.Arguments

License:

BSD style: see license.txt

Version:

Oct 2009: Initial release

Author:

Kris
class Arguments
Command-line argument parser. Simple usage is:
1
2
3
4
5
6
auto args = new Arguments;
args.parse ("-a -b", true);
auto a = args("a");
auto b = args("b");
if (a.set && b.set)
    ...
Argument parameters are assigned to the last known target, such that multiple parameters accumulate:
1
2
args.parse ("-a=1 -a=2 foo", true);
assert (args('a').assigned().length is 3);

That example results in argument 'a' assigned three parameters. Two parameters are explicitly assigned using '=', while a third is implicitly assigned(). Implicit parameters are often useful for collecting filenames or other parameters without specifying the associated argument:
1
2
args.parse ("thisfile.txt thatfile.doc -v", true);
assert (args(null).assigned().length is 2);
The 'null' argument is always defined and acts as an accumulator for parameters left uncaptured by other arguments. In the above instance it was assigned both parameters. Examples thus far have used 'sloppy' argument declaration, via the second argument of parse() being set true. This allows the parser to create argument declaration on-the-fly, which can be handy for trivial usage. However, most features require the a- priori declaration of arguments:
1
2
3
4
args = new Arguments;
args('x').required;
if (! args.parse("-x"))
      // x not supplied!

Sloppy arguments are disabled in that example, and a required argument 'x' is declared. The parse() method will fail if the pre-conditions are not fully met. Additional qualifiers include specifying how many parameters are allowed for each individual argument, default parameters, whether an argument requires the presence or exclusion of another, etc. Qualifiers are typically chained together and the following example shows argument "foo" being made required, with one parameter, aliased to 'f', and dependent upon the presence of another argument "bar":
1
2
args("foo").required.params(1).aliased('f').requires("bar");
args("help").aliased('?').aliased('h');

Parameters can be constrained to a set of matching text values, and the parser will fail on mismatched input:
1
2
args("greeting").restrict("hello", "yo", "gday");
args("enabled").restrict("true", "false", "t", "f", "y", "n");  

A set of declared arguments may be configured in this manner and the parser will return true only where all conditions are met. Where a error condition occurs you may traverse the set of arguments to find out which argument has what error. This can be handled like so, where arg.error holds a defined code:
1
2
3
4
if (! args.parse (...))
      foreach (arg; args)
               if (arg.error)
                   ...
Error codes are as follows:
1
2
3
4
5
6
7
8
None:           ok (zero)
ParamLo:        too few params for an argument
ParamHi:        too many params for an argument
Required:       missing argument is required 
Requires:       depends on a missing argument
Conflict:       conflicting argument is present
Extra:          unexpected argument (see sloppy)
Option:         parameter does not match options
A simpler way to handle errors is to invoke an internal format routine, which constructs error messages on your behalf:
1
2
if (! args.parse (...))
      stderr (args.errors(&stderr.layout.sprint));

Note that messages are constructed via a layout handler and the messages themselves may be customized (for i18n purposes). See the two errors() methods for more information on this.

The parser make a distinction between a short and long prefix, in that a long prefix argument is always distinct while short prefix arguments may be combined as a shortcut:
1
2
3
4
5
6
args.parse ("--foo --bar -abc", true);
assert (args("foo").set);
assert (args("bar").set);
assert (args("a").set);
assert (args("b").set);
assert (args("c").set);

In addition, short-prefix arguments may be "smushed" with an associated parameter when configured to do so:
1
2
3
args('o').params(1).smush;
if (args.parse ("-ofile"))
    assert (args('o').assigned()[0] == "file");

There are two callback varieties supports, where one is invoked when an associated argument is parsed and the other is invoked as parameters are assigned(). See the bind() methods for delegate signature details.

You may change the argument prefix to be something other than "-" and "--" via the constructor. You might, for example, need to specify a "/" indicator instead, and use ':' for explicitly assigning parameters:
1
2
3
4
5
6
7
8
auto args = new Args ("/", "-", ':');
args.parse ("-foo:param -bar /abc");
assert (args("foo").set);
assert (args("bar").set);
assert (args("a").set);
assert (args("b").set);
assert (args("c").set);
assert (args("foo").assigned().length is 1);

Returning to an earlier example we can declare some specifics:
1
2
3
args('v').params(0);
assert (args.parse (`-v thisfile.txt thatfile.doc`));
assert (args(null).assigned().length is 2);

Note that the -v flag is now in front of the implicit parameters but ignores them because it is declared to consume none. That is, implicit parameters are assigned to arguments from right to left, according to how many parameters said arguments may consume. Each sloppy argument consumes parameters by default, so those implicit parameters would have been assigned to -v without the declaration shown. On the other hand, an explicit assignment (via '=') always associates the parameter with that argument even when an overflow would occur (though will cause an error to be raised).

Certain parameters are used for capturing comments or other plain text from the user, including whitespace and other special chars. Such parameter values should be quoted on the commandline, and be assigned explicitly rather than implicitly:
1
args.parse (`--comment="-- a comment --"`);

Without the explicit assignment, the text content might otherwise be considered the start of another argument (due to how argv/argc values are stripped of original quotes).

Lastly, all subsequent text is treated as paramter-values after a "--" token is encountered. This notion is applied by unix systems to terminate argument processing in a similar manner. Such values are considered to be implicit, and are assigned to preceding args in the usual right to left fashion (or to the null argument):
1
2
args.parse (`-- -thisfile --thatfile`);
assert (args(null).assigned().length is 2);
this(const(char)[] sp = "-", const(char)[] lp = "--", char eq = '=')
Construct with the specific short & long prefixes, and the given assignment character (typically ':' on Windows but we set the defaults to look like unix instead)
bool parse(const(char)[] input, bool sloppy = false) [final]
Parse string[] into a set of Argument instances. The 'sloppy' option allows for unexpected arguments without error. Returns false where an error condition occurred, whereupon the arguments should be traversed to discover said condition(s):
1
2
3
auto args = new Arguments;
if (! args.parse (...))
      stderr (args.errors(&stderr.layout.sprint));
bool parse(const(char[])[] input, bool sloppy = false) [final]
Parse a string into a set of Argument instances. The 'sloppy' option allows for unexpected arguments without error. Returns false where an error condition occurred, whereupon the arguments should be traversed to discover said condition(s):
1
2
3
auto args = new Arguments;
if (! args.parse (...))
      Stderr (args.errors(&Stderr.layout.sprint));
Arguments clear() [final]
Clear parameter assignments, flags and errors. Note this does not remove any Arguments
Argument get(char name) [final]
Obtain an argument reference, creating an new instance where necessary. Use array indexing or opCall syntax if you prefer
Argument get(const(char)[] name) [final]
Obtain an argument reference, creating an new instance where necessary. Use array indexing or opCall syntax if you prefer.
Pass null to access the 'default' argument (where unassigned implicit parameters are gathered)
int opApply(scope int delegate(ref Argument) dg) [final]
Traverse the set of arguments
char[] errors(char[] delegate(char[] buf, const(char)[] fmt, ...) dg) [final]
Construct a string of error messages, using the given delegate to format the output. You would typically pass the system formatter here, like so:
1
auto msgs = args.errors (&stderr.layout.sprint);
The messages are replacable with custom (i18n) versions instead, using the errors(char[][]) method
Arguments errors(const(char[])[] errors) [final]
Use this method to replace the default error messages. Note that arguments are passed to the formatter in the following order, and these should be indexed appropriately by each of the error messages (see examples in errmsg above):
1
2
3
4
5
6
index 0: the argument name
index 1: number of parameters
index 2: configured minimum parameters
index 3: configured maximum parameters
index 4: conflicting/dependent argument (or invalid param)
index 5: array of configured parameter options
Arguments help(scope void delegate(const(char)[] arg, const(char)[] help) dg) [final]
Expose the configured set of help text, via the given delegate
class Argument
A specific argument instance. You get one of these from Arguments.get() and visit them via Arguments.opApply()
enum
Error identifiers:
1
2
3
4
5
6
7
8
None:           ok
ParamLo:        too few params for an argument
ParamHi:        too many params for an argument
Required:       missing argument is required 
Requires:       depends on a missing argument
Conflict:       conflicting argument is present
Extra:          unexpected argument (see sloppy)
Option:         parameter does not match options
int min [public]
int max [public]
int error [public]
error condition
bool set [public]
arg is present
char[] aliases [public]
Array of aliases
this(const(char)[] name)
Create with the given name
immutable(char)[] toString() [override]
Return the name of this argument
const(char[])[] assigned() [final]
return the assigned parameters, or the defaults if no parameters were assigned
Argument aliased(char name) [final]
Alias this argument with the given name. If you need long-names to be aliased, create the long-name first and alias it to a short one
Argument required() [@property, final]
Make this argument a requirement
Argument requires(Argument arg) [final]
Set this argument to depend upon another
Argument requires(const(char)[] other) [final]
Set this argument to depend upon another
Argument requires(char other) [final]
Set this argument to depend upon another
Argument conflicts(Argument arg) [final]
Set this argument to conflict with another
Argument conflicts(const(char)[] other) [final]
Set this argument to conflict with another
Argument conflicts(char other) [final]
Set this argument to conflict with another
Argument params() [final]
Enable parameter assignment: 0 to 42 by default
Argument params(int count) [final]
Set an exact number of parameters required
Argument params(int min, int max) [final]
Set both the minimum and maximum parameter counts
Argument defaults(const(char)[] values) [final]
Add another default parameter for this argument
Argument bind(Inspector inspector) [final]
Set an inspector for this argument, fired when a parameter is appended to an argument. Return null from the delegate when the value is ok, or a text string describing the issue to trigger an error
Argument bind(Invoker invoker) [final]
Set an invoker for this argument, fired when an argument declaration is seen
Argument smush(bool yes = true) [final]
Enable smushing for this argument, where "-ofile" would result in "file" being assigned to argument 'o'
Argument explicit() [@property, final]
Disable implicit arguments
Argument title(const(char)[] name) [final]
Alter the title of this argument, which can be useful for naming the default argument
Argument help(const(char)[] text) [final]
Set the help text for this argument
Argument halt() [final]
Fail the parse when this arg is encountered. You might use this for managing help text
Argument restrict(const(char[])[] options...) [final]
Restrict values to one of the given set