tango.text.Arguments

License:
BSD style:

Version:
Oct 2009: Initial release

author:
Kris

class Arguments;
Command-line argument parser. Simple usage is:
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:
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:
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:
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":
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:
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:
if (! args.parse (...))
      foreach (arg; args)
               if (arg.error)
                   ...


Error codes are as follows:
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:
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:
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:
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:
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:
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:
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):
args.parse (`-- -thisfile --thatfile`);
assert (args(null).assigned().length is 2);


Examples:
auto args = new Arguments;

// basic 
auto x = args['x'];
assert (args.parse (""));
x.required;
assert (args.parse ("") is false);
assert (args.clear().parse ("-x"));
assert (x.set);

// alias
x.aliased('X');
assert (args.clear().parse ("-X"));
assert (x.set);

// unexpected arg (with sloppy)
assert (args.clear().parse ("-y") is false);
assert (args.clear().parse ("-y") is false);
assert (args.clear().parse ("-y", true) is false);
assert (args['y'].set);
assert (args.clear().parse ("-x -y", true));

// parameters
x.params(0);
assert (args.clear().parse ("-x param"));
assert (x.assigned().length is 0);
assert (args(null).assigned().length is 1);
x.params(1);
assert (args.clear().parse ("-x=param"));
assert (x.assigned().length is 1);
assert (x.assigned()[0] == "param");
assert (args.clear().parse ("-x param"));
assert (x.assigned().length is 1);
assert (x.assigned()[0] == "param");

// too many args
x.params(1);
assert (args.clear().parse ("-x param1 param2"));
assert (x.assigned().length is 1);
assert (x.assigned()[0] == "param1");
assert (args(null).assigned().length is 1);
assert (args(null).assigned()[0] == "param2");

// now with default params
assert (args.clear().parse ("param1 param2 -x=blah"));
assert (args[null].assigned().length is 2);
assert (args(null).assigned().length is 2);
assert (x.assigned().length is 1);
x.params(0);
assert (!args.clear().parse ("-x=blah"));

// args as parameter
assert (args.clear().parse ("- -x"));
assert (args[null].assigned().length is 1);
assert (args[null].assigned()[0] == "-");

// multiple flags, with alias and sloppy
assert (args.clear().parse ("-xy"));
assert (args.clear().parse ("-xyX"));
assert (x.set);
assert (args['y'].set);
assert (args.clear().parse ("-xyz") is false);
assert (args.clear().parse ("-xyz", true));
auto z = args['z'];
assert (z.set);

// multiple flags with trailing arg
assert (args.clear().parse ("-xyz=10"));
assert (z.assigned().length is 1);

// again, but without sloppy param declaration
z.params(0);
assert (!args.clear().parse ("-xyz=10"));
assert (args.clear().parse ("-xzy=10"));
assert (args('y').assigned().length is 1);
assert (args('x').assigned().length is 0);
assert (args('z').assigned().length is 0);

// x requires y
x.requires('y');
assert (args.clear().parse ("-xy"));
assert (args.clear().parse ("-xz") is false);

// defaults
z.defaults("foo");
assert (args.clear().parse ("-xy"));
assert (z.assigned().length is 1);

// long names, with params
assert (args.clear().parse ("-xy --foobar") is false);
assert (args.clear().parse ("-xy --foobar", true));
assert (args["y"].set && x.set);
assert (args["foobar"].set);
assert (args.clear().parse ("-xy --foobar=10"));
assert (args["foobar"].assigned().length is 1);
assert (args["foobar"].assigned()[0] == "10");

// smush argument z, but not others
z.params();
assert (args.clear().parse ("-xy -zsmush") is false);
assert (x.set);
z.smush();
assert (args.clear().parse ("-xy -zsmush"));
assert (z.assigned().length is 1);
assert (z.assigned()[0] == "smush");
assert (x.assigned().length is 0);
z.params(0);

// conflict x with z
x.conflicts(z);
assert (args.clear().parse ("-xyz") is false);

// word mode, with prefix elimination
args = new Arguments (null, null);
assert (args.clear().parse ("foo bar wumpus") is false);
assert (args.clear().parse ("foo bar wumpus wombat", true));
assert (args("foo").set);
assert (args("bar").set);
assert (args("wumpus").set);
assert (args("wombat").set);

