| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127 | /******************************************************************************* copyright: Copyright (c) 2009 Kris. All rights reserved. license: BSD style: $(LICENSE) version: Oct 2009: Initial release author: Kris *******************************************************************************/ module tango.text.Arguments; private import tango.text.Util; private import tango.util.container.more.Stack; version=dashdash; // -- everything assigned to the null argument /******************************************************************************* 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); --- *******************************************************************************/ class Arguments { public alias get opCall; // args("name") public alias get opIndex; // args["name"] private Stack!(Argument) stack; // args with params private Argument[const(char)[]] args; // the set of args private Argument[const(char)[]] aliases; // set of aliases private char eq; // '=' or ':' private const(char)[] sp, // short prefix lp; // long prefix private const(char[])[] msgs = errmsg; // error messages private const const(char[])[] errmsg = // default errors [ "argument '{0}' expects {2} parameter(s) but has {1}\n", "argument '{0}' expects {3} parameter(s) but has {1}\n", "argument '{0}' is missing\n", "argument '{0}' requires '{4}'\n", "argument '{0}' conflicts with '{4}'\n", "unexpected argument '{0}'\n", "argument '{0}' expects one of {5}\n", "invalid parameter for argument '{0}': {4}\n", ]; /*********************************************************************** 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) ***********************************************************************/ this (const(char)[] sp="-", const(char)[] lp="--", char eq='=') { this.sp = sp; this.lp = lp; this.eq = eq; get(null).params(); // set null argument to consume params } /*********************************************************************** 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) { const(char[])[] tmp; foreach (s; quotes(input, " ")) tmp ~= s; return parse (tmp, sloppy); } /*********************************************************************** 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 bool parse (const(char[])[] input, bool sloppy=false) { bool done; int error; debug(Arguments) stdout.formatln ("\ncmdline: '{}'", input); stack.push (get(null)); foreach (s; input) { debug(Arguments) stdout.formatln ("'{}'", s); if (done is false) { if (s == "--") {done=true; version(dashdash){stack.clear().push(get(null));} continue;} else if (argument (s, lp, sloppy, false) || argument (s, sp, sloppy, true)) continue; } stack.top.append (s); } foreach (arg; args) error |= arg.valid(); return error is 0; } /*********************************************************************** Clear parameter assignments, flags and errors. Note this does not remove any Arguments ***********************************************************************/ final Arguments clear () { stack.clear(); foreach (arg; args) { arg.set = false; arg.values = null; arg.error = arg.None; } return this; } /*********************************************************************** Obtain an argument reference, creating an new instance where necessary. Use array indexing or opCall syntax if you prefer ***********************************************************************/ final Argument get (char name) { return get ((&name)[0..1]); } /*********************************************************************** 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 Argument get (const(char)[] name) { auto a = name in args; if (a is null) {name=name.dup; return args[name] = new Argument(name);} return *a; } /*********************************************************************** Traverse the set of arguments ***********************************************************************/ final int opApply (scope int delegate(ref Argument) dg) { int result; foreach (arg; args) if ((result=dg(arg)) != 0) break; return result; } /*********************************************************************** 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 char[] errors (char[] delegate(char[] buf, const(char)[] fmt, ...) dg) { char[256] tmp; char[] result; foreach (arg; args) { if (arg.error) result ~= dg (tmp, msgs[arg.error-1], arg.name, arg.values.length, arg.min, arg.max, arg.bogus, arg.options); } return result; } /*********************************************************************** 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 errors (const(char[])[] errors) { if (errors.length is errmsg.length) msgs = errors; else assert (false); return this; } /*********************************************************************** Expose the configured set of help text, via the given delegate ***********************************************************************/ final Arguments help (scope void delegate(const(char)[] arg, const(char)[] help) dg) { foreach (arg; args) if (arg.text.ptr) dg (arg.name, arg.text); return this; } /*********************************************************************** Test for the presence of a switch (long/short prefix) and enable the associated arg where found. Also look for and handle explicit parameter assignment ***********************************************************************/ private bool argument (const(char)[] s, const(char)[] p, bool sloppy, bool flag) { if (s.length >= p.length && s[0..p.length] == p) { s = s [p.length..$]; auto i = locate (s, eq); if (i < s.length) enable (s[0..i], sloppy, flag).append (s[i+1..$], true); else // trap empty arguments; attach as param to null-arg if (s.length) enable (s, sloppy, flag); else get(null).append (p, true); return true; } return false; } /*********************************************************************** Indicate the existance of an argument, and handle sloppy options along with multiple-flags and smushed parameters. Note that sloppy arguments are configured with parameters enabled. ***********************************************************************/ private Argument enable (const(char)[] elem, bool sloppy, bool flag=false) { if (flag && elem.length > 1) { // locate arg for first char auto arg = enable (elem[0..1], sloppy); elem = elem[1..$]; // drop further processing of this flag where in error if (arg.error is arg.None) { // smush remaining text or treat as additional args if (arg.cat) arg.append (elem, true); else arg = enable (elem, sloppy, true); } return arg; } // if not in args, or in aliases, then create new arg auto a = elem in args; if (a is null) if ((a = elem in aliases) is null) return get(elem).params().enable(!sloppy); return a.enable(); } /*********************************************************************** A specific argument instance. You get one of these from Arguments.get() and visit them via Arguments.opApply() ***********************************************************************/ class Argument { /*************************************************************** Error identifiers: --- 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 --- ***************************************************************/ enum {None, ParamLo, ParamHi, Required, Requires, Conflict, Extra, Option, Invalid}; alias void delegate() Invoker; alias const(char)[] delegate(const(char)[] value) Inspector; public int min, /// minimum params max, /// maximum params error; /// error condition public bool set; /// arg is present public char[] aliases; /// Array of aliases private bool req, // arg is required cat, // arg is smushable exp, // implicit params fail; // fail the parse public const(char)[] name, // arg name text; // help text private const(char)[] bogus; // name of conflict private const(char)[][] values, // assigned values options, // validation options deefalts; // configured defaults private Invoker invoker; // invocation callback private Inspector inspector; // inspection callback private Argument[] dependees, // who we require conflictees; // who we conflict with /*************************************************************** Create with the given name ***************************************************************/ this (const(char)[] name) { this.name = name; } /*************************************************************** Return the name of this argument ***************************************************************/ override immutable(char)[] toString() { return name.idup; } /*************************************************************** return the assigned parameters, or the defaults if no parameters were assigned ***************************************************************/ final const(char[])[] assigned () { return values.length ? values : deefalts; } /*************************************************************** 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 Argument aliased (char name) { this.outer.aliases[(&name)[0..1].idup] = this; this.aliases ~= name; return this; } /*************************************************************** Make this argument a requirement ***************************************************************/ @property final Argument required () { this.req = true; return this; } /*************************************************************** Set this argument to depend upon another ***************************************************************/ final Argument requires (Argument arg) { dependees ~= arg; return this; } /*************************************************************** Set this argument to depend upon another ***************************************************************/ final Argument requires (const(char)[] other) { return requires (this.outer.get(other)); } /*************************************************************** Set this argument to depend upon another ***************************************************************/ final Argument requires (char other) { return requires ((&other)[0..1]); } /*************************************************************** Set this argument to conflict with another ***************************************************************/ final Argument conflicts (Argument arg) { conflictees ~= arg; return this; } /*************************************************************** Set this argument to conflict with another ***************************************************************/ final Argument conflicts (const(char)[] other) { return conflicts (this.outer.get(other)); } /*************************************************************** Set this argument to conflict with another ***************************************************************/ final Argument conflicts (char other) { return conflicts ((&other)[0..1]); } /*************************************************************** Enable parameter assignment: 0 to 42 by default ***************************************************************/ final Argument params () { return params (0, 42); } /*************************************************************** Set an exact number of parameters required ***************************************************************/ final Argument params (int count) { return params (count, count); } /*************************************************************** Set both the minimum and maximum parameter counts ***************************************************************/ final Argument params (int min, int max) { this.min = min; this.max = max; return this; } /*************************************************************** Add another default parameter for this argument ***************************************************************/ final Argument defaults (const(char)[] values) { this.deefalts ~= values; return this; } /*************************************************************** 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 (Inspector inspector) { this.inspector = inspector; return this; } /*************************************************************** Set an invoker for this argument, fired when an argument declaration is seen ***************************************************************/ final Argument bind (Invoker invoker) { this.invoker = invoker; return this; } /*************************************************************** Enable smushing for this argument, where "-ofile" would result in "file" being assigned to argument 'o' ***************************************************************/ final Argument smush (bool yes=true) { cat = yes; return this; } /*************************************************************** Disable implicit arguments ***************************************************************/ @property final Argument explicit () { exp = true; return this; } /*************************************************************** Alter the title of this argument, which can be useful for naming the default argument ***************************************************************/ final Argument title (const(char)[] name) { this.name = name; return this; } /*************************************************************** Set the help text for this argument ***************************************************************/ final Argument help (const(char)[] text) { this.text = text; return this; } /*************************************************************** Fail the parse when this arg is encountered. You might use this for managing help text ***************************************************************/ final Argument halt () { this.fail = true; return this; } /*************************************************************** Restrict values to one of the given set ***************************************************************/ final Argument restrict (const(char[])[] options ...) { this.options = cast(const(char)[][])options; return this; } /*************************************************************** This arg is present, but set an error condition (Extra) when unexpected and sloppy is not enabled. Fires any configured invoker callback. ***************************************************************/ private Argument enable (bool unexpected=false) { this.set = true; if (max > 0) this.outer.stack.push(this); if (invoker) invoker(); if (unexpected) error = Extra; return this; } /*************************************************************** Append a parameter value, invoking an inspector as necessary ***************************************************************/ private void append (const(char)[] value, bool explicit=false) { // pop to an argument that can accept implicit parameters? if (explicit is false) for (auto s=&this.outer.stack; exp && s.size>1; this=s.top) s.pop(); this.set = true; // needed for default assignments values ~= value; // append new value if (error is None) { if (inspector) if ((bogus = inspector(value)).length) error = Invalid; if (options.length) { error = Option; foreach (option; options) if (option == value) error = None; } } // pop to an argument that can accept parameters for (auto s=&this.outer.stack; values.length >= max && s.size>1; this=s.top) s.pop(); } /*************************************************************** Test and set the error flag appropriately ***************************************************************/ private int valid () { if (error is None) { if (req && !set) error = Required; else if (set) { // short circuit? if (fail) return -1; if (values.length < min) error = ParamLo; else if (values.length > max) error = ParamHi; else { foreach (arg; dependees) if (! arg.set) error = Requires, bogus=arg.name; foreach (arg; conflictees) if (arg.set) error = Conflict, bogus=arg.name; } } } debug(Arguments) stdout.formatln ("{}: error={}, set={}, min={}, max={}, " "req={}, values={}, defaults={}, requires={}", name, error, set, min, max, req, values, deefalts, dependees); return error; } } } /******************************************************************************* *******************************************************************************/ debug(UnitTest) { unittest { 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); } } } /******************************************************************************* *******************************************************************************/ debug (Arguments) { import tango.io.Stdout; void main() { char[] crap = "crap"; auto args = new Arguments; args(null).title("root").params.help("root help"); args('x').aliased('X').params(0).required.help("x help"); args('y').defaults("hi").params(2).smush.explicit.help("y help"); args('a').required.defaults("hi").requires('y').params(1).help("a help"); args("foobar").params(2).help("foobar help"); if (! args.parse ("'one =two' -xa=bar -y=ff -yss --foobar=blah1 --foobar barf blah2 -- a b c d e")) stdout (args.errors(&stdout.layout.sprint)); else if (args.get('x')) args.help ((char[] a, char[] b){Stdout.formatln ("{}{}\n\t{}", args.lp, a, b);}); } } |