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