| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 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. |
| 4 |
| 5 library fasta.command_line; |
| 6 |
| 7 import 'errors.dart' show |
| 8 inputError, |
| 9 internalError; |
| 10 |
| 11 argumentError(String usage, String message) { |
| 12 if (usage != null) print(usage); |
| 13 inputError(null, null, message); |
| 14 } |
| 15 |
| 16 class ParsedArguments { |
| 17 final Map<String, dynamic> options = <String, dynamic>{}; |
| 18 final List<String> arguments = <String>[]; |
| 19 |
| 20 toString() => "ParsedArguments($options, $arguments)"; |
| 21 } |
| 22 |
| 23 class CommandLine { |
| 24 final Map<String, dynamic> options; |
| 25 |
| 26 final List<String> arguments; |
| 27 |
| 28 final String usage; |
| 29 |
| 30 CommandLine.parsed(ParsedArguments p, this.usage) |
| 31 : this.options = p.options, |
| 32 this.arguments = p.arguments { |
| 33 validate(); |
| 34 if (verbose) { |
| 35 print(p); |
| 36 } |
| 37 } |
| 38 |
| 39 CommandLine(List<String> arguments, |
| 40 {Map<String, dynamic> specification, String usage}) |
| 41 : this.parsed(parse(arguments, specification, usage), usage); |
| 42 |
| 43 bool get verbose { |
| 44 return options.containsKey("-v") || options.containsKey("--verbose"); |
| 45 } |
| 46 |
| 47 /// Override to validate arguments and options. |
| 48 void validate() { |
| 49 } |
| 50 |
| 51 /// Parses a list of command-line [arguments] into options and arguments. |
| 52 /// |
| 53 /// An /option/ is something that, normally, starts with `-` or `--` (one or |
| 54 /// two dashes). However, as a special case `/?` and `/h` are also recognized |
| 55 /// as options for increased compatibility with Windows. An option can have a |
| 56 /// value. |
| 57 /// |
| 58 /// An /argument/ is something that isn't an option, for example, a file name. |
| 59 /// |
| 60 /// The specification is a map of options to one of the type literals `Uri`, |
| 61 /// `int`, `bool`, or `String`, or a comma (`","`) that represents option |
| 62 /// values of type [Uri], [int], [bool], [String], or a comma-separated list |
| 63 /// of [String], respectively. |
| 64 /// |
| 65 /// If [arguments] contains `"--"`, anything before is parsed as options, and |
| 66 /// arguments; anything following is treated as arguments (even if starting |
| 67 /// with, for example, a `-`). |
| 68 /// |
| 69 /// Anything that looks like an option is assumed to be a `bool` option set |
| 70 /// to true, unless it's mentioned in [specification] in which case the |
| 71 /// option requires a value, either on the form `--option value` or |
| 72 /// `--option=value`. |
| 73 /// |
| 74 /// This method performs only a limited amount of validation, but if an error |
| 75 /// occurs, it will print [usage] along with a specific error message. |
| 76 static ParsedArguments parse(List<String> arguments, |
| 77 Map<String, dynamic> specification, String usage) { |
| 78 specification ??= const <String, dynamic>{}; |
| 79 ParsedArguments result = new ParsedArguments(); |
| 80 int index = arguments.indexOf("--"); |
| 81 Iterable<String> nonOptions = const <String>[]; |
| 82 Iterator<String> iterator = arguments.iterator; |
| 83 if (index != -1) { |
| 84 nonOptions = arguments.skip(index + 1); |
| 85 iterator = arguments.take(index).iterator; |
| 86 } |
| 87 while (iterator.moveNext()) { |
| 88 String argument = iterator.current; |
| 89 if (argument.startsWith("-")) { |
| 90 var valueSpecification = specification[argument]; |
| 91 String value; |
| 92 if (valueSpecification != null) { |
| 93 if (!iterator.moveNext()) { |
| 94 return argumentError(usage, "Expected value after '$argument'."); |
| 95 } |
| 96 value = iterator.current; |
| 97 } else { |
| 98 index = argument.indexOf("="); |
| 99 if (index != -1) { |
| 100 value = argument.substring(index + 1); |
| 101 argument = argument.substring(0, index); |
| 102 valueSpecification = specification[argument]; |
| 103 } |
| 104 } |
| 105 if (valueSpecification == null) { |
| 106 if (value != null) { |
| 107 return argumentError(usage, |
| 108 "Argument '$argument' doesn't take a value: '$value'."); |
| 109 } |
| 110 result.options[argument] = true; |
| 111 } else { |
| 112 if (valueSpecification is! String && valueSpecification is! Type) { |
| 113 return argumentError(usage, "Unrecognized type of value " |
| 114 "specification: ${valueSpecification.runtimeType}."); |
| 115 } |
| 116 switch ("$valueSpecification") { |
| 117 case ",": |
| 118 result.options.putIfAbsent(argument, () => <String>[]) |
| 119 .addAll(value.split(",")); |
| 120 break; |
| 121 |
| 122 case "int": |
| 123 case "bool": |
| 124 case "String": |
| 125 case "Uri": |
| 126 if (result.options.containsKey(argument)) { |
| 127 return argumentError(usage, "Multiple values for '$argument': " |
| 128 "'${result.options[argument]}' and '$value'."); |
| 129 } |
| 130 var parsedValue; |
| 131 if (valueSpecification == int) { |
| 132 parsedValue = int.parse(value, onError: (_) { |
| 133 return argumentError(usage, |
| 134 "Value for '$argument', '$value', isn't an int."); |
| 135 }); |
| 136 } else if (valueSpecification == bool) { |
| 137 if (value == "true" || value == "yes") { |
| 138 parsedValue = true; |
| 139 } else if (value == "false" || value == "no") { |
| 140 parsedValue = false; |
| 141 } else { |
| 142 return argumentError(usage, |
| 143 "Value for '$argument' is '$value', " |
| 144 "but expected one of: 'true', 'false', 'yes', or 'no'."); |
| 145 } |
| 146 } else if (valueSpecification == Uri) { |
| 147 parsedValue = Uri.base.resolve(value); |
| 148 } else if (valueSpecification == String) { |
| 149 parsedValue = value; |
| 150 } else if (valueSpecification is String) { |
| 151 return argumentError(usage, "Unrecognized value specification: " |
| 152 "'$valueSpecification', try using a type literal instead."); |
| 153 } else { |
| 154 // All possible cases should have been handled above. |
| 155 return internalError("assertion failure"); |
| 156 } |
| 157 result.options[argument] = parsedValue; |
| 158 break; |
| 159 |
| 160 default: |
| 161 return argumentError(usage, |
| 162 "Unrecognized value specification: '$valueSpecification'."); |
| 163 } |
| 164 } |
| 165 } else if (argument == "/?" || argument == "/h") { |
| 166 result.options[argument] = true; |
| 167 } else { |
| 168 result.arguments.add(argument); |
| 169 } |
| 170 } |
| 171 result.arguments.addAll(nonOptions); |
| 172 return result; |
| 173 } |
| 174 } |
| OLD | NEW |