132
|
1 /*******************************************************************************
|
|
2 copyright: Copyright (c) 2007 Darryl Bleau. All rights reserved.
|
|
3
|
|
4 license: BSD style: $(LICENSE)
|
|
5
|
|
6 version: Oct2007
|
|
7 author: Darryl B, Jeff D
|
|
8
|
|
9 History:
|
|
10 ---
|
|
11 Date Who What
|
|
12 Sep2006 Darryl Bleau Original C module
|
|
13 Sep2007 Jeff Davey Ported to D, added comments.
|
|
14 Oct2007 Darryl Bleau Added validation delegates/functions, more comments.
|
|
15 ---
|
|
16 *******************************************************************************/
|
|
17
|
|
18 /*******************************************************************************
|
|
19
|
|
20 This module is used to parse arguments, and give easy access to them.
|
|
21
|
|
22 *******************************************************************************/
|
|
23
|
|
24 module tango.util.Arguments;
|
|
25
|
|
26 private import tango.core.Exception : TracedException;
|
|
27
|
|
28 /***********************************************************************
|
|
29
|
|
30 This exception is thrown during argument validation.
|
|
31
|
|
32 ***********************************************************************/
|
|
33
|
|
34 public class ArgumentException : TracedException
|
|
35 {
|
|
36 /***********************************************************************
|
|
37
|
|
38 The reason the exception was thrown
|
|
39
|
|
40 ***********************************************************************/
|
|
41
|
|
42 enum ExceptionReason
|
|
43 {
|
|
44 /***********************************************************************
|
|
45 An invalid parameter was passed
|
|
46 ***********************************************************************/
|
|
47
|
|
48 INVALID_PARAMETER,
|
|
49 /***********************************************************************
|
|
50 A parameter wasn't passed to an argument when it was expected
|
|
51 ***********************************************************************/
|
|
52
|
|
53 MISSING_PARAMETER,
|
|
54 /***********************************************************************
|
|
55 An argument was missing
|
|
56 ***********************************************************************/
|
|
57
|
|
58 MISSING_ARGUMENT
|
|
59 }
|
|
60
|
|
61 private char[] _name;
|
|
62 private char[] _parameter;
|
|
63 private ExceptionReason _reason;
|
|
64
|
|
65 /***********************************************************************
|
|
66
|
|
67 The name of the specific argument
|
|
68
|
|
69 ***********************************************************************/
|
|
70
|
|
71 char[] name() { return _name; }
|
|
72
|
|
73 /***********************************************************************
|
|
74
|
|
75 The parameter to the argument
|
|
76
|
|
77 ***********************************************************************/
|
|
78
|
|
79 char[] parameter() { return _parameter; }
|
|
80
|
|
81 /***********************************************************************
|
|
82
|
|
83 The enum to the reason it failed
|
|
84
|
|
85 ***********************************************************************/
|
|
86
|
|
87 ExceptionReason reason() { return _reason; }
|
|
88
|
|
89 this(char[] msg, char[] name, char[] parameter, ExceptionReason reason)
|
|
90 {
|
|
91 _name = name;
|
|
92 _parameter = parameter;
|
|
93 _reason = reason;
|
|
94 super(msg);
|
|
95 }
|
|
96 }
|
|
97
|
|
98 /***********************************************************************
|
|
99
|
|
100 The Arguments class is used to parse arguments and encapsulate the
|
|
101 parameters passed by an application user.
|
|
102
|
|
103 The command line arguments into an array of found arguments and their
|
|
104 respective parameters (if any).
|
|
105
|
|
106 Arguments can be short (-), long (--), or implicit. Arguments can
|
|
107 optionally be passed parameters. For example, this module
|
|
108 parses command lines such as: "-a -b -c --long --argument=parameter"
|
|
109
|
|
110 Example:
|
|
111 ---
|
|
112 char[][] arguments = [ "programname", "-a:on", "-abc:on", "--this=good", "-x:on" ];
|
|
113 Arguments args = new Arguments(arguments);
|
|
114 if (args)
|
|
115 {
|
|
116 args.addValidation("b", true, false);
|
|
117 args.addValidation("c", true, true);
|
|
118 args.addValidation("x", false, true);
|
|
119 args.addValidation("this", false, true);
|
|
120 args.addValidation("this", (char[] a) { (return a.length < 5); });
|
|
121 try
|
|
122 {
|
|
123 args.validate();
|
|
124 return Test.Status.Success;
|
|
125 }
|
|
126 catch (ArgumentException ex)
|
|
127 {
|
|
128 messages ~= Stdout.layout.convert("{}: {} - {}", ex.name, ex.msg, ex.reason == ArgumentException.ExceptionReason.INVALID_PARAMETER ? "invalid parameter" : ex.reason == ArgumentException.ExceptionReason.MISSING_PARAMETER ? "missing parameter" : "missing argument");
|
|
129 }
|
|
130 }
|
|
131 ---
|
|
132
|
|
133 Syntax:
|
|
134 ---
|
|
135 Short Argument - -[x][:=]?[ parameter]...
|
|
136 Long Argument - --[long][:=]?[ parameter]...
|
|
137 Implicit Arguments - [implicitName]... Multiple implicit arguments are allowed.
|
|
138 ---
|
|
139
|
|
140 Usage:
|
|
141
|
|
142 Short options can be grouped in a single dash. The following are equivalent.
|
|
143 ---
|
|
144 - "myprogram -a -b -c"
|
|
145 - "myprogram -abc"
|
|
146 ---
|
|
147
|
|
148 Arguments can be passed with space, '=', or ':'. The following are equivalent.
|
|
149 ---
|
|
150 - "myprogram -c arg"
|
|
151 - "myprogram -c=arg"
|
|
152 - "myprogram -c:arg"
|
|
153 ---
|
|
154
|
|
155 As are these.
|
|
156 ---
|
|
157 - "myprogram --long arg"
|
|
158 - "myprogram --long=arg"
|
|
159 - "myprogram --long:arg"
|
|
160 ---
|
|
161
|
|
162 Arguments can contain either '=' or ':', but not both. For example.
|
|
163 the following results in the argument 'long' being set to 'arg=other'
|
|
164 and 'arg:other', respectively.
|
|
165 ---
|
|
166 - "myprogram --long:arg=other"
|
|
167 - "myprogram --long=arg:other"
|
|
168 ---
|
|
169
|
|
170 Blank dashes are ignored. The following are all equivalent.
|
|
171 ---
|
|
172 - "myprogram -c -- -a"
|
|
173 - "myprogram -c - -a"
|
|
174 - "myprogram - - - -a -- -c"
|
|
175 ---
|
|
176
|
|
177 In the absence of implicit arguments, short options can be infered when
|
|
178 they come first. Given no implicit arguments, the following are equivalent.
|
|
179 ---
|
|
180 - "myprogram abc"
|
|
181 - "myprogram -abc"
|
|
182 - "myprogram -a -b -c"
|
|
183 ---
|
|
184
|
|
185 Short options are case sensitive, while long options are not. The following
|
|
186 are equivalent.
|
|
187 ---
|
|
188 - "myprogram -a -A -LONG"
|
|
189 - "myprogram -a -A -Long"
|
|
190 - "myprogram -a -A -long"
|
|
191 ---
|
|
192
|
|
193 In the event of multiple definitions of an argument, any parameters given
|
|
194 will be concatenated. The following are equivalent.
|
|
195 ---
|
|
196 - "myprogram -a one two three"
|
|
197 - "myprogram -a one -a two -a three"
|
|
198 - "myprogram -a:one two -a=three"
|
|
199 ---
|
|
200
|
|
201 Multiple parameters can be iterated through using via the opIndex operator.
|
|
202 For example, given:
|
|
203 ---
|
|
204 - "myprogram -collect one two three '4 5 6'"
|
|
205 ---
|
|
206 args["collect"] will return a char[][] array ["one", "two", "three", "4 5 6"].
|
|
207
|
|
208 Implicit arguments can be defined by passing in an implicit arguments array,
|
|
209 which may look something like: ["first", "second"].
|
|
210 Given implicit arguments, any non-argument command line parameters will be
|
|
211 automatically assigned to the implicit arguments,
|
|
212 in the order they were given.
|
|
213 For example, given the implicit arguments ["first", "second"] and command line:
|
|
214 ---
|
|
215 - "myprogram hello there bob"
|
|
216 ---
|
|
217 The argument 'first' will be assigned 'hello', and the argument 'second' will
|
|
218 be assigned both 'there' and 'bob'.
|
|
219
|
|
220 Any intervening arguments will end the assignment. For example, given:
|
|
221 ---
|
|
222 - "myprogram hello there bob -a how are you"
|
|
223 ---
|
|
224 'first' is assigned 'hello', 'second' is assigned 'there' and 'bob', and 'a'
|
|
225 is assigned 'how', 'are', and 'you'.
|
|
226
|
|
227 Implicit arguments also allows programs to support non-option arguments,
|
|
228 given implicit arguments ["actions"], and a command line such as:
|
|
229 ---
|
|
230 - "myprogram mop sweep get_coffee -time:now"
|
|
231 ---
|
|
232 args["actions"] will contain ["mop", "sweep", "get_coffee"], and
|
|
233 args["time"] will contain "now".
|
|
234
|
|
235 ***********************************************************************/
|
|
236
|
|
237 public class Arguments
|
|
238 {
|
|
239 /// Function used to validate multiple parameters at once.
|
|
240 alias bool function(char[][] params, inout char[] invalidParam) validationFunctionMulti;
|
|
241 /// Delegate used to validate multiple parameters at once.
|
|
242 alias bool delegate(char[][] params, inout char[] invalidParam) validationDelegateMulti;
|
|
243 /// Function used to validate single parameters at a time.
|
|
244 alias bool function(char[] param) validationFunction;
|
|
245 /// Delegate used to validate single parameters at a time.
|
|
246 alias bool delegate(char[] param) validationDelegate;
|
|
247
|
|
248 private char[][][char[]] _args;
|
|
249 private char[] _program;
|
|
250 private struct validation
|
|
251 {
|
|
252 validationFunction[] validF;
|
|
253 validationDelegate[] validD;
|
|
254 validationFunctionMulti[] validFM;
|
|
255 validationDelegateMulti[] validDM;
|
|
256 bool required;
|
|
257 bool paramRequired;
|
|
258 }
|
|
259 private validation[char[]] _validations;
|
|
260
|
|
261 private char[] parseLongArgument(char[] arg)
|
|
262 {
|
|
263 char[] rtn;
|
|
264
|
|
265 int locate(char[] arg, char c) {
|
|
266 foreach (i, a; arg)
|
|
267 if (a is c)
|
|
268 return i;
|
|
269 return arg.length;
|
|
270 }
|
|
271
|
|
272
|
|
273 if (arg)
|
|
274 {
|
|
275 int equalDelim = locate(arg, '=');
|
|
276 int colonDelim = locate(arg, ':');
|
|
277 int paramPos = ((equalDelim != arg.length) && (colonDelim != arg.length)) ? (equalDelim < colonDelim ? equalDelim : colonDelim) : (equalDelim != arg.length) ? equalDelim : colonDelim;
|
|
278 if (paramPos != arg.length)
|
|
279 {
|
|
280 char[] argName = arg[0 .. paramPos];
|
|
281 char[] value = arg[(paramPos + 1) .. arg.length];
|
|
282 setArg(argName, value);
|
|
283 rtn = argName;
|
|
284 }
|
|
285 else
|
|
286 {
|
|
287 setArg(arg, null);
|
|
288 rtn = arg;
|
|
289 }
|
|
290 }
|
|
291 return rtn;
|
|
292 }
|
|
293
|
|
294 private void setArg(char[] arg, char[] value)
|
|
295 {
|
|
296 if (arg)
|
|
297 {
|
|
298 if ((arg in _args) || (value !is null))
|
|
299 _args[arg] ~= value;
|
|
300 else
|
|
301 _args[arg] = null;
|
|
302 }
|
|
303 }
|
|
304
|
|
305 private char[] parseShortArgument(char[] arg)
|
|
306 {
|
|
307 char[] rtn;
|
|
308
|
|
309 if (arg)
|
|
310 {
|
|
311 char[] argName;
|
|
312 uint i;
|
|
313 for (i = 0; i < arg.length; i++)
|
|
314 {
|
|
315 if (arg[i] != '=' && arg[i] != ':')
|
|
316 {
|
|
317 argName = arg[i .. i + 1];
|
|
318 setArg(argName, null);
|
|
319 }
|
|
320 else if (((arg.length) - i) > 1)
|
|
321 {
|
|
322 setArg(argName, arg[i+1 .. arg.length]);
|
|
323 break;
|
|
324 }
|
|
325 }
|
|
326 rtn = argName;
|
|
327 }
|
|
328 return rtn;
|
|
329 }
|
|
330
|
|
331 /***********************************************************************
|
|
332
|
|
333 Allows external argument assignment, works the same as command line
|
|
334 in that it appends to any values already assigned to the given key.
|
|
335
|
|
336 Params:
|
|
337 value = assigned value
|
|
338 key = key to assign to
|
|
339
|
|
340 ***********************************************************************/
|
|
341
|
|
342 void opIndexAssign(char[] value, char[] key)
|
|
343 {
|
|
344 setArg(key, value);
|
|
345 }
|
|
346
|
|
347 /***********************************************************************
|
|
348
|
|
349 Allows removal of keys from the arguments. Useful if you want to replace
|
|
350 values for a given key rather than to append to them.
|
|
351
|
|
352 Params:
|
|
353 key = key to remove values from.
|
|
354
|
|
355 ***********************************************************************/
|
|
356
|
|
357 void remove(char[] key)
|
|
358 {
|
|
359 _args[key] = null;
|
|
360 }
|
|
361
|
|
362 /***********************************************************************
|
|
363
|
|
364 Directly access an argument's parameters via opIndex operator as an
|
|
365 array.
|
|
366 This is to cover something like: param1 "parm with space" param2
|
|
367
|
|
368 ***********************************************************************/
|
|
369
|
|
370 char[][] opIndex(char[] key)
|
|
371 {
|
|
372 char[][] rtn = null;
|
|
373 if (key && (key in _args))
|
|
374 rtn = _args[key];
|
|
375 return rtn;
|
|
376 }
|
|
377
|
|
378 /***********************************************************************
|
|
379
|
|
380 Operator is used to check if the argument exists
|
|
381
|
|
382 ***********************************************************************/
|
|
383
|
|
384 bool opIn_r(char[] key)
|
|
385 {
|
|
386 bool rtn = false;
|
|
387 if (key)
|
|
388 rtn = (key in _args) != null;
|
|
389 return rtn;
|
|
390 }
|
|
391
|
|
392 /***********************************************************************
|
|
393
|
|
394 Adds a validation to the arguments
|
|
395
|
|
396 Params:
|
|
397 argument = the argument name
|
|
398 required = specifies if this argument is required
|
|
399 paramRequired = specifies if this argument requires a parameter
|
|
400
|
|
401 ***********************************************************************/
|
|
402
|
|
403 void addValidation(char[] argument, bool required, bool paramRequired)
|
|
404 {
|
|
405 if (argument)
|
|
406 {
|
|
407 validation* val = getValidation(argument);
|
|
408 if (val !is null)
|
|
409 {
|
|
410 val.required = required;
|
|
411 val.paramRequired = paramRequired;
|
|
412 }
|
|
413 }
|
|
414 }
|
|
415
|
|
416 /***********************************************************************
|
|
417
|
|
418 Adds a validation to the arguments
|
|
419
|
|
420 Params:
|
|
421 argument = the argument name
|
|
422 validF = a validation function for single parameters
|
|
423
|
|
424 ***********************************************************************/
|
|
425
|
|
426 void addValidation(char[] argument, validationFunction validF)
|
|
427 {
|
|
428 if (argument && validF)
|
|
429 {
|
|
430 validation* val = getValidation(argument);
|
|
431 if (val !is null)
|
|
432 val.validF ~= validF;
|
|
433 }
|
|
434 }
|
|
435
|
|
436 /***********************************************************************
|
|
437
|
|
438 Adds a validation to the arguments
|
|
439
|
|
440 Params:
|
|
441 argument = the argument name
|
|
442 validD = a validation delegate for single parameters
|
|
443
|
|
444 ***********************************************************************/
|
|
445
|
|
446 void addValidation(char[] argument, validationDelegate validD)
|
|
447 {
|
|
448 if (argument && validD)
|
|
449 {
|
|
450 validation* val = getValidation(argument);
|
|
451 if (val !is null)
|
|
452 val.validD ~= validD;
|
|
453 }
|
|
454 }
|
|
455
|
|
456 /***********************************************************************
|
|
457
|
|
458 Adds a validation to the arguments
|
|
459
|
|
460 Params:
|
|
461 argument = the argument name
|
|
462 validF = a validation function for multiple parameters
|
|
463
|
|
464 ***********************************************************************/
|
|
465
|
|
466 void addValidation(char[] argument, validationFunctionMulti validFM)
|
|
467 {
|
|
468 if (argument && validFM)
|
|
469 {
|
|
470 validation* val = getValidation(argument);
|
|
471 if (val !is null)
|
|
472 val.validFM ~= validFM;
|
|
473 }
|
|
474 }
|
|
475
|
|
476 /***********************************************************************
|
|
477
|
|
478 Adds a validation to the arguments
|
|
479
|
|
480 Params:
|
|
481 argument = the argument name
|
|
482 validD = a validation delegate for multiple parameters
|
|
483
|
|
484 ***********************************************************************/
|
|
485
|
|
486 void addValidation(char[] argument, validationDelegateMulti validDM)
|
|
487 {
|
|
488 if (argument && validDM)
|
|
489 {
|
|
490 validation* val = getValidation(argument);
|
|
491 if (val !is null)
|
|
492 val.validDM ~= validDM;
|
|
493 }
|
|
494 }
|
|
495
|
|
496 private validation* getValidation(char[] argument)
|
|
497 {
|
|
498 validation* rtn = null;
|
|
499 if (!(argument in _validations))
|
|
500 {
|
|
501 validation newValidation;
|
|
502 _validations[argument] = newValidation;
|
|
503 }
|
|
504 if (argument in _validations)
|
|
505 rtn = &(_validations[argument]);
|
|
506 return rtn;
|
|
507 }
|
|
508
|
|
509 /***********************************************************************
|
|
510
|
|
511 Validates the parsed arguments.
|
|
512
|
|
513 Throws ArgumentException if it finds something wrong.
|
|
514
|
|
515 ***********************************************************************/
|
|
516
|
|
517 void validate()
|
|
518 {
|
|
519 foreach(char[] argument, validation val; _validations)
|
|
520 {
|
|
521 if (val.required && !(argument in _args))
|
|
522 throw new ArgumentException("Argument required.", argument, null, ArgumentException.ExceptionReason.MISSING_ARGUMENT);
|
|
523 if (val.paramRequired && (argument in _args) && (_args[argument].length == 0))
|
|
524 throw new ArgumentException("Parameter required.", argument, null, ArgumentException.ExceptionReason.MISSING_PARAMETER);
|
|
525 if ((argument in _args) && (_args[argument].length > 0))
|
|
526 {
|
|
527 char[] invalidParameter = null;
|
|
528 foreach(validationFunctionMulti validFM; val.validFM)
|
|
529 if (!validFM(_args[argument], invalidParameter))
|
|
530 break;
|
|
531 if (invalidParameter is null)
|
|
532 {
|
|
533 foreach(validationDelegateMulti validDM; val.validDM)
|
|
534 if (!validDM(_args[argument], invalidParameter))
|
|
535 break;
|
|
536 if (invalidParameter is null)
|
|
537 {
|
|
538 foreach(char[] arg; _args[argument])
|
|
539 {
|
|
540 foreach(validationFunction validF; val.validF)
|
|
541 {
|
|
542 if (!validF(arg))
|
|
543 {
|
|
544 invalidParameter = arg;
|
|
545 break;
|
|
546 }
|
|
547 }
|
|
548 if (invalidParameter is null)
|
|
549 {
|
|
550 foreach(validationDelegate validD; val.validD)
|
|
551 {
|
|
552 if (!validD(arg))
|
|
553 {
|
|
554 invalidParameter = arg;
|
|
555 break;
|
|
556 }
|
|
557 }
|
|
558 }
|
|
559 }
|
|
560 }
|
|
561 }
|
|
562 if (invalidParameter !is null)
|
|
563 throw new ArgumentException("Invalid parameter.", argument, invalidParameter, ArgumentException.ExceptionReason.INVALID_PARAMETER);
|
|
564 }
|
|
565 }
|
|
566 }
|
|
567
|
|
568 /***********************************************************************
|
|
569
|
|
570 Parse the arguments according to the passed implicitArg list
|
|
571 and aliases.
|
|
572
|
|
573 ***********************************************************************/
|
|
574
|
|
575
|
|
576 void parse(char[][] arguments, char[][] implicitArgs, char[][][] aliases)
|
|
577 {
|
|
578 char[] lastArgumentSet;
|
|
579 uint currentImplicitArg = 0;
|
|
580 for (uint i = 1; i < arguments.length; i++)
|
|
581 {
|
|
582 char[] currentArgument = arguments[i];
|
|
583 if (currentArgument)
|
|
584 {
|
|
585 if (currentArgument[0] == '-')
|
|
586 {
|
|
587 if (currentArgument.length > 1)
|
|
588 {
|
|
589 if (currentArgument[1] == '-')
|
|
590 {
|
|
591 if (currentArgument.length > 2)
|
|
592 lastArgumentSet = parseLongArgument(currentArgument[2 .. currentArgument.length]); // long argument
|
|
593 }
|
|
594 else
|
|
595 lastArgumentSet = parseShortArgument(currentArgument[1 .. currentArgument.length]); // short argument
|
|
596 }
|
|
597 }
|
|
598 else
|
|
599 {
|
|
600 char[] argName;
|
|
601 // implicit argument / previously set argument
|
|
602 if (implicitArgs && (currentImplicitArg < implicitArgs.length))
|
|
603 lastArgumentSet = argName = implicitArgs[currentImplicitArg++];
|
|
604 else
|
|
605 argName = lastArgumentSet;
|
|
606
|
|
607 if (argName)
|
|
608 setArg(argName, currentArgument);
|
|
609 else
|
|
610 lastArgumentSet = parseShortArgument(currentArgument);
|
|
611 }
|
|
612 }
|
|
613 }
|
|
614
|
|
615 if (aliases)
|
|
616 {
|
|
617 for (uint i = 0; i < aliases.length; i++)
|
|
618 {
|
|
619 bool foundOne = false;
|
|
620 char[][] currentValues;
|
|
621 for (uint j = 0; j < aliases[i].length; j++)
|
|
622 {
|
|
623 if (aliases[i][j] in _args)
|
|
624 {
|
|
625 foundOne = true;
|
|
626 currentValues ~= _args[aliases[i][j]];
|
|
627 }
|
|
628 }
|
|
629
|
|
630 if (foundOne)
|
|
631 {
|
|
632 for (uint j = 0; j < aliases[i].length; j++)
|
|
633 _args[aliases[i][j]] = currentValues;
|
|
634 }
|
|
635 }
|
|
636 }
|
|
637 }
|
|
638
|
|
639 /***********************************************************************
|
|
640
|
|
641 Constructor that supports all features
|
|
642
|
|
643 Params:
|
|
644 arguments = the list of arguments (usually from main)
|
|
645 implicitArgs = assigns values using these keys in order from the arguments array.
|
|
646 aliases = aliases specific arguments to each other to concat parameters. looks like aliases[0] = [ "alias1", "alias2", "alias3" ]; (which groups all these arguments together)
|
|
647
|
|
648 ***********************************************************************/
|
|
649
|
|
650 this(char[][] arguments, char[][] implicitArgs, char[][][] aliases)
|
|
651 {
|
|
652 _program = arguments[0];
|
|
653 this.parse(arguments, implicitArgs, aliases);
|
|
654 }
|
|
655
|
|
656 /***********************************************************************
|
|
657
|
|
658 Basic constructor which only deals with arguments
|
|
659
|
|
660 Params:
|
|
661 arguments = array usually from main()
|
|
662
|
|
663 ***********************************************************************/
|
|
664
|
|
665 this(char[][] arguments)
|
|
666 {
|
|
667 this(arguments, null, null);
|
|
668 }
|
|
669
|
|
670
|
|
671 /***********************************************************************
|
|
672
|
|
673 This constructor allows implicitArgs to be set as well
|
|
674
|
|
675 Params:
|
|
676 arguments = array usually from main()
|
|
677 implicitArgs = the implicit arguments
|
|
678
|
|
679 ***********************************************************************/
|
|
680
|
|
681 this(char[][] arguments, char[][] implicitArgs)
|
|
682 {
|
|
683 this(arguments, implicitArgs, null);
|
|
684 }
|
|
685
|
|
686 /***********************************************************************
|
|
687
|
|
688 This constructor allows aliases
|
|
689
|
|
690 Params:
|
|
691 arguments = array usually from main
|
|
692 aliases = the array of arguments to alias
|
|
693
|
|
694 ***********************************************************************/
|
|
695
|
|
696 this(char[][] arguments, char[][][] aliases)
|
|
697 {
|
|
698 this(arguments, null, aliases);
|
|
699 }
|
|
700 }
|
|
701
|
|
702 /+
|
|
703
|
|
704 TODO: Either rewrite this test to not use the Test class, or resolve ticket
|
|
705 749 to include Test in Tango.
|
|
706
|
|
707 version(UnitTest)
|
|
708 {
|
|
709 import tango.util.Test;
|
|
710 import tango.text.Util;
|
|
711 import tango.io.Stdout;
|
|
712
|
|
713 unittest
|
|
714 {
|
|
715 Test.Status parseTest(inout char[][] messages)
|
|
716 {
|
|
717 char[][] arguments = [ "ignoreprogramname", "--accumulate", "one", "-x", "on", "--accumulate:two", "-y", "off", "--accumulate=three", "-abc" ];
|
|
718 Arguments args = new Arguments(arguments);
|
|
719 if (args)
|
|
720 {
|
|
721 if (!("ignoreprogramname" in args))
|
|
722 {
|
|
723 if (join(args["accumulate"], " ") == "one two three")
|
|
724 {
|
|
725 if (args["x"][0] == "on")
|
|
726 {
|
|
727 if (("a" in args) && ("b" in args) && ("c" in args))
|
|
728 return Test.Status.Success;
|
|
729 }
|
|
730 }
|
|
731 }
|
|
732 }
|
|
733 return Test.Status.Failure;
|
|
734 }
|
|
735
|
|
736 Test.Status implicitParseTest(inout char[][] messages)
|
|
737 {
|
|
738 char[][] arguments = ["ignoreprogramname", "-r", "zero", "one two three", "four five", "-s", "six"];
|
|
739 char[][] implicitArgs = [ "first", "second" ];
|
|
740 Arguments args = new Arguments(arguments, implicitArgs);
|
|
741 if (args)
|
|
742 {
|
|
743 if (!("ignoreprogramname" in args))
|
|
744 {
|
|
745 if (("r" in args) && !args["r"])
|
|
746 {
|
|
747 if (args["first"][0] == "zero")
|
|
748 {
|
|
749 if (join(args["second"], " ") == "one two three four five")
|
|
750 {
|
|
751 if (args["second"] == ["one two three", "four five"])
|
|
752 {
|
|
753 if (args["s"][0] == "six")
|
|
754 return Test.Status.Success;
|
|
755 }
|
|
756 }
|
|
757 }
|
|
758 }
|
|
759 }
|
|
760 }
|
|
761 return Test.Status.Failure;
|
|
762 }
|
|
763
|
|
764 Test.Status aliasParseTest(inout char[][] messages)
|
|
765 {
|
|
766 char[][] arguments = [ "ignoreprogramname", "abc", "-d", "-e=eee", "--eff=f" ];
|
|
767 char[][][4] aliases;
|
|
768 aliases[0] = [ "lettera", "a" ];
|
|
769 aliases[1] = [ "letterbc", "c", "b" ];
|
|
770 aliases[2] = [ "letterd", "d" ];
|
|
771 aliases[3] = [ "lettere", "eff", "e" ];
|
|
772 Arguments args = new Arguments(arguments, aliases);
|
|
773 if (args)
|
|
774 {
|
|
775 if (!("ignoreprogramname" in args) && ("letterbc" in args) && ("a" in args) && ("b" in args) &&
|
|
776 ("c" in args) && ("d" in args) && ("eff" in args))
|
|
777 {
|
|
778 if (("lettera" in args) && ("letterbc" in args) && ("letterd" in args))
|
|
779 {
|
|
780 if (join(args["eff"], " ") == "f eee")
|
|
781 {
|
|
782 if (args["eff"] == ["f", "eee"])
|
|
783 return Test.Status.Success;
|
|
784 }
|
|
785 }
|
|
786 }
|
|
787 }
|
|
788 return Test.Status.Failure;
|
|
789 }
|
|
790
|
|
791 bool testRan = false;
|
|
792 bool testValidation(char[] arg)
|
|
793 {
|
|
794 bool rtn = true;
|
|
795 if (arg.length > 5)
|
|
796 rtn = false;
|
|
797 testRan = true;
|
|
798 return rtn;
|
|
799 }
|
|
800
|
|
801 Test.Status validationTest(inout char[][] messages)
|
|
802 {
|
|
803 char[][] arguments = [ "programname", "-a:on", "-abc:on", "--this=good", "-x:on" ];
|
|
804 Arguments args = new Arguments(arguments);
|
|
805 if (args)
|
|
806 {
|
|
807 args.addValidation("b", true, false);
|
|
808 args.addValidation("c", true, true);
|
|
809 args.addValidation("x", false, true);
|
|
810 args.addValidation("this", false, true);
|
|
811 args.addValidation("this", &testValidation);
|
|
812 try
|
|
813 {
|
|
814 args.validate();
|
|
815 if (testRan)
|
|
816 return Test.Status.Success;
|
|
817 }
|
|
818 catch (ArgumentException ex)
|
|
819 {
|
|
820 messages ~= Stdout.layout.convert("{}: {} - {} ({})", ex.name, ex.msg, ex.reason == ArgumentException.ExceptionReason.INVALID_PARAMETER ? "invalid parameter" : ex.reason == ArgumentException.ExceptionReason.MISSING_PARAMETER ? "missing parameter" : "missing argument", ex.parameter);
|
|
821 }
|
|
822 }
|
|
823 return Test.Status.Failure;
|
|
824 }
|
|
825
|
|
826 Test argTest = new Test("tetra.util.Arguments");
|
|
827 argTest["Normal"] = &parseTest;
|
|
828 argTest["Implicit"] = &implicitParseTest;
|
|
829 argTest["Alias"] = &aliasParseTest;
|
|
830 argTest["Validation"] = &validationTest;
|
|
831 argTest.run;
|
|
832 }
|
|
833 }
|
|
834 +/
|