// use '/' instead of '-'
args = new Arguments ("/", "/");
assert (args.clear().parse ("/foo /bar /wumpus") is false);
assert (args.clear().parse ("/foo /bar /wumpus /wombat", true));
assert (args("foo").set);
assert (args("bar").set);
assert (args("wumpus").set);
assert (args("wombat").set);

// use '/' for short and '-' for long
args = new Arguments ("/", "-");
assert (args.clear().parse ("-foo -bar -wumpus -wombat /abc", true));
assert (args("foo").set);
assert (args("bar").set);
assert (args("wumpus").set);
assert (args("wombat").set);
assert (args("a").set);
assert (args("b").set);
assert (args("c").set);

// "--" makes all subsequent be implicit parameters
args = new Arguments;
version (dashdash)
        {
        args('f').params(0);
        assert (args.parse ("-f -- -bar -wumpus -wombat --abc"));
        assert (args('f').assigned().length is 0);
        assert (args(null).assigned().length is 4);
        }
     else
        {
        args('f').params(2);
        assert (args.parse ("-f -- -bar -wumpus -wombat --abc"));
        assert (args('f').assigned().length is 2);
        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)

final bool parse(const(char)[] input, bool sloppy = false);
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):
auto args = new Arguments;
if (! args.parse (...))
      stderr (args.errors(&stderr.layout.sprint));


final bool parse(const(char[])[] input, bool sloppy = false);
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):
auto args = new Arguments;
if (! args.parse (...))
      Stderr (args.errors(&Stderr.layout.sprint));


final Arguments clear();
Clear parameter assignments, flags and errors. Note this does not remove any Arguments

final Argument get(char name);
Obtain an argument reference, creating an new instance where necessary. Use array indexing or opCall syntax if you prefer

final Argument get(const(char)[] name);
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)

final int opApply(scope int delegate(ref Argument) dg);
Traverse the set of arguments

final char[] errors(char[] delegate(char[] buf, const(char)[] fmt, ...) dg);
Construct a string of error messages, using the given delegate to format the output. You would typically pass the system formatter here, like so:
auto msgs = args.errors (&stderr.layout.sprint);


The messages are replacable with custom (i18n) versions instead, using the errors(char[][]) method

final Arguments errors(const(char[])[] errors);
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):
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


final Arguments help(scope void delegate(const(char)[] arg, const(char)[] help) dg);
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()

int min;
minimum params

int max;
maximum params

int error;
error condition

bool set;
arg is present

char[] aliases;
Array of aliases

this(const(char)[] name);
Create with the given name

immutable(char)[] toString();
Return the name of this argument

final const(char[])[] assigned();
return the assigned parameters, or the defaults if no parameters were assigned

final Argument aliased(char name);
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

final @property Argument required();
Make this argument a requirement

final Argument requires(Argument arg);
Set this argument to depend upon another

final Argument requires(const(char)[] other);
Set this argument to depend upon another

final Argument requires(char other);
Set this argument to depend upon another

final Argument conflicts(Argument arg);
Set this argument to conflict with another

final Argument conflicts(const(char)[] other);
Set this argument to conflict with another

final Argument conflicts(char other);
Set this argument to conflict with another

final Argument params();
Enable parameter assignment: 0 to 42 by default

final Argument params(int count);
Set an exact number of parameters required

final Argument params(int min, int max);
Set both the minimum and maximum parameter counts

final Argument defaults(const(char)[] values);
Add another default parameter for this argument

final Argument bind(Inspector inspector);
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

final Argument bind(Invoker invoker);
Set an invoker for this argument, fired when an argument declaration is seen

final Argument smush(bool yes = true);
Enable smushing for this argument, where "-ofile" would result in "file" being assigned to argument 'o'

final @property Argument explicit();
Disable implicit arguments

final Argument title(const(char)[] name);
Alter the title of this argument, which can be useful for naming the default argument

final Argument help(const(char)[] text);
Set the help text for this argument

final Argument halt();
Fail the parse when this arg is encountered. You might use this for managing help text

final Argument restrict(const(char[])[] options...);
Restrict values to one of the given set


Page generated by Ddoc. Copyright (c) 2009 Kris. All rights reserved.