| 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, |
|---|
| 127 | | ex.reason == ArgumentException.ExceptionReason.INVALID_PARAMETER ? |
|---|
| 128 | | "invalid parameter" : ex.reason == ArgumentException.ExceptionReason.MISSING_PARAMETER ? |
|---|
| 129 | | "missing parameter" : "missing argument"); |
|---|
| 130 | | } |
|---|
| 131 | | } |
|---|
| 132 | | --- |
|---|
| 133 | | |
|---|
| 134 | | Syntax: |
|---|
| 135 | | --- |
|---|
| 136 | | Short Argument - -[x][:=]?[ parameter]... |
|---|
| 137 | | Long Argument - --[long][:=]?[ parameter]... |
|---|
| 138 | | Implicit Arguments - [implicitName]... Multiple implicit arguments are allowed. |
|---|
| 139 | | --- |
|---|
| 140 | | |
|---|
| 141 | | Usage: |
|---|
| 142 | | |
|---|
| 143 | | Short options can be grouped in a single dash. The following are equivalent. |
|---|
| 144 | | --- |
|---|
| 145 | | - "myprogram -a -b -c" |
|---|
| 146 | | - "myprogram -abc" |
|---|
| 147 | | --- |
|---|
| 148 | | |
|---|
| 149 | | Arguments can be passed with space, '=', or ':'. The following are equivalent. |
|---|
| 150 | | --- |
|---|
| 151 | | - "myprogram -c arg" |
|---|
| 152 | | - "myprogram -c=arg" |
|---|
| 153 | | - "myprogram -c:arg" |
|---|
| 154 | | --- |
|---|
| 155 | | |
|---|
| 156 | | As are these. |
|---|
| 157 | | --- |
|---|
| 158 | | - "myprogram --long arg" |
|---|
| 159 | | - "myprogram --long=arg" |
|---|
| 160 | | - "myprogram --long:arg" |
|---|
| 161 | | --- |
|---|
| 162 | | |
|---|
| 163 | | Arguments can contain either '=' or ':', but not both. For example. |
|---|
| 164 | | the following results in the argument 'long' being set to 'arg=other' |
|---|
| 165 | | and 'arg:other', respectively. |
|---|
| 166 | | --- |
|---|
| 167 | | - "myprogram --long:arg=other" |
|---|
| 168 | | - "myprogram --long=arg:other" |
|---|
| 169 | | --- |
|---|
| 170 | | |
|---|
| 171 | | Blank dashes are ignored. The following are all equivalent. |
|---|
| 172 | | --- |
|---|
| 173 | | - "myprogram -c -- -a" |
|---|
| 174 | | - "myprogram -c - -a" |
|---|
| 175 | | - "myprogram - - - -a -- -c" |
|---|
| 176 | | --- |
|---|
| 177 | | |
|---|
| 178 | | In the absence of implicit arguments, short options can be infered when |
|---|
| 179 | | they come first. Given no implicit arguments, the following are equivalent. |
|---|
| 180 | | --- |
|---|
| 181 | | - "myprogram abc" |
|---|
| 182 | | - "myprogram -abc" |
|---|
| 183 | | - "myprogram -a -b -c" |
|---|
| 184 | | --- |
|---|
| 185 | | |
|---|
| 186 | | Short options are case sensitive, while long options are not. The following |
|---|
| 187 | | are equivalent. |
|---|
| 188 | | --- |
|---|
| 189 | | - "myprogram -a -A -LONG" |
|---|
| 190 | | - "myprogram -a -A -Long" |
|---|
| 191 | | - "myprogram -a -A -long" |
|---|
| 192 | | --- |
|---|
| 193 | | |
|---|
| 194 | | In the event of multiple definitions of an argument, any parameters given |
|---|
| 195 | | will be concatenated. The following are equivalent. |
|---|
| 196 | | --- |
|---|
| 197 | | - "myprogram -a one two three" |
|---|
| 198 | | - "myprogram -a one -a two -a three" |
|---|
| 199 | | - "myprogram -a:one two -a=three" |
|---|
| 200 | | --- |
|---|
| 201 | | |
|---|
| 202 | | Multiple parameters can be iterated through using via the opIndex operator. |
|---|
| 203 | | For example, given: |
|---|
| 204 | | --- |
|---|
| 205 | | - "myprogram -collect one two three '4 5 6'" |
|---|
| 206 | | --- |
|---|
| 207 | | args["collect"] will return a char[][] array ["one", "two", "three", "4 5 6"]. |
|---|
| 208 | | |
|---|
| 209 | | Implicit arguments can be defined by passing in an implicit arguments array, |
|---|
| 210 | | which may look something like: ["first", "second"]. |
|---|
| 211 | | Given implicit arguments, any non-argument command line parameters will be |
|---|
| 212 | | automatically assigned to the implicit arguments, |
|---|
| 213 | | in the order they were given. |
|---|
| 214 | | For example, given the implicit arguments ["first", "second"] and command line: |
|---|
| 215 | | --- |
|---|
| 216 | | - "myprogram hello there bob" |
|---|
| 217 | | --- |
|---|
| 218 | | The argument 'first' will be assigned 'hello', and the argument 'second' will |
|---|
| 219 | | be assigned both 'there' and 'bob'. |
|---|
| 220 | | |
|---|
| 221 | | Any intervening arguments will end the assignment. For example, given: |
|---|
| 222 | | --- |
|---|
| 223 | | - "myprogram hello there bob -a how are you" |
|---|
| 224 | | --- |
|---|
| 225 | | 'first' is assigned 'hello', 'second' is assigned 'there' and 'bob', and 'a' |
|---|
| 226 | | is assigned 'how', 'are', and 'you'. |
|---|
| 227 | | |
|---|
| 228 | | Implicit arguments also allows programs to support non-option arguments, |
|---|
| 229 | | given implicit arguments ["actions"], and a command line such as: |
|---|
| 230 | | --- |
|---|
| 231 | | - "myprogram mop sweep get_coffee -time:now" |
|---|
| 232 | | --- |
|---|
| 233 | | args["actions"] will contain ["mop", "sweep", "get_coffee"], and |
|---|
| 234 | | args["time"] will contain "now". |
|---|
| 235 | | |
|---|
| 236 | | ***********************************************************************/ |
|---|
| | 47 | if (args["lights"] == "off") |
|---|
| | 48 | lights.enabled = false; // turn the lights off |
|---|
| | 49 | sleep(args.count("z")); // sleep for the indicated period |
|---|
| | 50 | } |
|---|
| | 51 | --- |
|---|
| | 52 | |
|---|
| | 53 | Terminology: |
|---|
| | 54 | --- |
|---|
| | 55 | - Short Argument: Single character arguments, potentially grouped under a |
|---|
| | 56 | single prefix. (-a, -abc) |
|---|
| | 57 | - Long Argument: Multiple character arguments, always contained under |
|---|
| | 58 | their own prefix. (--file, --help) |
|---|
| | 59 | - Parameter: A parameter to a particular argument, identified by a |
|---|
| | 60 | parameter delimiter or whitespace with no prefix (--arg=parameter, -arg parameter) |
|---|
| | 61 | - Implicit Parameter: A given parameter that doesn't match to a particular |
|---|
| | 62 | argument, accessible using the null index (args[null]). (file1.txt file2.txt) |
|---|
| | 63 | --- |
|---|
| | 64 | |
|---|
| | 65 | Behavior: |
|---|
| | 66 | |
|---|
| | 67 | Default: |
|---|
| | 68 | |
|---|
| | 69 | Arguments defaults to a basic parse behavior which provides for handling |
|---|
| | 70 | of most common argument strings. You can invoke this default parse behavior |
|---|
| | 71 | by simply passing the argument string to the class constructor. |
|---|
| | 72 | |
|---|
| | 73 | Short Arguments can be grouped under a single prefix. The following are |
|---|
| | 74 | equivalent. |
|---|
| | 75 | --- |
|---|
| | 76 | - "-a -b -c" |
|---|
| | 77 | - "-abc" |
|---|
| | 78 | --- |
|---|
| | 79 | |
|---|
| | 80 | Argument Parameters can be identified via being space-separated from their |
|---|
| | 81 | argument, or via either a '=' or a ':'. The following are equivalent. |
|---|
| | 82 | --- |
|---|
| | 83 | - "-c arg" |
|---|
| | 84 | - "-c=arg" |
|---|
| | 85 | - "-c:arg" |
|---|
| | 86 | --- |
|---|
| | 87 | |
|---|
| | 88 | Only the first found parameter will be skipped over as a delimiter. The |
|---|
| | 89 | following are equivalent. |
|---|
| | 90 | --- |
|---|
| | 91 | - "-c =blah" |
|---|
| | 92 | - "-c==blah" |
|---|
| | 93 | - "-c:=blah" |
|---|
| | 94 | --- |
|---|
| | 95 | |
|---|
| | 96 | Empty prefixes are ignored. The following are equivalent. |
|---|
| | 97 | --- |
|---|
| | 98 | - "-c -- -a" |
|---|
| | 99 | - "-c - -a" |
|---|
| | 100 | - "- - - - -a -- -c" |
|---|
| | 101 | --- |
|---|
| | 102 | |
|---|
| | 103 | Multiple parameters to a particular argument are appended to the array for |
|---|
| | 104 | that argument. The following are equivalent. |
|---|
| | 105 | --- |
|---|
| | 106 | - "-a one two three" |
|---|
| | 107 | - "-a one -a two -a three" |
|---|
| | 108 | - "-a:one two -a=three" |
|---|
| | 109 | --- |
|---|
| | 110 | |
|---|
| | 111 | Implicit parameters are assigned to the [null] index of the class. |
|---|
| | 112 | --- |
|---|
| | 113 | - "file1 file2 file3" |
|---|
| | 114 | --- |
|---|
| | 115 | |
|---|
| | 116 | Defined: |
|---|
| | 117 | |
|---|
| | 118 | Accepted arguments and the particular behavior to apply to them can be |
|---|
| | 119 | defined via the .define method. .define returns a Arguments.Definition, |
|---|
| | 120 | which can then be chained with additional methods for further definition. |
|---|
| | 121 | The prefixes for both short and long arguments can be overwritten or |
|---|
| | 122 | appended to, as can the parameter delimiters, by setting the .prefixLong, |
|---|
| | 123 | .prefixShort, and/or delimiters arrays as appropriate. |
|---|
| | 124 | |
|---|
| | 125 | Null prefixes can be used to specify 'word sentence' behavior. |
|---|
| | 126 | --- |
|---|
| | 127 | args.prefixLong = [null]; |
|---|
| | 128 | --- |
|---|
| | 129 | |
|---|
| | 130 | Will parse each of the items as if they were Arguments prefixed with "--" |
|---|
| | 131 | in the default behavior. |
|---|
| | 132 | --- |
|---|
| | 133 | myProgram one two three |
|---|
| | 134 | --- |
|---|
| | 135 | |
|---|
| | 136 | Null delimiters can be used to specify 'arguments smushed up with |
|---|
| | 137 | parameters' behavior. |
|---|
| | 138 | --- |
|---|
| | 139 | args.define("f").parameters.delimiters([null]); |
|---|
| | 140 | --- |
|---|
| | 141 | |
|---|
| | 142 | Will parse the following as args["f"] = ["file"]. |
|---|
| | 143 | --- |
|---|
| | 144 | myProgram -ffile |
|---|
| | 145 | --- |
|---|
| | 146 | |
|---|
| | 147 | You can also define argument aliases. |
|---|
| | 148 | --- |
|---|
| | 149 | args.define("a").aliases(["b"]); |
|---|
| | 150 | --- |
|---|
| | 151 | |
|---|
| | 152 | Given that, the following would be equivalent. |
|---|
| | 153 | --- |
|---|
| | 154 | - "-a" |
|---|
| | 155 | - "-b" |
|---|
| | 156 | --- |
|---|
| | 157 | |
|---|
| | 158 | You can also specify that an argument has default parameters. |
|---|
| | 159 | --- |
|---|
| | 160 | args.define("x").defaults(["hello"]); |
|---|
| | 161 | --- |
|---|
| | 162 | |
|---|
| | 163 | In which case, the following would be equivalent. |
|---|
| | 164 | --- |
|---|
| | 165 | - "-a -x hello" |
|---|
| | 166 | - "-a" |
|---|
| | 167 | --- |
|---|
| | 168 | |
|---|
| | 169 | Finally, you may also specify limiting behavior, that a particular argument |
|---|
| | 170 | is required, conflicts with another argument, requires another argument, |
|---|
| | 171 | that a callback should be called when encountering the argument, or that a |
|---|
| | 172 | particular validation routine should be called on all defined parameters |
|---|
| | 173 | for an argument. |
|---|
| | 174 | --- |
|---|
| | 175 | args.define("x").required; |
|---|
| | 176 | args.define("x").conflicts("b"); |
|---|
| | 177 | args.define("x").requires("a"); |
|---|
| | 178 | args.define("x").callback( ... ); |
|---|
| | 179 | args.define("x").validation( ... ); |
|---|
| | 180 | --- |
|---|
| | 181 | |
|---|
| | 182 | Note that requires and conflicts are order-sensitive, if you want, for |
|---|
| | 183 | example, "a" and "b" to be mutually exclusive, you would need to define |
|---|
| | 184 | that explicitly. |
|---|
| | 185 | --- |
|---|
| | 186 | args.define("a").conflicts("b"); |
|---|
| | 187 | args.define("b").conflicts("a"); |
|---|
| | 188 | --- |
|---|
| | 189 | |
|---|
| | 190 | Samples: |
|---|
| | 191 | |
|---|
| | 192 | Sample definitions (tar-like). |
|---|
| | 193 | --- |
|---|
| | 194 | args.prefixShort = [null]; |
|---|
| | 195 | args.define("f").parameters; |
|---|
| | 196 | args.define("v"); |
|---|
| | 197 | args.define("x"); |
|---|
| | 198 | args.define("z"); |
|---|
| | 199 | args.parse(["zxfv", "file1.tar", "file2.tar"]); |
|---|
| | 200 | --- |
|---|
| | 201 | |
|---|
| | 202 | Sample definitions (dsss-like). |
|---|
| | 203 | --- |
|---|
| | 204 | args.prefixLong = [null]; |
|---|
| | 205 | args.define("net").parameters(0,2); |
|---|
| | 206 | args.parse(["net", "install", "tango"]); |
|---|
| | 207 | --- |
|---|
| | 208 | |
|---|
| | 209 | Sample definitions (ls-like). |
|---|
| | 210 | --- |
|---|
| | 211 | args.define("a"); |
|---|
| | 212 | args.define("l"); |
|---|
| | 213 | args.parse(["-al", "blah.txt"]); |
|---|
| | 214 | args.parse(["blah.txt", "-al"]); |
|---|
| | 215 | --- |
|---|
| | 216 | |
|---|
| | 217 | Sample definitions (complex). |
|---|
| | 218 | --- |
|---|
| | 219 | args.define("x").defaults(["on"]).aliases(["X"]).conflicts("a").requires("b").required; |
|---|
| | 220 | args.define("C").callback(delegate void(char[] n, char[] p){ ... }); |
|---|
| | 221 | args.define("V").validation(delegate bool(char[][] p, out char[] ip){ ... }); |
|---|
| | 222 | --- |
|---|
| | 223 | |
|---|
| | 224 | Ideas: |
|---|
| | 225 | Some thoughts about future progression of the module. |
|---|
| | 226 | --- |
|---|
| | 227 | -Forcing arguments to lowercase. (define("x").lowercase, -X gives "x" in args) |
|---|
| | 228 | -User defined callback when encountering an undefined argument. |
|---|
| | 229 | -Standard help text generation |
|---|
| | 230 | --- |
|---|
| | 231 | *******************************************************************************/ |
|---|
| 240 | | /// Function used to validate multiple parameters at once. |
|---|
| 241 | | alias bool function(char[][] params, inout char[] invalidParam) validationFunctionMulti; |
|---|
| 242 | | /// Delegate used to validate multiple parameters at once. |
|---|
| 243 | | alias bool delegate(char[][] params, inout char[] invalidParam) validationDelegateMulti; |
|---|
| 244 | | /// Function used to validate single parameters at a time. |
|---|
| 245 | | alias bool function(char[] param) validationFunction; |
|---|
| 246 | | /// Delegate used to validate single parameters at a time. |
|---|
| 247 | | alias bool delegate(char[] param) validationDelegate; |
|---|
| | 235 | /// All Arguments exceptions are derived from this |
|---|
| | 236 | class ParseException : Exception |
|---|
| | 237 | { |
|---|
| | 238 | private char[] _argument; |
|---|
| | 239 | /// The name of the particular argument |
|---|
| | 240 | char[] argument() { return _argument; } |
|---|
| | 241 | this(char[] msg, char[] argument) |
|---|
| | 242 | { |
|---|
| | 243 | _argument = argument; |
|---|
| | 244 | super(msg); |
|---|
| | 245 | } |
|---|
| | 246 | } |
|---|
| | 247 | |
|---|
| | 248 | /// Thrown when a parameter for a given argument is determined to be invalid |
|---|
| | 249 | class InvalidParameterException : ParseException |
|---|
| | 250 | { |
|---|
| | 251 | private char[] _parameter; |
|---|
| | 252 | /// The invalid parameter |
|---|
| | 253 | char[] parameter() { return _parameter; } |
|---|
| | 254 | this(char[] msg, char[] argument, char[] parameter) |
|---|
| | 255 | { |
|---|
| | 256 | _parameter = parameter; |
|---|
| | 257 | super(msg, argument); |
|---|
| | 258 | } |
|---|
| | 259 | } |
|---|
| | 260 | |
|---|
| | 261 | /// Thrown when the minimum defined number of parameters for a given |
|---|
| | 262 | /// argument are not discovered |
|---|
| | 263 | class InsufficientParameterException : ParseException |
|---|
| | 264 | { |
|---|
| | 265 | private int _count; |
|---|
| | 266 | private int _expected; |
|---|
| | 267 | /// The discovered parameter count |
|---|
| | 268 | int count() { return _count; } |
|---|
| | 269 | /// The expected parameter count |
|---|
| | 270 | int expected() { return _expected; } |
|---|
| | 271 | this(char[] msg, char[] argument, int count, int expected) |
|---|
| | 272 | { |
|---|
| | 273 | _count = count; |
|---|
| | 274 | _expected = expected; |
|---|
| | 275 | super(msg, argument); |
|---|
| | 276 | } |
|---|
| | 277 | } |
|---|
| | 278 | |
|---|
| | 279 | /// Thrown when a previously discovered argument was defined to conflict |
|---|
| | 280 | /// with this one |
|---|
| | 281 | class ConflictingArgumentException : ParseException |
|---|
| | 282 | { |
|---|
| | 283 | private char[] _conflict; |
|---|
| | 284 | /// The name of the conflicting argument |
|---|
| | 285 | char[] conflict() { return _conflict; } |
|---|
| | 286 | this(char[] msg, char[] argument, char[] conflict) |
|---|
| | 287 | { |
|---|
| | 288 | _conflict = conflict; |
|---|
| | 289 | super(msg, argument); |
|---|
| | 290 | } |
|---|
| | 291 | } |
|---|
| | 292 | |
|---|
| | 293 | /// Thrown when an argument requires a previously defined argument which |
|---|
| | 294 | /// was not discovered |
|---|
| | 295 | class MissingPrerequisiteException : ParseException |
|---|
| | 296 | { |
|---|
| | 297 | private char[] _requirement; |
|---|
| | 298 | /// The name of the missing prerequisite argument |
|---|
| | 299 | char[] requirement() { return _requirement; } |
|---|
| | 300 | this(char[] msg, char[] argument, char[] requirement) |
|---|
| | 301 | { |
|---|
| | 302 | _requirement = requirement; |
|---|
| | 303 | super(msg, argument); |
|---|
| | 304 | } |
|---|
| | 305 | } |
|---|
| | 306 | |
|---|
| | 307 | /// Thrown when an argument is defined as being required but is not |
|---|
| | 308 | /// discovered |
|---|
| | 309 | class MissingArgumentException : ParseException |
|---|
| | 310 | { |
|---|
| | 311 | this(char[] msg, char[] argument) |
|---|
| | 312 | { |
|---|
| | 313 | super(msg, argument); |
|---|
| | 314 | } |
|---|
| | 315 | } |
|---|
| 253 | | validationFunction[] validF; |
|---|
| 254 | | validationDelegate[] validD; |
|---|
| 255 | | validationFunctionMulti[] validFM; |
|---|
| 256 | | validationDelegateMulti[] validDM; |
|---|
| 257 | | bool required; |
|---|
| 258 | | bool paramRequired; |
|---|
| 259 | | } |
|---|
| 260 | | private validation[char[]] _validations; |
|---|
| 261 | | |
|---|
| 262 | | private char[] parseLongArgument(char[] arg) |
|---|
| 263 | | { |
|---|
| 264 | | char[] rtn; |
|---|
| 265 | | |
|---|
| 266 | | int locate(char[] arg, char c) { |
|---|
| 267 | | foreach (i, a; arg) |
|---|
| 268 | | if (a is c) |
|---|
| 269 | | return i; |
|---|
| 270 | | return arg.length; |
|---|
| 271 | | } |
|---|
| 272 | | |
|---|
| 273 | | |
|---|
| 274 | | if (arg) |
|---|
| 275 | | { |
|---|
| 276 | | int equalDelim = locate(arg, '='); |
|---|
| 277 | | int colonDelim = locate(arg, ':'); |
|---|
| 278 | | int paramPos = ((equalDelim != arg.length) && |
|---|
| 279 | | (colonDelim != arg.length)) ? |
|---|
| 280 | | (equalDelim < colonDelim ? equalDelim : colonDelim) : |
|---|
| 281 | | (equalDelim != arg.length) ? equalDelim : colonDelim; |
|---|
| 282 | | if (paramPos != arg.length) |
|---|
| 283 | | { |
|---|
| 284 | | char[] argName = arg[0 .. paramPos]; |
|---|
| 285 | | char[] value = arg[(paramPos + 1) .. arg.length]; |
|---|
| 286 | | setArg(argName, value); |
|---|
| 287 | | rtn = argName; |
|---|
| 288 | | } |
|---|
| 289 | | else |
|---|
| 290 | | { |
|---|
| 291 | | setArg(arg, null); |
|---|
| 292 | | rtn = arg; |
|---|
| 293 | | } |
|---|
| 294 | | } |
|---|
| 295 | | return rtn; |
|---|
| 296 | | } |
|---|
| 297 | | |
|---|
| 298 | | private void setArg(char[] arg, char[] value) |
|---|
| 299 | | { |
|---|
| 300 | | if (arg) |
|---|
| 301 | | { |
|---|
| 302 | | if ((arg in _args) || (value !is null)) |
|---|
| 303 | | _args[arg] ~= value; |
|---|
| 304 | | else |
|---|
| 305 | | _args[arg] = null; |
|---|
| 306 | | } |
|---|
| 307 | | } |
|---|
| 308 | | |
|---|
| 309 | | private char[] parseShortArgument(char[] arg) |
|---|
| 310 | | { |
|---|
| 311 | | char[] rtn; |
|---|
| 312 | | |
|---|
| 313 | | if (arg) |
|---|
| 314 | | { |
|---|
| 315 | | char[] argName; |
|---|
| 316 | | uint i; |
|---|
| 317 | | for (i = 0; i < arg.length; i++) |
|---|
| 318 | | { |
|---|
| 319 | | if (arg[i] != '=' && arg[i] != ':') |
|---|
| | 345 | private char[] _name; |
|---|
| | 346 | /// The name of the particular argument. |
|---|
| | 347 | char[] name() { return _name; } |
|---|
| | 348 | private int _parameterMin; |
|---|
| | 349 | /// The defined minumum number of parameters this argument requires. |
|---|
| | 350 | int parameterMin() { return _parameterMin; } |
|---|
| | 351 | private int _parameterMax; |
|---|
| | 352 | /// The defined maximum number of parameters this argument will consume. |
|---|
| | 353 | int parameterMax() { return _parameterMax; } |
|---|
| | 354 | private bool _isRequired; |
|---|
| | 355 | /// Whether this argument is required. |
|---|
| | 356 | bool isRequired() { return _isRequired; } |
|---|
| | 357 | private char[][] _definedDelimiters; |
|---|
| | 358 | /// A set of defined parameter delimiters for this argument. |
|---|
| | 359 | char[][] definedDelimiters() { return _definedDelimiters; } |
|---|
| | 360 | private char[][] _definedAliases; |
|---|
| | 361 | /// A set of defined aliases for this argument. |
|---|
| | 362 | char[][] definedAliases() { return _definedAliases; } |
|---|
| | 363 | private char[][] _definedConflicts; |
|---|
| | 364 | /// A set of defined conflicting arguments to this argument. Note that |
|---|
| | 365 | /// this is order-sensitive. |
|---|
| | 366 | char[][] definedConflicts() { return _definedConflicts; } |
|---|
| | 367 | private char[][] _definedPrerequisites; |
|---|
| | 368 | /// A set of defined required arguments to this argument. Note that |
|---|
| | 369 | /// they are also order-sensitive. |
|---|
| | 370 | char[][] definedPrerequisites() { return _definedPrerequisites; } |
|---|
| | 371 | private argumentCallback[] _callbacks; |
|---|
| | 372 | /// A set of defined callback functions to be called when this argument |
|---|
| | 373 | /// is discovered. |
|---|
| | 374 | argumentCallback[] callbacks() { return _callbacks; } |
|---|
| | 375 | private argumentValidation[] _validations; |
|---|
| | 376 | /// A set of defined validation functions to be called on the |
|---|
| | 377 | /// parameters for this argument. |
|---|
| | 378 | argumentValidation[] validations() { return _validations; } |
|---|
| | 379 | private char[][] _defaultParameters; |
|---|
| | 380 | /// A set of default parameters that will represent this argument if |
|---|
| | 381 | /// it is not discovered during parsing. |
|---|
| | 382 | char[][] defaultParameters() { return _defaultParameters; } |
|---|
| | 383 | |
|---|
| | 384 | /// Defines both a minumum and maximum number of parameters that this |
|---|
| | 385 | /// argument will take. Set max to -1 for unlimited. |
|---|
| | 386 | Definition parameters(int min, int max) |
|---|
| | 387 | { |
|---|
| | 388 | this._parameterMin = min; |
|---|
| | 389 | this._parameterMax = max; |
|---|
| | 390 | return this; |
|---|
| | 391 | } |
|---|
| | 392 | |
|---|
| | 393 | /// Defines a definite number of parameters that this argument must |
|---|
| | 394 | /// take, implies both min and max of the given requirement. |
|---|
| | 395 | Definition parameters(int req) |
|---|
| | 396 | { |
|---|
| | 397 | this._parameterMin = req; |
|---|
| | 398 | this._parameterMax = req; |
|---|
| | 399 | return this; |
|---|
| | 400 | } |
|---|
| | 401 | |
|---|
| | 402 | // Defines that this argument will consume any and all parameters |
|---|
| | 403 | /// following it that don't belong to another argument. |
|---|
| | 404 | Definition parameters() |
|---|
| | 405 | { |
|---|
| | 406 | this._parameterMin = 0; |
|---|
| | 407 | this._parameterMax = -1; |
|---|
| | 408 | return this; |
|---|
| | 409 | } |
|---|
| | 410 | |
|---|
| | 411 | /// Sets the default parameters that will be assigned to this argument |
|---|
| | 412 | /// if it is not discovered during parsing. |
|---|
| | 413 | Definition defaults(char[][] defaultParameters) |
|---|
| | 414 | { |
|---|
| | 415 | this._defaultParameters = defaultParameters; |
|---|
| | 416 | return this; |
|---|
| | 417 | } |
|---|
| | 418 | |
|---|
| | 419 | /// Sets the set of valid delimiters for this argument. Set to [null] |
|---|
| | 420 | /// for '-ffile' behavior. Supercedes any defined Arguments.delimiters, |
|---|
| | 421 | /// for this argument only. |
|---|
| | 422 | Definition delimiters(char[][] delimiters) |
|---|
| | 423 | { |
|---|
| | 424 | this._definedDelimiters ~= delimiters; |
|---|
| | 425 | return this; |
|---|
| | 426 | } |
|---|
| | 427 | |
|---|
| | 428 | /// Defines that this argument must be discovered during parsing. |
|---|
| | 429 | Definition required() |
|---|
| | 430 | { |
|---|
| | 431 | this._isRequired = true; |
|---|
| | 432 | return this; |
|---|
| | 433 | } |
|---|
| | 434 | |
|---|
| | 435 | /// Defines a set of aliases which will also correspond to this argument. |
|---|
| | 436 | /// Note that only the root defined argument is accessible via the |
|---|
| | 437 | /// Arguments index[] following parse. |
|---|
| | 438 | Definition aliases(char[][] aliases) |
|---|
| | 439 | { |
|---|
| | 440 | this._definedAliases ~= aliases; |
|---|
| | 441 | foreach(char[] thisAlias; aliases) |
|---|
| | 442 | _aliases[thisAlias] = this.name; |
|---|
| | 443 | return this; |
|---|
| | 444 | } |
|---|
| | 445 | |
|---|
| | 446 | /// Defines a set of arguments which, if found before this one is |
|---|
| | 447 | /// discovered, will conflict with this one. Note that for mutually |
|---|
| | 448 | /// exclusive conflicts, you need to declare both directions. |
|---|
| | 449 | Definition conflicts(char[][] conflictingArguments) |
|---|
| | 450 | { |
|---|
| | 451 | this._definedConflicts ~= conflictingArguments; |
|---|
| | 452 | return this; |
|---|
| | 453 | } |
|---|
| | 454 | |
|---|
| | 455 | /// Defines a set of arguments which must be discovered before this |
|---|
| | 456 | /// one is. |
|---|
| | 457 | Definition prerequisites(char[][] requiredArguments) |
|---|
| | 458 | { |
|---|
| | 459 | this._definedPrerequisites ~= requiredArguments; |
|---|
| | 460 | return this; |
|---|
| | 461 | } |
|---|
| | 462 | |
|---|
| | 463 | /// Defines a callback function that will be called when this argument |
|---|
| | 464 | /// is discovered. |
|---|
| | 465 | Definition callback(argumentCallback cb) |
|---|
| | 466 | { |
|---|
| | 467 | this._callbacks ~= cb; |
|---|
| | 468 | return this; |
|---|
| | 469 | } |
|---|
| | 470 | |
|---|
| | 471 | /// Defines a validation function which will be called on any and all |
|---|
| | 472 | /// found parameters for this argument. |
|---|
| | 473 | Definition validation(argumentValidation av) |
|---|
| | 474 | { |
|---|
| | 475 | this._validations ~= av; |
|---|
| | 476 | return this; |
|---|
| | 477 | } |
|---|
| | 478 | |
|---|
| | 479 | /*********************************************************************** |
|---|
| | 480 | Constructor |
|---|
| | 481 | Params: |
|---|
| | 482 | name = name of this argument. |
|---|
| | 483 | ***********************************************************************/ |
|---|
| | 484 | this(char[] name) |
|---|
| | 485 | { |
|---|
| | 486 | this._name = name; |
|---|
| | 487 | } |
|---|
| | 488 | } |
|---|
| | 489 | /// A set of defined argument Definitions, indexed by their respective name. |
|---|
| | 490 | Definition[char[]] definitions; |
|---|
| | 491 | |
|---|
| | 492 | /// The set of prefixes which define a short argument. |
|---|
| | 493 | char[][] prefixShort = ["-"]; |
|---|
| | 494 | /// The set of prefixes which define a long argument. |
|---|
| | 495 | char[][] prefixLong = ["--"]; |
|---|
| | 496 | /// The set of delimiters which identify a parameter to an argument. |
|---|
| | 497 | char[][] delimiters = [":", "="]; |
|---|
| | 498 | |
|---|
| | 499 | private char[][]* _prefixCompare(char[] candidate, char[][][] prefixes, out char[]* prefix) |
|---|
| | 500 | { // returns the prefix array that contains the longest prefix match for our |
|---|
| | 501 | // candidate, and sets out var to the matching prefix. |
|---|
| | 502 | char[][]* rtn = null; |
|---|
| | 503 | char[]* matchingPrefix = null; |
|---|
| | 504 | for (uint p = 0; p < prefixes.length; p++) |
|---|
| | 505 | { |
|---|
| | 506 | for (uint i = 0; i < prefixes[p].length; i++) |
|---|
| | 507 | { |
|---|
| | 508 | if (candidate.length >= prefixes[p][i].length) |
|---|
| 321 | | argName = arg[i .. i + 1]; |
|---|
| 322 | | setArg(argName, null); |
|---|
| 323 | | } |
|---|
| 324 | | else if (((arg.length) - i) > 1) |
|---|
| 325 | | { |
|---|
| 326 | | setArg(argName, arg[i+1 .. arg.length]); |
|---|
| 327 | | break; |
|---|
| 328 | | } |
|---|
| 329 | | } |
|---|
| 330 | | rtn = argName; |
|---|
| 331 | | } |
|---|
| 332 | | return rtn; |
|---|
| 333 | | } |
|---|
| 334 | | |
|---|
| 335 | | /*********************************************************************** |
|---|
| 336 | | |
|---|
| 337 | | Allows external argument assignment, works the same as command line |
|---|
| 338 | | in that it appends to any values already assigned to the given key. |
|---|
| 339 | | |
|---|
| 340 | | Params: |
|---|
| 341 | | value = assigned value |
|---|
| 342 | | key = key to assign to |
|---|
| 343 | | |
|---|
| 344 | | ***********************************************************************/ |
|---|
| 345 | | |
|---|
| 346 | | void opIndexAssign(char[] value, char[] key) |
|---|
| 347 | | { |
|---|
| 348 | | setArg(key, value); |
|---|
| 349 | | } |
|---|
| 350 | | |
|---|
| 351 | | /*********************************************************************** |
|---|
| 352 | | |
|---|
| 353 | | Allows removal of keys from the arguments. Useful if you want to replace |
|---|
| 354 | | values for a given key rather than to append to them. |
|---|
| 355 | | |
|---|
| 356 | | Params: |
|---|
| 357 | | key = key to remove values from. |
|---|
| 358 | | |
|---|
| 359 | | ***********************************************************************/ |
|---|
| 360 | | |
|---|
| 361 | | void remove(char[] key) |
|---|
| 362 | | { |
|---|
| 363 | | _args[key] = null; |
|---|
| 364 | | } |
|---|
| 365 | | |
|---|
| 366 | | /*********************************************************************** |
|---|
| 367 | | |
|---|
| 368 | | Directly access an argument's parameters via opIndex operator as an |
|---|
| 369 | | array. |
|---|
| 370 | | This is to cover something like: param1 "parm with space" param2 |
|---|
| 371 | | |
|---|
| 372 | | ***********************************************************************/ |
|---|
| 373 | | |
|---|
| 374 | | char[][] opIndex(char[] key) |
|---|
| 375 | | { |
|---|
| 376 | | char[][] rtn = null; |
|---|
| 377 | | if (key && (key in _args)) |
|---|
| 378 | | rtn = _args[key]; |
|---|
| 379 | | return rtn; |
|---|
| 380 | | } |
|---|
| 381 | | |
|---|
| 382 | | /*********************************************************************** |
|---|
| 383 | | |
|---|
| 384 | | Operator is used to check if the argument exists |
|---|
| 385 | | |
|---|
| 386 | | ***********************************************************************/ |
|---|
| 387 | | |
|---|
| 388 | | bool opIn_r(char[] key) |
|---|
| 389 | | { |
|---|
| 390 | | bool rtn = false; |
|---|
| 391 | | if (key) |
|---|
| 392 | | rtn = (key in _args) !is null; |
|---|
| 393 | | return rtn; |
|---|
| 394 | | } |
|---|
| 395 | | |
|---|
| 396 | | /*********************************************************************** |
|---|
| 397 | | |
|---|
| 398 | | Adds a validation to the arguments |
|---|
| 399 | | |
|---|
| 400 | | Params: |
|---|
| 401 | | argument = the argument name |
|---|
| 402 | | required = specifies if this argument is required |
|---|
| 403 | | paramRequired = specifies if this argument requires a parameter |
|---|
| 404 | | |
|---|
| 405 | | ***********************************************************************/ |
|---|
| 406 | | |
|---|
| 407 | | void addValidation(char[] argument, bool required, bool paramRequired) |
|---|
| 408 | | { |
|---|
| 409 | | if (argument) |
|---|
| 410 | | { |
|---|
| 411 | | validation* val = getValidation(argument); |
|---|
| 412 | | if (val !is null) |
|---|
| 413 | | { |
|---|
| 414 | | val.required = required; |
|---|
| 415 | | val.paramRequired = paramRequired; |
|---|
| 416 | | } |
|---|
| 417 | | } |
|---|
| 418 | | } |
|---|
| 419 | | |
|---|
| 420 | | /*********************************************************************** |
|---|
| 421 | | |
|---|
| 422 | | Adds a validation to the arguments |
|---|
| 423 | | |
|---|
| 424 | | Params: |
|---|
| 425 | | argument = the argument name |
|---|
| 426 | | validF = a validation function for single parameters |
|---|
| 427 | | |
|---|
| 428 | | ***********************************************************************/ |
|---|
| 429 | | |
|---|
| 430 | | void addValidation(char[] argument, validationFunction validF) |
|---|
| 431 | | { |
|---|
| 432 | | if (argument && validF) |
|---|
| 433 | | { |
|---|
| 434 | | validation* val = getValidation(argument); |
|---|
| 435 | | if (val !is null) |
|---|
| 436 | | val.validF ~= validF; |
|---|
| 437 | | } |
|---|
| 438 | | } |
|---|
| 439 | | |
|---|
| 440 | | /*********************************************************************** |
|---|
| 441 | | |
|---|
| 442 | | Adds a validation to the arguments |
|---|
| 443 | | |
|---|
| 444 | | Params: |
|---|
| 445 | | argument = the argument name |
|---|
| 446 | | validD = a validation delegate for single parameters |
|---|
| 447 | | |
|---|
| 448 | | ***********************************************************************/ |
|---|
| 449 | | |
|---|
| 450 | | void addValidation(char[] argument, validationDelegate validD) |
|---|
| 451 | | { |
|---|
| 452 | | if (argument && validD) |
|---|
| 453 | | { |
|---|
| 454 | | validation* val = getValidation(argument); |
|---|
| 455 | | if (val !is null) |
|---|
| 456 | | val.validD ~= validD; |
|---|
| 457 | | } |
|---|
| 458 | | } |
|---|
| 459 | | |
|---|
| 460 | | /*********************************************************************** |
|---|
| 461 | | |
|---|
| 462 | | Adds a validation to the arguments |
|---|
| 463 | | |
|---|
| 464 | | Params: |
|---|
| 465 | | argument = the argument name |
|---|
| 466 | | validF = a validation function for multiple parameters |
|---|
| 467 | | |
|---|
| 468 | | ***********************************************************************/ |
|---|
| 469 | | |
|---|
| 470 | | void addValidation(char[] argument, validationFunctionMulti validFM) |
|---|
| 471 | | { |
|---|
| 472 | | if (argument && validFM) |
|---|
| 473 | | { |
|---|
| 474 | | validation* val = getValidation(argument); |
|---|
| 475 | | if (val !is null) |
|---|
| 476 | | val.validFM ~= validFM; |
|---|
| 477 | | } |
|---|
| 478 | | } |
|---|
| 479 | | |
|---|
| 480 | | /*********************************************************************** |
|---|
| 481 | | |
|---|
| 482 | | Adds a validation to the arguments |
|---|
| 483 | | |
|---|
| 484 | | Params: |
|---|
| 485 | | argument = the argument name |
|---|
| 486 | | validD = a validation delegate for multiple parameters |
|---|
| 487 | | |
|---|
| 488 | | ***********************************************************************/ |
|---|
| 489 | | |
|---|
| 490 | | void addValidation(char[] argument, validationDelegateMulti validDM) |
|---|
| 491 | | { |
|---|
| 492 | | if (argument && validDM) |
|---|
| 493 | | { |
|---|
| 494 | | validation* val = getValidation(argument); |
|---|
| 495 | | if (val !is null) |
|---|
| 496 | | val.validDM ~= validDM; |
|---|
| 497 | | } |
|---|
| 498 | | } |
|---|
| 499 | | |
|---|
| 500 | | private validation* getValidation(char[] argument) |
|---|
| 501 | | { |
|---|
| 502 | | validation* rtn = null; |
|---|
| 503 | | if (!(argument in _validations)) |
|---|
| 504 | | { |
|---|
| 505 | | validation newValidation; |
|---|
| 506 | | _validations[argument] = newValidation; |
|---|
| 507 | | } |
|---|
| 508 | | if (argument in _validations) |
|---|
| 509 | | rtn = &(_validations[argument]); |
|---|
| 510 | | return rtn; |
|---|
| 511 | | } |
|---|
| 512 | | |
|---|
| 513 | | /*********************************************************************** |
|---|
| 514 | | |
|---|
| 515 | | Validates the parsed arguments. |
|---|
| 516 | | |
|---|
| 517 | | Throws ArgumentException if it finds something wrong. |
|---|
| 518 | | |
|---|
| 519 | | ***********************************************************************/ |
|---|
| 520 | | |
|---|
| 521 | | void validate() |
|---|
| 522 | | { |
|---|
| 523 | | foreach(char[] argument, validation val; _validations) |
|---|
| 524 | | { |
|---|
| 525 | | if (val.required && !(argument in _args)) |
|---|
| 526 | | throw new ArgumentException("Argument required.", argument, null, ArgumentException.ExceptionReason.MISSING_ARGUMENT); |
|---|
| 527 | | |
|---|
| 528 | | if (val.paramRequired && (argument in _args) && (_args[argument].length == 0)) |
|---|
| 529 | | throw new ArgumentException("Parameter required.", argument, null, ArgumentException.ExceptionReason.MISSING_PARAMETER); |
|---|
| 530 | | |
|---|
| 531 | | if ((argument in _args) && (_args[argument].length > 0)) |
|---|
| 532 | | { |
|---|
| 533 | | char[] invalidParameter = null; |
|---|
| 534 | | foreach(validationFunctionMulti validFM; val.validFM) |
|---|
| 535 | | if (!validFM(_args[argument], invalidParameter)) |
|---|
| 536 | | break; |
|---|
| 537 | | if (invalidParameter is null) |
|---|
| 538 | | { |
|---|
| 539 | | foreach(validationDelegateMulti validDM; val.validDM) |
|---|
| 540 | | if (!validDM(_args[argument], invalidParameter)) |
|---|
| | 510 | if (prefixes[p][i] == candidate[0..(prefixes[p][i].length)]) |
|---|
| | 511 | { |
|---|
| | 512 | if ((matchingPrefix is null) || (prefixes[p][i].length > (*matchingPrefix).length)) |
|---|
| | 513 | { |
|---|
| | 514 | matchingPrefix = &prefixes[p][i]; |
|---|
| | 515 | rtn = &prefixes[p]; |
|---|