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