OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2014, 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 part of protoc; |
| 6 |
| 7 /// Helper function implementing a generic option parser that reads |
| 8 /// `request.parameters` and treats each token as either a flag ("name") or a |
| 9 /// key-value pair ("name=value"). For each option "name", it looks up whether a |
| 10 /// [SingleOptionParser] exists in [parsers] and delegates the actual parsing of |
| 11 /// the option to it. Returns `true` if no errors were reported. |
| 12 bool genericOptionsParser( |
| 13 CodeGeneratorRequest request, CodeGeneratorResponse response, |
| 14 Map<String, SingleOptionParser> parsers) { |
| 15 var parameter = request.parameter != null ? request.parameter : ''; |
| 16 var options = parameter.trim().split(','); |
| 17 var map = <String, String>{}; |
| 18 var errors = []; |
| 19 |
| 20 for (var option in options) { |
| 21 option = option.trim(); |
| 22 if (option.isEmpty) continue; |
| 23 var reportError = (details) { |
| 24 errors.add('Error found trying to parse the option: $option.\n$details'); |
| 25 }; |
| 26 |
| 27 var nameValue = option.split('='); |
| 28 if (nameValue.length != 1 && nameValue.length != 2) { |
| 29 reportError('Options should be a single token, or a name=value pair'); |
| 30 continue; |
| 31 } |
| 32 var name = nameValue[0].trim(); |
| 33 var parser = parsers[name]; |
| 34 if (parser == null) { |
| 35 reportError('Unknown option ($name).'); |
| 36 continue; |
| 37 } |
| 38 |
| 39 var value = nameValue.length > 1 ? nameValue[1].trim() : null; |
| 40 parser.parse(name, value, reportError); |
| 41 } |
| 42 |
| 43 if (errors.length == 0) return true; |
| 44 |
| 45 response.error = errors.join('\n'); |
| 46 return false; |
| 47 } |
| 48 |
| 49 /// Options expected by the protoc code generation compiler. |
| 50 class GenerationOptions { |
| 51 /// Maps a fully qualified field name, to the desired name we wish to |
| 52 /// generate. For example `MyMessage.has_field` to `HasFld`. |
| 53 final Map<String, String> fieldNameOverrides; |
| 54 |
| 55 GenerationOptions(this.fieldNameOverrides); |
| 56 } |
| 57 |
| 58 /// A parser for a name-value pair option. Options parsed in |
| 59 /// [genericOptionsParser] delegate to instances of this class to |
| 60 /// parse the value of a specific option. |
| 61 abstract class SingleOptionParser { |
| 62 |
| 63 /// Parse the [name]=[value] value pair and report any errors to [onError]. If |
| 64 /// the option is a flag, [value] will be null. Note, [name] is commonly |
| 65 /// unused. It is provided because [SingleOptionParser] can be registered for |
| 66 /// multiple option names in [genericOptionsParser]. |
| 67 void parse(String name, String value, onError(String details)); |
| 68 } |
| 69 |
| 70 /// Parser used by the compiler, which supports the `field_name` option (see |
| 71 /// [FieldNameOptionParser]) and any additional option added in [parsers]. If |
| 72 /// [parsers] has a key for `field_name`, it will be ignored. |
| 73 GenerationOptions parseGenerationOptions( |
| 74 CodeGeneratorRequest request, CodeGeneratorResponse response, |
| 75 [Map<String, SingleOptionParser> parsers]) { |
| 76 var fieldNameOptionParser = new FieldNameOptionParser(); |
| 77 var map = {}; |
| 78 if (parsers != null) parsers.forEach((k, v) { map[k] = v; }); |
| 79 map['field_name'] = fieldNameOptionParser; |
| 80 if (genericOptionsParser(request, response, map)) { |
| 81 return new GenerationOptions(fieldNameOptionParser.mappings); |
| 82 } |
| 83 return null; |
| 84 } |
| 85 |
| 86 /// A [SingleOptionParser] to parse the `field_name` option. This option |
| 87 /// overrides the default name given to some fields that would otherwise collide |
| 88 /// with existing field names in Dart core objects or in [GeneratedMessage]. |
| 89 /// (see `README.md` for details). |
| 90 class FieldNameOptionParser implements SingleOptionParser { |
| 91 /// Maps a fully qualified field name, to the desired name we wish to |
| 92 /// generate. For example `MyMessage.has_field` to `HasFld`. |
| 93 final Map<String, String> mappings = {}; |
| 94 |
| 95 void parse(String name, String value, onError(String message)) { |
| 96 if (value == null) { |
| 97 onError('Invalid field_name option, expected a non-emtpy value.'); |
| 98 return; |
| 99 } |
| 100 |
| 101 List<String> fromTo = value.split('|'); |
| 102 if (fromTo.length != 2) { |
| 103 onError('Invalid field_name option, expected a single "|" separator.'); |
| 104 return; |
| 105 } |
| 106 |
| 107 var fromName = fromTo[0].trim(); |
| 108 var toName = fromTo[1].trim(); |
| 109 if (fromName.isEmpty || toName.isEmpty) { |
| 110 onError('Invalid field_name option, ' |
| 111 '"from" and "to" names should not be empty.'); |
| 112 return; |
| 113 } |
| 114 |
| 115 mappings['.$fromName'] = toName; |
| 116 } |
| 117 } |
OLD | NEW |