1 // Copyright 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 package com.google.api.generator.gapic.protoparser;
16 
17 import com.google.common.annotations.VisibleForTesting;
18 import com.google.common.base.Strings;
19 import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest;
20 import java.util.Arrays;
21 import java.util.Optional;
22 
23 // Parses the arguments from the protoc plugin.
24 public class PluginArgumentParser {
25   private static final String COMMA = ",";
26   private static final String EQUALS = "=";
27 
28   // Synced to rules_java_gapic/java_gapic.bzl.
29   @VisibleForTesting static final String KEY_GRPC_SERVICE_CONFIG = "grpc-service-config";
30   @VisibleForTesting static final String KEY_GAPIC_CONFIG = "gapic-config";
31   @VisibleForTesting static final String KEY_METADATA = "metadata";
32   @VisibleForTesting static final String KEY_NUMERIC_ENUM = "rest-numeric-enums";
33   @VisibleForTesting static final String KEY_SERVICE_YAML_CONFIG = "api-service-config";
34   @VisibleForTesting static final String KEY_TRANSPORT = "transport";
35 
36   private static final String JSON_FILE_ENDING = "grpc_service_config.json";
37   private static final String GAPIC_YAML_FILE_ENDING = "gapic.yaml";
38   private static final String SERVICE_YAML_FILE_ENDING = ".yaml";
39 
parseJsonConfigPath(CodeGeneratorRequest request)40   static Optional<String> parseJsonConfigPath(CodeGeneratorRequest request) {
41     return parseJsonConfigPath(request.getParameter());
42   }
43 
parseGapicYamlConfigPath(CodeGeneratorRequest request)44   static Optional<String> parseGapicYamlConfigPath(CodeGeneratorRequest request) {
45     return parseGapicYamlConfigPath(request.getParameter());
46   }
47 
parseServiceYamlConfigPath(CodeGeneratorRequest request)48   static Optional<String> parseServiceYamlConfigPath(CodeGeneratorRequest request) {
49     return parseServiceYamlConfigPath(request.getParameter());
50   }
51 
parseTransport(CodeGeneratorRequest request)52   static Optional<String> parseTransport(CodeGeneratorRequest request) {
53     return parseConfigArgument(request.getParameter(), KEY_TRANSPORT);
54   }
55 
hasMetadataFlag(CodeGeneratorRequest request)56   static boolean hasMetadataFlag(CodeGeneratorRequest request) {
57     return hasFlag(request.getParameter(), KEY_METADATA);
58   }
59 
hasNumericEnumFlag(CodeGeneratorRequest request)60   static boolean hasNumericEnumFlag(CodeGeneratorRequest request) {
61     return hasFlag(request.getParameter(), KEY_NUMERIC_ENUM);
62   }
63 
64   /** Expects a comma-separated list of file paths. */
65   @VisibleForTesting
parseJsonConfigPath(String pluginProtocArgument)66   static Optional<String> parseJsonConfigPath(String pluginProtocArgument) {
67     return parseFileArgument(pluginProtocArgument, KEY_GRPC_SERVICE_CONFIG, JSON_FILE_ENDING);
68   }
69 
70   @VisibleForTesting
parseGapicYamlConfigPath(String pluginProtocArgument)71   static Optional<String> parseGapicYamlConfigPath(String pluginProtocArgument) {
72     return parseFileArgument(pluginProtocArgument, KEY_GAPIC_CONFIG, GAPIC_YAML_FILE_ENDING);
73   }
74 
75   @VisibleForTesting
parseServiceYamlConfigPath(String pluginProtocArgument)76   static Optional<String> parseServiceYamlConfigPath(String pluginProtocArgument) {
77     return parseFileArgument(
78         pluginProtocArgument, KEY_SERVICE_YAML_CONFIG, SERVICE_YAML_FILE_ENDING);
79   }
80 
81   @VisibleForTesting
parseConfigArgument(String pluginProtocArgument, String key)82   private static Optional<String> parseConfigArgument(String pluginProtocArgument, String key) {
83     if (Strings.isNullOrEmpty(pluginProtocArgument)) {
84       return Optional.empty();
85     }
86 
87     for (String argComponent : pluginProtocArgument.split(COMMA)) {
88       String[] args = argComponent.trim().split(EQUALS);
89       if (args.length == 2 && key.equals(args[0])) {
90         return Optional.of(args[1]);
91       }
92     }
93     return Optional.empty();
94   }
95 
96   @VisibleForTesting
hasFlag(String pluginProtocArgument, String flagKey)97   static boolean hasFlag(String pluginProtocArgument, String flagKey) {
98     return Arrays.stream(pluginProtocArgument.split(COMMA)).anyMatch(s -> s.equals(flagKey));
99   }
100 
parseFileArgument( String pluginProtocArgument, String key, String fileEnding)101   private static Optional<String> parseFileArgument(
102       String pluginProtocArgument, String key, String fileEnding) {
103     if (Strings.isNullOrEmpty(pluginProtocArgument)) {
104       return Optional.empty();
105     }
106     for (String argComponent : pluginProtocArgument.split(COMMA)) {
107       String[] args = argComponent.trim().split(EQUALS);
108       if (args.length < 2) {
109         continue;
110       }
111       String keyVal = args[0];
112       String valueVal = args[1];
113       boolean valueMeetsCriteria = keyVal.equals(key) && valueVal.endsWith(fileEnding);
114       if (fileEnding.equals(SERVICE_YAML_FILE_ENDING)) {
115         valueMeetsCriteria &= !valueVal.endsWith(GAPIC_YAML_FILE_ENDING);
116       }
117 
118       if (valueMeetsCriteria) {
119         return Optional.of(valueVal);
120       }
121     }
122     return Optional.empty();
123   }
124 }
125