| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * This library lets you define parsers for parsing raw command-line arguments | 6 * This library lets you define parsers for parsing raw command-line arguments |
| 7 * into a set of options and values using [GNU][] and [POSIX][] style options. | 7 * into a set of options and values using [GNU][] and [POSIX][] style options. |
| 8 * | 8 * |
| 9 * ## Defining options ## | 9 * ## Defining options ## |
| 10 * | 10 * |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 116 * you define: | 116 * you define: |
| 117 * | 117 * |
| 118 * parser.addFlag('verbose', abbr: 'v'); | 118 * parser.addFlag('verbose', abbr: 'v'); |
| 119 * parser.addFlag('french', abbr: 'f'); | 119 * parser.addFlag('french', abbr: 'f'); |
| 120 * parser.addFlag('iambic-pentameter', abbr: 'i'); | 120 * parser.addFlag('iambic-pentameter', abbr: 'i'); |
| 121 * | 121 * |
| 122 * Then all three flags could be set using: | 122 * Then all three flags could be set using: |
| 123 * | 123 * |
| 124 * -vfi | 124 * -vfi |
| 125 * | 125 * |
| 126 * By default, an option has only a single value, with later option values |
| 127 * overriding earlier ones; for example: |
| 128 * |
| 129 * var parser = new ArgParser(); |
| 130 * parser.addOption('mode'); |
| 131 * var results = parser.parse(['--mode', 'on', '--mode', 'off']); |
| 132 * print(results['mode']); // prints 'off' |
| 133 * |
| 134 * If you need multiple values, set the [allowMultiple] flag. In that |
| 135 * case the option can occur multiple times and when parsing arguments a |
| 136 * List of values will be returned: |
| 137 * |
| 138 * var parser = new ArgParser(); |
| 139 * parser.addOption('mode', allowMultiple: true); |
| 140 * var results = parser.parse(['--mode', 'on', '--mode', 'off']); |
| 141 * print(results['mode']); // prints '[on, off]' |
| 142 * |
| 126 * ## Usage ## | 143 * ## Usage ## |
| 127 * | 144 * |
| 128 * This library can also be used to automatically generate nice usage help | 145 * This library can also be used to automatically generate nice usage help |
| 129 * text like you get when you run a program with `--help`. To use this, you | 146 * text like you get when you run a program with `--help`. To use this, you |
| 130 * will also want to provide some help text when you create your options. To | 147 * will also want to provide some help text when you create your options. To |
| 131 * define help text for the entire option, do: | 148 * define help text for the entire option, do: |
| 132 * | 149 * |
| 133 * parser.addOption('mode', help: 'The compiler configuration', | 150 * parser.addOption('mode', help: 'The compiler configuration', |
| 134 * allowed: ['debug', 'release']); | 151 * allowed: ['debug', 'release']); |
| 135 * parser.addFlag('verbose', help: 'Show additional diagnostic info'); | 152 * parser.addFlag('verbose', help: 'Show additional diagnostic info'); |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 207 } | 224 } |
| 208 | 225 |
| 209 /** | 226 /** |
| 210 * Defines a value-taking option. Throws an [IllegalArgumentException] if: | 227 * Defines a value-taking option. Throws an [IllegalArgumentException] if: |
| 211 * | 228 * |
| 212 * * There is already an option with name [name]. | 229 * * There is already an option with name [name]. |
| 213 * * There is already an option using abbreviation [abbr]. | 230 * * There is already an option using abbreviation [abbr]. |
| 214 */ | 231 */ |
| 215 void addOption(String name, [String abbr, String help, List<String> allowed, | 232 void addOption(String name, [String abbr, String help, List<String> allowed, |
| 216 Map<String, String> allowedHelp, String defaultsTo, | 233 Map<String, String> allowedHelp, String defaultsTo, |
| 217 void callback(bool value)]) { | 234 void callback(bool value), bool allowMultiple = false]) { |
| 218 _addOption(name, abbr, help, allowed, allowedHelp, defaultsTo, | 235 _addOption(name, abbr, help, allowed, allowedHelp, defaultsTo, |
| 219 callback, isFlag: false); | 236 callback, isFlag: false, allowMultiple: allowMultiple); |
| 220 } | 237 } |
| 221 | 238 |
| 222 void _addOption(String name, String abbr, String help, List<String> allowed, | 239 void _addOption(String name, String abbr, String help, List<String> allowed, |
| 223 Map<String, String> allowedHelp, defaultsTo, | 240 Map<String, String> allowedHelp, defaultsTo, |
| 224 void callback(bool value), [bool isFlag, bool negatable = false]) { | 241 void callback(bool value), [bool isFlag, bool negatable = false, |
| 242 bool allowMultiple = false]) { |
| 225 // Make sure the name isn't in use. | 243 // Make sure the name isn't in use. |
| 226 if (_options.containsKey(name)) { | 244 if (_options.containsKey(name)) { |
| 227 throw new IllegalArgumentException('Duplicate option "$name".'); | 245 throw new IllegalArgumentException('Duplicate option "$name".'); |
| 228 } | 246 } |
| 229 | 247 |
| 230 // Make sure the abbreviation isn't too long or in use. | 248 // Make sure the abbreviation isn't too long or in use. |
| 231 if (abbr != null) { | 249 if (abbr != null) { |
| 232 if (abbr.length > 1) { | 250 if (abbr.length > 1) { |
| 233 throw new IllegalArgumentException( | 251 throw new IllegalArgumentException( |
| 234 'Abbreviation "$abbr" is longer than one character.'); | 252 'Abbreviation "$abbr" is longer than one character.'); |
| 235 } | 253 } |
| 236 | 254 |
| 237 var existing = _findByAbbr(abbr); | 255 var existing = _findByAbbr(abbr); |
| 238 if (existing != null) { | 256 if (existing != null) { |
| 239 throw new IllegalArgumentException( | 257 throw new IllegalArgumentException( |
| 240 'Abbreviation "$abbr" is already used by "${existing.name}".'); | 258 'Abbreviation "$abbr" is already used by "${existing.name}".'); |
| 241 } | 259 } |
| 242 } | 260 } |
| 243 | 261 |
| 244 _options[name] = new _Option(name, abbr, help, allowed, allowedHelp, | 262 _options[name] = new _Option(name, abbr, help, allowed, allowedHelp, |
| 245 defaultsTo, callback, isFlag: isFlag, negatable: negatable); | 263 defaultsTo, callback, isFlag: isFlag, negatable: negatable, |
| 264 allowMultiple: allowMultiple); |
| 246 _optionNames.add(name); | 265 _optionNames.add(name); |
| 247 } | 266 } |
| 248 | 267 |
| 249 /** | 268 /** |
| 250 * Parses [args], a list of command-line arguments, matches them against the | 269 * Parses [args], a list of command-line arguments, matches them against the |
| 251 * flags and options defined by this parser, and returns the result. | 270 * flags and options defined by this parser, and returns the result. |
| 252 */ | 271 */ |
| 253 ArgResults parse(List<String> args) { | 272 ArgResults parse(List<String> args) { |
| 254 _args = args; | 273 _args = args; |
| 255 _current = 0; | 274 _current = 0; |
| 256 var results = {}; | 275 var results = {}; |
| 257 | 276 |
| 258 // Initialize flags to their defaults. | 277 // Initialize flags to their defaults. |
| 259 _options.forEach((name, option) { | 278 _options.forEach((name, option) { |
| 260 results[name] = option.defaultValue; | 279 if (option.allowMultiple) { |
| 280 results[name] = []; |
| 281 } else { |
| 282 results[name] = option.defaultValue; |
| 283 } |
| 261 }); | 284 }); |
| 262 | 285 |
| 263 // Parse the args. | 286 // Parse the args. |
| 264 for (_current = 0; _current < args.length; _current++) { | 287 for (_current = 0; _current < args.length; _current++) { |
| 265 var arg = args[_current]; | 288 var arg = args[_current]; |
| 266 | 289 |
| 267 if (arg == '--') { | 290 if (arg == '--') { |
| 268 // Reached the argument terminator, so stop here. | 291 // Reached the argument terminator, so stop here. |
| 269 _current++; | 292 _current++; |
| 270 break; | 293 break; |
| 271 } | 294 } |
| 272 | 295 |
| 273 // Try to parse the current argument as an option. Note that the order | 296 // Try to parse the current argument as an option. Note that the order |
| 274 // here matters. | 297 // here matters. |
| 275 if (_parseSoloOption(results)) continue; | 298 if (_parseSoloOption(results)) continue; |
| 276 if (_parseAbbreviation(results)) continue; | 299 if (_parseAbbreviation(results)) continue; |
| 277 if (_parseLongOption(results)) continue; | 300 if (_parseLongOption(results)) continue; |
| 278 | 301 |
| 279 // If we got here, the argument doesn't look like an option, so stop. | 302 // If we got here, the argument doesn't look like an option, so stop. |
| 280 break; | 303 break; |
| 281 } | 304 } |
| 282 | 305 |
| 283 // Invoke the callbacks. | 306 // Set unspecified multivalued arguments to their default value, |
| 307 // if any, and invoke the callbacks. |
| 284 for (var name in _optionNames) { | 308 for (var name in _optionNames) { |
| 285 var option = _options[name]; | 309 var option = _options[name]; |
| 310 if (option.allowMultiple && |
| 311 results[name].length == 0 && |
| 312 option.defaultValue != null) { |
| 313 results[name].add(option.defaultValue); |
| 314 } |
| 286 if (option.callback != null) option.callback(results[name]); | 315 if (option.callback != null) option.callback(results[name]); |
| 287 } | 316 } |
| 288 | 317 |
| 289 // Add in the leftover arguments we didn't parse. | 318 // Add in the leftover arguments we didn't parse. |
| 290 return new ArgResults(results, | 319 return new ArgResults(results, |
| 291 _args.getRange(_current, _args.length - _current)); | 320 _args.getRange(_current, _args.length - _current)); |
| 292 } | 321 } |
| 293 | 322 |
| 294 /** | 323 /** |
| 295 * Generates a string displaying usage information for the defined options. | 324 * Generates a string displaying usage information for the defined options. |
| (...skipping 12 matching lines...) Expand all Loading... |
| 308 } | 337 } |
| 309 | 338 |
| 310 /** Validates and stores [value] as the value for [option]. */ | 339 /** Validates and stores [value] as the value for [option]. */ |
| 311 _setOption(Map results, _Option option, value) { | 340 _setOption(Map results, _Option option, value) { |
| 312 // See if it's one of the allowed values. | 341 // See if it's one of the allowed values. |
| 313 if (option.allowed != null) { | 342 if (option.allowed != null) { |
| 314 _validate(option.allowed.some((allow) => allow == value), | 343 _validate(option.allowed.some((allow) => allow == value), |
| 315 '"$value" is not an allowed value for option "${option.name}".'); | 344 '"$value" is not an allowed value for option "${option.name}".'); |
| 316 } | 345 } |
| 317 | 346 |
| 318 results[option.name] = value; | 347 if (option.allowMultiple) { |
| 348 results[option.name].add(value); |
| 349 } else { |
| 350 results[option.name] = value; |
| 351 } |
| 319 } | 352 } |
| 320 | 353 |
| 321 /** | 354 /** |
| 322 * Pulls the value for [option] from the next argument in [_args] (where the | 355 * Pulls the value for [option] from the next argument in [_args] (where the |
| 323 * current option is at index [_current]. Validates that there is a valid | 356 * current option is at index [_current]. Validates that there is a valid |
| 324 * value there. | 357 * value there. |
| 325 */ | 358 */ |
| 326 void _readNextArgAsValue(Map results, _Option option) { | 359 void _readNextArgAsValue(Map results, _Option option) { |
| 327 _current++; | 360 _current++; |
| 328 // Take the option argument from the next command line arg. | 361 // Take the option argument from the next command line arg. |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 490 class _Option { | 523 class _Option { |
| 491 final String name; | 524 final String name; |
| 492 final String abbreviation; | 525 final String abbreviation; |
| 493 final List allowed; | 526 final List allowed; |
| 494 final defaultValue; | 527 final defaultValue; |
| 495 final Function callback; | 528 final Function callback; |
| 496 final String help; | 529 final String help; |
| 497 final Map<String, String> allowedHelp; | 530 final Map<String, String> allowedHelp; |
| 498 final bool isFlag; | 531 final bool isFlag; |
| 499 final bool negatable; | 532 final bool negatable; |
| 533 final bool allowMultiple; |
| 500 | 534 |
| 501 _Option(this.name, this.abbreviation, this.help, this.allowed, | 535 _Option(this.name, this.abbreviation, this.help, this.allowed, |
| 502 this.allowedHelp, this.defaultValue, this.callback, [this.isFlag, | 536 this.allowedHelp, this.defaultValue, this.callback, [this.isFlag, |
| 503 this.negatable]); | 537 this.negatable, this.allowMultiple = false]); |
| 504 } | 538 } |
| 505 | 539 |
| 506 /** | 540 /** |
| 507 * Takes an [ArgParser] and generates a string of usage (i.e. help) text for its | 541 * Takes an [ArgParser] and generates a string of usage (i.e. help) text for its |
| 508 * defined options. Internally, it works like a tabular printer. The output is | 542 * defined options. Internally, it works like a tabular printer. The output is |
| 509 * divided into three horizontal columns, like so: | 543 * divided into three horizontal columns, like so: |
| 510 * | 544 * |
| 511 * -h, --help Prints the usage information | 545 * -h, --help Prints the usage information |
| 512 * | | | | | 546 * | | | | |
| 513 * | 547 * |
| (...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 715 allowedBuffer.add(allowed); | 749 allowedBuffer.add(allowed); |
| 716 if (allowed == option.defaultValue) { | 750 if (allowed == option.defaultValue) { |
| 717 allowedBuffer.add(' (default)'); | 751 allowedBuffer.add(' (default)'); |
| 718 } | 752 } |
| 719 first = false; | 753 first = false; |
| 720 } | 754 } |
| 721 allowedBuffer.add(']'); | 755 allowedBuffer.add(']'); |
| 722 return allowedBuffer.toString(); | 756 return allowedBuffer.toString(); |
| 723 } | 757 } |
| 724 } | 758 } |
| OLD | NEW |