Skip to content

Commit 39918f1

Browse files
authored
[ggj][codegen] fix!: wrap protobuf location and process comments (#336)
* feat: add protobuf comment parser util * fix: add basic proto build rules * feat: add header comments to ServiceClient * fix: build protoc at test time * fix!: wrap protobuf location and process comments * fix: merge
1 parent b4ca070 commit 39918f1

File tree

3 files changed

+107
-43
lines changed

3 files changed

+107
-43
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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.model;
16+
17+
import com.google.common.escape.Escaper;
18+
import com.google.common.escape.Escapers;
19+
import com.google.protobuf.DescriptorProtos.SourceCodeInfo.Location;
20+
import javax.annotation.Nonnull;
21+
22+
/**
23+
* A light wrapper around SourceCodeInfo.Location to provide cleaner protobuf comments. Please see
24+
* additional documentation on descriptor.proto.
25+
*/
26+
public class SourceCodeInfoLocation {
27+
// Not a singleton because of nested-class instantiation mechanics.
28+
private final NewlineEscaper ESCAPER = new NewlineEscaper();
29+
30+
@Nonnull private final Location location;
31+
32+
private SourceCodeInfoLocation(Location location) {
33+
this.location = location;
34+
}
35+
36+
public static SourceCodeInfoLocation create(@Nonnull Location location) {
37+
return new SourceCodeInfoLocation(location);
38+
}
39+
40+
public String getLeadingComments() {
41+
return processProtobufComment(location.getLeadingComments());
42+
}
43+
44+
public String getTrailingComments() {
45+
return processProtobufComment(location.getTrailingComments());
46+
}
47+
48+
public String getLeadingDetachedComments(int index) {
49+
return processProtobufComment(location.getLeadingDetachedComments(index));
50+
}
51+
52+
private String processProtobufComment(String s) {
53+
return ESCAPER.escape(s).trim();
54+
}
55+
56+
private class NewlineEscaper extends Escaper {
57+
private final Escaper charEscaper = Escapers.builder().addEscape('\n', "").build();
58+
59+
@Override
60+
public String escape(String sourceString) {
61+
return charEscaper.escape(sourceString);
62+
}
63+
}
64+
}

src/main/java/com/google/api/generator/gapic/protoparser/SourceCodeInfoParser.java

+15-14
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
package com.google.api.generator.gapic.protoparser;
1616

17+
import com.google.api.generator.gapic.model.SourceCodeInfoLocation;
1718
import com.google.common.base.Function;
1819
import com.google.common.base.Joiner;
1920
import com.google.common.collect.ImmutableList;
@@ -60,72 +61,72 @@ public class SourceCodeInfoParser {
6061

6162
/** Gets the location of a message, if available. */
6263
@Nullable
63-
public Location getLocation(Descriptor message) {
64+
public SourceCodeInfoLocation getLocation(Descriptor message) {
6465
FileDescriptor file = message.getFile();
6566
if (!file.toProto().hasSourceCodeInfo()) {
6667
return null;
6768
}
68-
return getLocation(file, buildPath(message));
69+
return SourceCodeInfoLocation.create(getLocation(file, buildPath(message)));
6970
}
7071

7172
/** Gets the location of a field, if available. */
7273
@Nullable
73-
public Location getLocation(FieldDescriptor field) {
74+
public SourceCodeInfoLocation getLocation(FieldDescriptor field) {
7475
FileDescriptor file = field.getFile();
7576
if (!file.toProto().hasSourceCodeInfo()) {
7677
return null;
7778
}
78-
return getLocation(file, buildPath(field));
79+
return SourceCodeInfoLocation.create(getLocation(file, buildPath(field)));
7980
}
8081

8182
/** Gets the location of a service, if available. */
8283
@Nullable
83-
public Location getLocation(ServiceDescriptor service) {
84+
public SourceCodeInfoLocation getLocation(ServiceDescriptor service) {
8485
FileDescriptor file = service.getFile();
8586
if (!file.toProto().hasSourceCodeInfo()) {
8687
return null;
8788
}
88-
return getLocation(file, buildPath(service));
89+
return SourceCodeInfoLocation.create(getLocation(file, buildPath(service)));
8990
}
9091

9192
/** Gets the location of a method, if available. */
9293
@Nullable
93-
public Location getLocation(MethodDescriptor method) {
94+
public SourceCodeInfoLocation getLocation(MethodDescriptor method) {
9495
FileDescriptor file = method.getFile();
9596
if (!file.toProto().hasSourceCodeInfo()) {
9697
return null;
9798
}
98-
return getLocation(file, buildPath(method));
99+
return SourceCodeInfoLocation.create(getLocation(file, buildPath(method)));
99100
}
100101

101102
/** Gets the location of an enum type, if available. */
102103
@Nullable
103-
public Location getLocation(EnumDescriptor enumType) {
104+
public SourceCodeInfoLocation getLocation(EnumDescriptor enumType) {
104105
FileDescriptor file = enumType.getFile();
105106
if (!file.toProto().hasSourceCodeInfo()) {
106107
return null;
107108
}
108-
return getLocation(file, buildPath(enumType));
109+
return SourceCodeInfoLocation.create(getLocation(file, buildPath(enumType)));
109110
}
110111

111112
/** Gets the location of an enum value, if available. */
112113
@Nullable
113-
public Location getLocation(EnumValueDescriptor enumValue) {
114+
public SourceCodeInfoLocation getLocation(EnumValueDescriptor enumValue) {
114115
FileDescriptor file = enumValue.getFile();
115116
if (!file.toProto().hasSourceCodeInfo()) {
116117
return null;
117118
}
118-
return getLocation(file, buildPath(enumValue));
119+
return SourceCodeInfoLocation.create(getLocation(file, buildPath(enumValue)));
119120
}
120121

121122
/** Gets the location of a oneof, if available. */
122123
@Nullable
123-
public Location getLocation(OneofDescriptor oneof) {
124+
public SourceCodeInfoLocation getLocation(OneofDescriptor oneof) {
124125
FileDescriptor file = oneof.getFile();
125126
if (!file.toProto().hasSourceCodeInfo()) {
126127
return null;
127128
}
128-
return getLocation(file, buildPath(oneof));
129+
return SourceCodeInfoLocation.create(getLocation(file, buildPath(oneof)));
129130
}
130131

131132
// -----------------------------------------------------------------------------

src/test/java/com/google/api/generator/gapic/protoparser/SourceCodeInfoParserTest.java

+28-29
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
import static com.google.common.truth.Truth.assertThat;
1818
import static junit.framework.Assert.assertEquals;
1919

20+
import com.google.api.generator.gapic.model.SourceCodeInfoLocation;
2021
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
2122
import com.google.protobuf.DescriptorProtos.FileDescriptorSet;
22-
import com.google.protobuf.DescriptorProtos.SourceCodeInfo.Location;
2323
import com.google.protobuf.Descriptors.Descriptor;
2424
import com.google.protobuf.Descriptors.EnumDescriptor;
2525
import com.google.protobuf.Descriptors.FileDescriptor;
@@ -46,48 +46,47 @@ public void setUp() throws Exception {
4646

4747
@Test
4848
public void getServiceInfo() {
49-
Location location = parser.getLocation(protoFile.findServiceByName("FooService"));
49+
SourceCodeInfoLocation location = parser.getLocation(protoFile.findServiceByName("FooService"));
5050
assertEquals(
51-
" This is a service description.\n It takes up multiple lines, like so.\n",
51+
"This is a service description. It takes up multiple lines, like so.",
5252
location.getLeadingComments());
5353

5454
location = parser.getLocation(protoFile.findServiceByName("BarService"));
55-
assertEquals(" This is another service description.\n", location.getLeadingComments());
55+
assertEquals("This is another service description.", location.getLeadingComments());
5656
}
5757

5858
@Test
5959
public void getMethodInfo() {
6060
ServiceDescriptor service = protoFile.findServiceByName("FooService");
61-
Location location = parser.getLocation(service.findMethodByName("FooMethod"));
61+
SourceCodeInfoLocation location = parser.getLocation(service.findMethodByName("FooMethod"));
6262
assertEquals(
63-
" FooMethod does something.\n This comment also takes up multiple lines.\n",
63+
"FooMethod does something. This comment also takes up multiple lines.",
6464
location.getLeadingComments());
6565

6666
service = protoFile.findServiceByName("BarService");
6767
location = parser.getLocation(service.findMethodByName("BarMethod"));
68-
assertEquals(" BarMethod does another thing.\n", location.getLeadingComments());
68+
assertEquals("BarMethod does another thing.", location.getLeadingComments());
6969
}
7070

7171
@Test
7272
public void getOuterMessageInfo() {
7373
Descriptor message = protoFile.findMessageTypeByName("FooMessage");
74-
Location location = parser.getLocation(message);
74+
SourceCodeInfoLocation location = parser.getLocation(message);
7575
assertEquals(
76-
" This is a message descxription.\n"
77-
+ " Lorum ipsum dolor sit amet consectetur adipiscing elit.\n",
76+
"This is a message descxription. Lorum ipsum dolor sit amet consectetur adipiscing elit.",
7877
location.getLeadingComments());
7978

8079
// Fields.
8180
location = parser.getLocation(message.findFieldByName("field_one"));
8281
assertEquals(
83-
" This is a field description for field_one.\n"
84-
+ " And here is the second line of that description.\n",
82+
"This is a field description for field_one. And here is the second line of that"
83+
+ " description.",
8584
location.getLeadingComments());
86-
assertEquals(" A field trailing comment.\n", location.getTrailingComments());
85+
assertEquals("A field trailing comment.", location.getTrailingComments());
8786

8887
location = parser.getLocation(message.findFieldByName("field_two"));
89-
assertEquals(" This is another field description.\n", location.getLeadingComments());
90-
assertEquals(" Another field trailing comment.\n", location.getTrailingComments());
88+
assertEquals("This is another field description.", location.getLeadingComments());
89+
assertEquals("Another field trailing comment.", location.getTrailingComments());
9190
}
9291

9392
@Test
@@ -96,52 +95,52 @@ public void getInnerMessageInfo() {
9695
assertThat(message).isNotNull();
9796
message = message.findNestedTypeByName("BarMessage");
9897

99-
Location location = parser.getLocation(message);
98+
SourceCodeInfoLocation location = parser.getLocation(message);
10099
assertEquals(
101-
" This is an inner message description for BarMessage.\n", location.getLeadingComments());
100+
"This is an inner message description for BarMessage.", location.getLeadingComments());
102101

103102
// Fields.
104103
location = parser.getLocation(message.findFieldByName("field_three"));
105-
assertEquals(" A third leading comment for field_three.\n", location.getLeadingComments());
104+
assertEquals("A third leading comment for field_three.", location.getLeadingComments());
106105

107106
location = parser.getLocation(message.findFieldByName("field_two"));
108-
assertEquals("\n This is a block comment for field_two.\n", location.getLeadingComments());
107+
assertEquals("This is a block comment for field_two.", location.getLeadingComments());
109108
}
110109

111110
@Test
112111
public void getOuterEnumInfo() {
113112
EnumDescriptor protoEnum = protoFile.findEnumTypeByName("OuterEnum");
114-
Location location = parser.getLocation(protoEnum);
115-
assertEquals(" This is an outer enum.\n", location.getLeadingComments());
113+
SourceCodeInfoLocation location = parser.getLocation(protoEnum);
114+
assertEquals("This is an outer enum.", location.getLeadingComments());
116115

117116
// Enum fields.
118117
location = parser.getLocation(protoEnum.findValueByName("VALUE_UNSPECIFIED"));
119-
assertEquals(" Another unspecified value.\n", location.getLeadingComments());
118+
assertEquals("Another unspecified value.", location.getLeadingComments());
120119
}
121120

122121
@Test
123122
public void getInnerEnumInfo() {
124123
Descriptor message = protoFile.findMessageTypeByName("FooMessage");
125124
EnumDescriptor protoEnum = message.findEnumTypeByName("FoodEnum");
126-
Location location = parser.getLocation(protoEnum);
127-
assertEquals(" An inner enum.\n", location.getLeadingComments());
125+
SourceCodeInfoLocation location = parser.getLocation(protoEnum);
126+
assertEquals("An inner enum.", location.getLeadingComments());
128127

129128
// Enum fields.
130129
location = parser.getLocation(protoEnum.findValueByName("RICE"));
131-
assertEquals(" 😋 🍚.\n", location.getLeadingComments());
130+
assertEquals("😋 🍚.", location.getLeadingComments());
132131
location = parser.getLocation(protoEnum.findValueByName("CHOCOLATE"));
133-
assertEquals(" 🤤 🍫.\n", location.getLeadingComments());
132+
assertEquals("🤤 🍫.", location.getLeadingComments());
134133
}
135134

136135
@Test
137136
public void getOnoeofInfo() {
138137
Descriptor message = protoFile.findMessageTypeByName("FooMessage");
139138
OneofDescriptor protoOneof = message.getOneofs().get(0);
140-
Location location = parser.getLocation(protoOneof);
141-
assertEquals(" An inner oneof.\n", location.getLeadingComments());
139+
SourceCodeInfoLocation location = parser.getLocation(protoOneof);
140+
assertEquals("An inner oneof.", location.getLeadingComments());
142141

143142
location = parser.getLocation(protoOneof.getField(0));
144-
assertEquals(" An InnerOneof comment for its field.\n", location.getLeadingComments());
143+
assertEquals("An InnerOneof comment for its field.", location.getLeadingComments());
145144
}
146145

147146
/**

0 commit comments

Comments
 (0)