Skip to content

Commit 4cb7763

Browse files
authored
Fix Multiple External Type Usage in One Message (#8)
* update example/user_add_comment.proto * make created_at and updated_at optional * avoid message definition conflicts * update sample output * revert the original name of a message * make debts order predictable * fix test
1 parent 0f4382d commit 4cb7763

File tree

5 files changed

+99
-59
lines changed

5 files changed

+99
-59
lines changed

content_builder.go

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"errors"
55
"fmt"
6+
"regexp"
67
"strings"
78

89
"google.golang.org/protobuf/types/descriptorpb"
@@ -33,44 +34,77 @@ func (b *contentBuilder) build(protoFile *descriptorpb.FileDescriptorProto) (str
3334
}
3435

3536
func (b *contentBuilder) buildMessage(message *descriptorpb.DescriptorProto, level int) {
36-
b.output.WriteString(buildIndent(level) + "message " + message.GetName() + " {\n")
37+
fmt.Fprintf(b.output, "%smessage %s {\n", buildIndent(level), message.GetName())
38+
debts := []string(nil)
3739
for _, field := range message.GetField() {
38-
b.buildField(field, level+1)
40+
debts = append(debts, b.buildField(field, level+1))
3941
}
40-
b.output.WriteString(buildIndent(level) + "}\n")
42+
b.payDebts(debts, level+1)
43+
fmt.Fprintf(b.output, "%s}\n", buildIndent(level))
4144
}
4245

43-
func (b *contentBuilder) buildField(field *descriptorpb.FieldDescriptorProto, level int) {
44-
fieldType := strings.ToLower(strings.TrimPrefix(field.GetType().String(), "TYPE_"))
45-
if field.GetType() == descriptorpb.FieldDescriptorProto_TYPE_MESSAGE {
46-
fieldType = b.buildFieldType(field.GetTypeName(), level)
47-
}
48-
b.output.WriteString(buildIndent(level))
49-
b.buildFieldLabel(field.GetLabel())
50-
fmt.Fprintf(b.output, "%s %s = %d;\n", fieldType, field.GetName(), field.GetNumber())
46+
func (b *contentBuilder) buildField(field *descriptorpb.FieldDescriptorProto, level int) string {
47+
fieldType, debt := b.getFieldType(field)
48+
fmt.Fprintf(b.output, "%s%s%s %s = %d;\n",
49+
buildIndent(level),
50+
b.getLabelPrefix(field.GetLabel()),
51+
fieldType,
52+
field.GetName(),
53+
field.GetNumber(),
54+
)
55+
return debt
5156
}
5257

53-
func (b *contentBuilder) buildFieldType(typeName string, level int) string {
54-
if b.messageEncoding == "json" {
55-
if typeName, ok := wktMapping[typeName]; ok {
56-
return typeName
57-
}
58+
func (b *contentBuilder) getFieldType(field *descriptorpb.FieldDescriptorProto) (string, string) {
59+
if field.GetType() != descriptorpb.FieldDescriptorProto_TYPE_MESSAGE {
60+
return strings.ToLower(strings.TrimPrefix(field.GetType().String(), "TYPE_")), ""
5861
}
59-
60-
b.output.WriteString("\n")
61-
b.buildMessage(b.messageTypes[typeName], level)
62-
b.output.WriteString("\n")
63-
return typeName[strings.LastIndexByte(typeName, '.')+1:]
62+
fullMessageName := field.GetTypeName()
63+
wkt := wktMapping[fullMessageName]
64+
if b.messageEncoding == "json" && wkt != "" {
65+
return wkt, ""
66+
}
67+
return getLocalName(fullMessageName), fullMessageName
6468
}
6569

66-
func (b *contentBuilder) buildFieldLabel(label descriptorpb.FieldDescriptorProto_Label) {
70+
func (b *contentBuilder) getLabelPrefix(label descriptorpb.FieldDescriptorProto_Label) string {
6771
if label == descriptorpb.FieldDescriptorProto_LABEL_REPEATED {
68-
b.output.WriteString("repeated ")
69-
} else if b.schemaSyntax == "proto2" {
70-
b.output.WriteString(strings.ToLower(strings.TrimPrefix(label.String(), "LABEL_")) + " ")
72+
return "repeated "
73+
}
74+
if b.schemaSyntax == "proto2" {
75+
return strings.ToLower(strings.TrimPrefix(label.String(), "LABEL_")) + " "
7176
}
77+
return ""
78+
}
79+
80+
func (b *contentBuilder) payDebts(debts []string, level int) {
81+
payedDebts := make(map[string]bool)
82+
for _, debt := range debts {
83+
if debt != "" && !payedDebts[debt] {
84+
b.payDebt(debt, level)
85+
payedDebts[debt] = true
86+
}
87+
}
88+
}
89+
90+
func (b *contentBuilder) payDebt(debt string, level int) {
91+
message := b.messageTypes[debt]
92+
defer func(originalName *string) { message.Name = originalName }(message.Name)
93+
localName := getLocalName(debt)
94+
message.Name = &localName
95+
b.output.WriteString("\n")
96+
b.buildMessage(message, level)
7297
}
7398

7499
func buildIndent(level int) string {
75100
return strings.Repeat(" ", level)
76101
}
102+
103+
var localNamePattern = regexp.MustCompile(`\..`)
104+
105+
func getLocalName(fullMessageName string) string {
106+
return localNamePattern.ReplaceAllStringFunc(
107+
fullMessageName,
108+
func(s string) string { return strings.ToUpper(s[1:]) },
109+
)
110+
}

example/user_add_comment.pps

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,37 @@
11
syntax = "proto2";
22

33
message UserAddComment {
4+
required ExampleUserAddCommentUser user = 1;
5+
required string comment = 2;
6+
repeated ExampleCommonLabel labels = 3;
7+
required GoogleProtobufTimestamp timestamp = 101;
48

5-
message User {
9+
message ExampleUserAddCommentUser {
610
required string first_name = 1;
711
optional string last_name = 2;
812
optional bytes avatar = 3;
13+
optional ExampleUserAddCommentUserLocation location = 4;
14+
optional GoogleProtobufTimestamp created_at = 5;
15+
optional GoogleProtobufTimestamp updated_at = 6;
916

10-
message Location {
17+
message ExampleUserAddCommentUserLocation {
1118
required double longitude = 1;
1219
required double latitude = 2;
1320
}
1421

15-
optional Location location = 4;
22+
message GoogleProtobufTimestamp {
23+
optional int64 seconds = 1;
24+
optional int32 nanos = 2;
25+
}
1626
}
1727

18-
required User user = 1;
19-
required string comment = 2;
20-
21-
message Label {
28+
message ExampleCommonLabel {
2229
optional string key = 1;
2330
optional string value = 2;
2431
}
2532

26-
repeated Label labels = 3;
27-
28-
message Timestamp {
33+
message GoogleProtobufTimestamp {
2934
optional int64 seconds = 1;
3035
optional int32 nanos = 2;
3136
}
32-
33-
required Timestamp timestamp = 101;
3437
}

example/user_add_comment.proto

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,22 @@ import "example/common/label.proto";
66
import "google/protobuf/timestamp.proto";
77

88
message UserAddComment {
9+
required User user = 1;
10+
required string comment = 2;
11+
repeated example.common.Label labels = 3;
12+
required google.protobuf.Timestamp timestamp = 101;
13+
914
message User {
1015
required string first_name = 1;
1116
optional string last_name = 2;
1217
optional bytes avatar = 3;
18+
optional Location location = 4;
19+
optional google.protobuf.Timestamp created_at = 5;
20+
optional google.protobuf.Timestamp updated_at = 6;
1321

1422
message Location {
1523
required double longitude = 1;
1624
required double latitude = 2;
1725
}
18-
19-
optional Location location = 4;
2026
}
21-
22-
required User user = 1;
23-
required string comment = 2;
24-
repeated example.common.Label labels = 3;
25-
26-
required google.protobuf.Timestamp timestamp = 101;
2727
}

test/user_add_comment.in

276 Bytes
Binary file not shown.

test/user_add_comment.out

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,38 @@
1-
z�
2-
example/user_add_comment.ppsz�syntax = "proto2";
1+
z�
2+
example/user_add_comment.ppsz�syntax = "proto2";
33

44
message UserAddComment {
5+
required ExampleUserAddCommentUser user = 1;
6+
required string comment = 2;
7+
repeated ExampleCommonLabel labels = 3;
8+
required GoogleProtobufTimestamp timestamp = 101;
59

6-
message User {
10+
message ExampleUserAddCommentUser {
711
required string first_name = 1;
812
optional string last_name = 2;
913
optional bytes avatar = 3;
14+
optional ExampleUserAddCommentUserLocation location = 4;
15+
optional GoogleProtobufTimestamp created_at = 5;
16+
optional GoogleProtobufTimestamp updated_at = 6;
1017

11-
message Location {
18+
message ExampleUserAddCommentUserLocation {
1219
required double longitude = 1;
1320
required double latitude = 2;
1421
}
1522

16-
optional Location location = 4;
23+
message GoogleProtobufTimestamp {
24+
optional int64 seconds = 1;
25+
optional int32 nanos = 2;
26+
}
1727
}
1828

19-
required User user = 1;
20-
required string comment = 2;
21-
22-
message Label {
29+
message ExampleCommonLabel {
2330
optional string key = 1;
2431
optional string value = 2;
2532
}
2633

27-
repeated Label labels = 3;
28-
29-
message Timestamp {
34+
message GoogleProtobufTimestamp {
3035
optional int64 seconds = 1;
3136
optional int32 nanos = 2;
3237
}
33-
34-
required Timestamp timestamp = 101;
3538
}

0 commit comments

Comments
 (0)