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);
- 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