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