Skip to content

Commit 8f2e818

Browse files
authored
Handle Enums (#13)
* add enum in the example * pepare for v1.5.0 * build enums * bug fix * update enum position * live without debts * add enums after top-level messages * refactor printing * use proper naming to avoid conflicts * generation reordering * checkpoint * correction * use proper name for other types * refactor * refactor * fix test * minor
1 parent 1960b3a commit 8f2e818

File tree

4 files changed

+112
-76
lines changed

4 files changed

+112
-76
lines changed

content_builder.go

Lines changed: 88 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package main
33
import (
44
"errors"
55
"fmt"
6-
"regexp"
76
"strings"
87

98
"google.golang.org/protobuf/types/descriptorpb"
@@ -28,99 +27,122 @@ func (b *contentBuilder) build(protoFile *descriptorpb.FileDescriptorProto) (str
2827
}
2928

3029
compVersion := b.request.GetCompilerVersion()
31-
fmt.Fprintln(b.output, "// Code generated by protoc-gen-pubsub-schema. DO NOT EDIT.")
32-
fmt.Fprintln(b.output, "// versions:")
33-
fmt.Fprintln(b.output, "// protoc-gen-pubsub-schema v1.4.2")
30+
fmt.Fprintf(b.output, "// Code generated by protoc-gen-pubsub-schema. DO NOT EDIT.\n")
31+
fmt.Fprintf(b.output, "// versions:\n")
32+
fmt.Fprintf(b.output, "// protoc-gen-pubsub-schema v1.5.0\n")
3433
fmt.Fprintf(b.output, "// protoc v%d.%d.%d%s\n", compVersion.GetMajor(), compVersion.GetMinor(), compVersion.GetPatch(), compVersion.GetSuffix())
35-
fmt.Fprintln(b.output, "// source:", protoFile.GetName())
36-
fmt.Fprintln(b.output, "")
37-
fmt.Fprintf(b.output, `syntax = "%s";`, b.schemaSyntax)
38-
b.output.WriteString("\n\n")
39-
b.buildMessage(protoFile.GetMessageType()[0], 0)
34+
fmt.Fprintf(b.output, "// source: %s\n\n", protoFile.GetName())
35+
fmt.Fprintf(b.output, "syntax = \"%s\";\n", b.schemaSyntax)
36+
b.buildMessages(protoFile.GetMessageType(), 0)
37+
b.buildEnums(protoFile.GetEnumType(), 0)
4038
return b.output.String(), nil
4139
}
4240

41+
func (b *contentBuilder) buildMessages(messages []*descriptorpb.DescriptorProto, level int) {
42+
for _, message := range messages {
43+
fmt.Fprintln(b.output)
44+
b.buildMessage(message, level)
45+
}
46+
}
47+
4348
func (b *contentBuilder) buildMessage(message *descriptorpb.DescriptorProto, level int) {
4449
fmt.Fprintf(b.output, "%smessage %s {\n", buildIndent(level), message.GetName())
45-
debts := []string(nil)
46-
for _, field := range message.GetField() {
47-
debts = append(debts, b.buildField(field, level+1))
48-
}
49-
b.payDebts(debts, level+1)
50+
b.buildFields(message.GetField(), level+1)
51+
b.buildMessages(message.GetNestedType(), level+1)
52+
b.buildEnums(message.GetEnumType(), level+1)
53+
b.buildOtherTypes(message, level+1)
5054
fmt.Fprintf(b.output, "%s}\n", buildIndent(level))
5155
}
5256

53-
func (b *contentBuilder) buildField(field *descriptorpb.FieldDescriptorProto, level int) string {
54-
fieldType, debt := b.getFieldType(field)
55-
fmt.Fprintf(b.output, "%s%s%s %s = %d;\n",
56-
buildIndent(level),
57-
b.getLabelPrefix(field.GetLabel()),
58-
fieldType,
59-
field.GetName(),
60-
field.GetNumber(),
61-
)
62-
return debt
57+
func (b *contentBuilder) buildFields(fields []*descriptorpb.FieldDescriptorProto, level int) {
58+
for _, field := range fields {
59+
fmt.Fprint(b.output, buildIndent(level))
60+
label := field.GetLabel()
61+
if b.schemaSyntax == "proto2" || label == descriptorpb.FieldDescriptorProto_LABEL_REPEATED {
62+
fmt.Fprintf(b.output, "%s ", strings.ToLower(strings.TrimPrefix(label.String(), "LABEL_")))
63+
}
64+
fmt.Fprintf(b.output, "%s %s = %d;\n", b.getFieldType(field), field.GetName(), field.GetNumber())
65+
}
6366
}
6467

65-
func (b *contentBuilder) getFieldType(field *descriptorpb.FieldDescriptorProto) (string, string) {
66-
if field.GetType() != descriptorpb.FieldDescriptorProto_TYPE_MESSAGE {
67-
return strings.ToLower(strings.TrimPrefix(field.GetType().String(), "TYPE_")), ""
68-
}
69-
fullMessageName := field.GetTypeName()
70-
if b.messageEncoding == "json" {
71-
if wkt, ok := wktMapping[fullMessageName]; ok {
72-
return wkt, ""
68+
func (b *contentBuilder) getFieldType(field *descriptorpb.FieldDescriptorProto) string {
69+
typeName := field.GetTypeName()
70+
switch field.GetType() {
71+
case descriptorpb.FieldDescriptorProto_TYPE_MESSAGE:
72+
if b.messageEncoding == "json" && wktMapping[typeName] != "" {
73+
return wktMapping[typeName]
74+
}
75+
if b.isNestedType(typeName) {
76+
return shortName(typeName)
7377
}
78+
return pascalCase(typeName)
79+
case descriptorpb.FieldDescriptorProto_TYPE_ENUM:
80+
return shortName(typeName)
81+
default:
82+
return strings.ToLower(strings.TrimPrefix(field.GetType().String(), "TYPE_"))
7483
}
75-
return b.getLocalName(fullMessageName), fullMessageName
7684
}
7785

78-
func (b *contentBuilder) getLabelPrefix(label descriptorpb.FieldDescriptorProto_Label) string {
79-
if label == descriptorpb.FieldDescriptorProto_LABEL_REPEATED {
80-
return "repeated "
81-
}
82-
if b.schemaSyntax == "proto2" {
83-
return strings.ToLower(strings.TrimPrefix(label.String(), "LABEL_")) + " "
86+
func (b *contentBuilder) buildEnums(enums []*descriptorpb.EnumDescriptorProto, level int) {
87+
for _, enum := range enums {
88+
fmt.Fprintln(b.output)
89+
fmt.Fprintf(b.output, "%senum %s {\n", buildIndent(level), enum.GetName())
90+
for _, value := range enum.GetValue() {
91+
fmt.Fprintf(b.output, "%s%s = %d;\n", buildIndent(level+1), value.GetName(), value.GetNumber())
92+
}
93+
fmt.Fprintf(b.output, "%s}\n", buildIndent(level))
8494
}
85-
return ""
8695
}
8796

88-
func (b *contentBuilder) payDebts(debts []string, level int) {
89-
payedDebts := make(map[string]bool)
90-
for _, debt := range debts {
91-
if debt != "" && !payedDebts[debt] {
92-
b.payDebt(debt, level)
93-
payedDebts[debt] = true
97+
func (b *contentBuilder) buildOtherTypes(message *descriptorpb.DescriptorProto, level int) {
98+
built := make(map[string]bool)
99+
for _, field := range message.GetField() {
100+
typeName := field.GetTypeName()
101+
if field.GetType() != descriptorpb.FieldDescriptorProto_TYPE_MESSAGE {
102+
continue
94103
}
104+
if b.messageEncoding == "json" && wktMapping[typeName] != "" {
105+
continue
106+
}
107+
if b.isNestedType(typeName) {
108+
continue
109+
}
110+
if built[typeName] {
111+
continue
112+
}
113+
b.buildOtherType(typeName, level)
114+
built[typeName] = true
95115
}
96116
}
97117

98-
func (b *contentBuilder) payDebt(debt string, level int) {
99-
message := b.messageTypes[debt]
100-
defer func(originalName *string) { message.Name = originalName }(message.Name)
101-
localName := b.getLocalName(debt)
102-
message.Name = &localName
103-
b.output.WriteString("\n")
118+
func (b *contentBuilder) buildOtherType(typeName string, level int) {
119+
message := b.messageTypes[typeName]
120+
defer func(name *string) { message.Name = name }(message.Name)
121+
*message.Name = pascalCase(typeName)
122+
fmt.Fprintln(b.output)
104123
b.buildMessage(message, level)
105124
}
106125

107-
var localNamePattern = regexp.MustCompile(`\..`)
126+
func (b *contentBuilder) isNestedType(name string) bool {
127+
return b.messageTypes[name[:strings.LastIndexByte(name, '.')]] != nil
128+
}
108129

109-
func (b *contentBuilder) getLocalName(fullMessageName string) string {
110-
if b.isNestedType(fullMessageName) {
111-
return fullMessageName[strings.LastIndexByte(fullMessageName, '.')+1:]
112-
}
113-
return localNamePattern.ReplaceAllStringFunc(
114-
fullMessageName,
115-
func(s string) string { return strings.ToUpper(s[1:]) },
116-
)
130+
func buildIndent(level int) string {
131+
return strings.Repeat(" ", level)
117132
}
118133

119-
func (b *contentBuilder) isNestedType(fullMessageName string) bool {
120-
parent := fullMessageName[:strings.LastIndexByte(fullMessageName, '.')]
121-
return b.messageTypes[parent] != nil
134+
func shortName(name string) string {
135+
return name[strings.LastIndexByte(name, '.')+1:]
122136
}
123137

124-
func buildIndent(level int) string {
125-
return strings.Repeat(" ", level)
138+
func pascalCase(name string) string {
139+
sb := new(strings.Builder)
140+
for i, c := range name {
141+
if i > 0 && name[i-1] == '.' {
142+
sb.WriteString(strings.ToUpper(string(c)))
143+
} else if c != '.' {
144+
sb.WriteRune(c)
145+
}
146+
}
147+
return sb.String()
126148
}

example/user_add_comment.pps

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Code generated by protoc-gen-pubsub-schema. DO NOT EDIT.
22
// versions:
3-
// protoc-gen-pubsub-schema v1.4.2
3+
// protoc-gen-pubsub-schema v1.5.0
44
// protoc v3.17.3
55
// source: example/user_add_comment.proto
66

@@ -15,16 +15,23 @@ message UserAddComment {
1515
message User {
1616
required string first_name = 1;
1717
optional string last_name = 2;
18-
optional bytes avatar = 3;
19-
optional Location location = 4;
20-
optional GoogleProtobufTimestamp created_at = 5;
21-
optional GoogleProtobufTimestamp updated_at = 6;
18+
required Role role = 3;
19+
optional bytes avatar = 4;
20+
optional Location location = 5;
21+
optional GoogleProtobufTimestamp created_at = 6;
22+
optional GoogleProtobufTimestamp updated_at = 7;
2223

2324
message Location {
2425
required double longitude = 1;
2526
required double latitude = 2;
2627
}
2728

29+
enum Role {
30+
OWNER = 1;
31+
EDITOR = 2;
32+
VIEWER = 3;
33+
}
34+
2835
message GoogleProtobufTimestamp {
2936
optional int64 seconds = 1;
3037
optional int32 nanos = 2;

example/user_add_comment.proto

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,21 @@ message UserAddComment {
1414
message User {
1515
required string first_name = 1;
1616
optional string last_name = 2;
17-
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;
17+
required Role role = 3;
18+
optional bytes avatar = 4;
19+
optional Location location = 5;
20+
optional google.protobuf.Timestamp created_at = 6;
21+
optional google.protobuf.Timestamp updated_at = 7;
2122

2223
message Location {
2324
required double longitude = 1;
2425
required double latitude = 2;
2526
}
27+
28+
enum Role {
29+
OWNER = 1;
30+
EDITOR = 2;
31+
VIEWER = 3;
32+
}
2633
}
2734
}

test/user_add_comment.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
file: {
22
name: "example/user_add_comment.pps"
3-
content: "// Code generated by protoc-gen-pubsub-schema. DO NOT EDIT.\n// versions:\n// protoc-gen-pubsub-schema v1.4.2\n// protoc v3.17.3\n// source: example/user_add_comment.proto\n\nsyntax = \"proto2\";\n\nmessage UserAddComment {\n required User user = 1;\n required string comment = 2;\n repeated ExampleCommonLabel labels = 3;\n required GoogleProtobufTimestamp timestamp = 101;\n\n message User {\n required string first_name = 1;\n optional string last_name = 2;\n optional bytes avatar = 3;\n optional Location location = 4;\n optional GoogleProtobufTimestamp created_at = 5;\n optional GoogleProtobufTimestamp updated_at = 6;\n\n message Location {\n required double longitude = 1;\n required double latitude = 2;\n }\n\n message GoogleProtobufTimestamp {\n optional int64 seconds = 1;\n optional int32 nanos = 2;\n }\n }\n\n message ExampleCommonLabel {\n optional string key = 1;\n optional string value = 2;\n }\n\n message GoogleProtobufTimestamp {\n optional int64 seconds = 1;\n optional int32 nanos = 2;\n }\n}\n"
3+
content: "// Code generated by protoc-gen-pubsub-schema. DO NOT EDIT.\n// versions:\n// protoc-gen-pubsub-schema v1.5.0\n// protoc v3.17.3\n// source: example/user_add_comment.proto\n\nsyntax = \"proto2\";\n\nmessage UserAddComment {\n required User user = 1;\n required string comment = 2;\n repeated ExampleCommonLabel labels = 3;\n required GoogleProtobufTimestamp timestamp = 101;\n\n message User {\n required string first_name = 1;\n optional string last_name = 2;\n optional bytes avatar = 3;\n optional Location location = 4;\n optional GoogleProtobufTimestamp created_at = 5;\n optional GoogleProtobufTimestamp updated_at = 6;\n\n message Location {\n required double longitude = 1;\n required double latitude = 2;\n }\n\n message GoogleProtobufTimestamp {\n optional int64 seconds = 1;\n optional int32 nanos = 2;\n }\n }\n\n message ExampleCommonLabel {\n optional string key = 1;\n optional string value = 2;\n }\n\n message GoogleProtobufTimestamp {\n optional int64 seconds = 1;\n optional int32 nanos = 2;\n }\n}\n"
44
}

0 commit comments

Comments
 (0)