Skip to content

Commit

Permalink
impl: simplify protobuf translator
Browse files Browse the repository at this point in the history
  • Loading branch information
julieqiu committed Nov 4, 2024
1 parent 382af5f commit f01db53
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 72 deletions.
5 changes: 2 additions & 3 deletions generator/cmd/protoc-gen-gclient/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,11 @@ func run(inputPath, outDir, templateDir string) error {
}
}

req, err := protobuf.NewTranslator(&protobuf.Options{
Request: genReq,
req, err := protobuf.Translate(genReq, &protobuf.Options{
OutDir: outDir,
Language: opts.Language,
TemplateDir: templateDir,
}).Translate()
})
if err != nil {
return err
}
Expand Down
122 changes: 53 additions & 69 deletions generator/internal/genclient/translator/protobuf/protobuf.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package protobuf

import (
"fmt"
"log/slog"
"strings"

Expand All @@ -25,66 +26,45 @@ import (
"google.golang.org/protobuf/types/pluginpb"
)

type Translator struct {
request *pluginpb.CodeGeneratorRequest
language string

// Only used for local testing
outDir string
templateDir string

// State by FQN
state *genclient.APIState
}

type Options struct {
Request *pluginpb.CodeGeneratorRequest
Language string
// Only used for local testing
OutDir string
TemplateDir string
}

func NewTranslator(opts *Options) *Translator {
return &Translator{
outDir: opts.OutDir,
request: opts.Request,
language: opts.Language,
templateDir: opts.TemplateDir,
state: &genclient.APIState{
ServiceByID: make(map[string]*genclient.Service),
MessageByID: make(map[string]*genclient.Message),
EnumByID: make(map[string]*genclient.Enum),
},
// Translate translates proto representation into a [genclienGenerateRequest].
func Translate(req *pluginpb.CodeGeneratorRequest, opts *Options) (*genclient.GenerateRequest, error) {
state := &genclient.APIState{
ServiceByID: make(map[string]*genclient.Service),
MessageByID: make(map[string]*genclient.Message),
EnumByID: make(map[string]*genclient.Enum),
}
}

// Translates proto representation into a [genclient.GenerateRequest].
func (t *Translator) Translate() (*genclient.GenerateRequest, error) {
api := &genclient.API{
//TODO(codyoss): https://github.com/googleapis/google-cloud-rust/issues/38
Name: "secretmanager",
}
files := t.request.GetSourceFileDescriptors()
files := req.GetSourceFileDescriptors()
for _, f := range files {
var fileServices []*genclient.Service
fFQN := "." + f.GetPackage()

// Messages
for _, m := range f.MessageType {
mFQN := fFQN + "." + m.GetName()
msg := t.processMessage(m, mFQN, nil)
msg := processMessage(state, m, mFQN, nil)
api.Messages = append(api.Messages, msg)
}

// Services
for _, s := range f.Service {
service := &genclient.Service{}
sFQN := fFQN + "." + s.GetName()
t.state.ServiceByID[sFQN] = service
service.Name = s.GetName()
service.ID = sFQN
service.DefaultHost = parseDefaultHost(s.GetOptions())
service := &genclient.Service{
Name: s.GetName(),
ID: fmt.Sprintf("%s.%s", fFQN, s.GetName()),
DefaultHost: parseDefaultHost(s.GetOptions()),
}
state.ServiceByID[service.ID] = service
for _, m := range s.Method {
method := &genclient.Method{}
method.HTTPInfo = parseHTTPInfo(m.GetOptions())
Expand All @@ -111,27 +91,28 @@ func (t *Translator) Translate() (*genclient.GenerateRequest, error) {
// Because of message nesting we need to call recursively and
// strip out parts of the path.
m := f.MessageType[p[1]]
t.addMessageDocumentation(m, p[2:], strings.TrimSpace(loc.GetLeadingComments()), fFQN+"."+m.GetName())
addMessageDocumentation(state, m, p[2:], strings.TrimSpace(loc.GetLeadingComments()), fFQN+"."+m.GetName())
case 6:
sFQN := fFQN + "." + f.GetService()[p[1]].GetName()
t.addServiceDocumentation(p[2:], strings.TrimSpace(loc.GetLeadingComments()), sFQN)
addServiceDocumentation(state, p[2:],
strings.TrimSpace(loc.GetLeadingComments()), sFQN)
default:
slog.Warn("file dropped documentation", "loc", p, "docs", loc.GetLeadingComments())
}
}
api.Services = append(api.Services, fileServices...)
}

codec, err := language.NewCodec(t.language)
codec, err := language.NewCodec(opts.Language)
if err != nil {
return nil, err
}
api.State = t.state
api.State = state
return &genclient.GenerateRequest{
API: api,
Codec: codec,
OutDir: t.outDir,
TemplateDir: t.templateDir,
OutDir: opts.OutDir,
TemplateDir: opts.TemplateDir,
}, nil
}

Expand Down Expand Up @@ -190,24 +171,25 @@ func normalizeTypes(in *descriptorpb.FieldDescriptorProto, field *genclient.Fiel
}
}

func (t *Translator) processMessage(m *descriptorpb.DescriptorProto, mFQN string, parent *genclient.Message) *genclient.Message {
message := &genclient.Message{}
t.state.MessageByID[mFQN] = message
message.Name = m.GetName()
message.ID = mFQN
message.Parent = parent
func processMessage(state *genclient.APIState, m *descriptorpb.DescriptorProto, mFQN string, parent *genclient.Message) *genclient.Message {
message := &genclient.Message{
Name: m.GetName(),
ID: mFQN,
Parent: parent,
}
state.MessageByID[mFQN] = message
if opts := m.GetOptions(); opts != nil && opts.GetMapEntry() {
message.IsMap = true
}
if len(m.GetNestedType()) > 0 {
for _, nm := range m.GetNestedType() {
nmFQN := mFQN + "." + nm.GetName()
nmsg := t.processMessage(nm, nmFQN, message)
nmsg := processMessage(state, nm, nmFQN, message)
message.Messages = append(message.Messages, nmsg)
}
}
for _, e := range m.GetEnumType() {
e := t.processEnum(e, mFQN, message)
e := processEnum(state, e, mFQN, message)
message.Enums = append(message.Enums, e)
}
// TODO(codyoss): https://github.com/googleapis/google-cloud-rust/issues/39
Expand All @@ -222,55 +204,57 @@ func (t *Translator) processMessage(m *descriptorpb.DescriptorProto, mFQN string
return message
}

func (t *Translator) processEnum(e *descriptorpb.EnumDescriptorProto, baseFQN string, parent *genclient.Message) *genclient.Enum {
enum := &genclient.Enum{}
t.state.EnumByID[baseFQN+"."+e.GetName()] = enum
enum.Name = e.GetName()
enum.Parent = parent
func processEnum(state *genclient.APIState, e *descriptorpb.EnumDescriptorProto, baseFQN string, parent *genclient.Message) *genclient.Enum {
enum := &genclient.Enum{
Name: e.GetName(),
Parent: parent,
}
state.EnumByID[baseFQN+"."+e.GetName()] = enum
for _, ev := range e.Value {
enumValue := &genclient.EnumValue{}
enumValue.Name = ev.GetName()
enumValue.Number = ev.GetNumber()
enumValue.Parent = enum
enumValue := &genclient.EnumValue{
Name: ev.GetName(),
Number: ev.GetNumber(),
Parent: enum,
}
enum.Values = append(enum.Values, enumValue)
}
return enum
}

func (t *Translator) addServiceDocumentation(p []int32, doc string, sFQN string) {
func addServiceDocumentation(state *genclient.APIState, p []int32, doc string, sFQN string) {
// These magic numbers come from reading the proto docs. They come
// from field numbers of the different descriptor types. See struct
// tags on https://pkg.go.dev/google.golang.org/protobuf/types/descriptorpb#ServiceDescriptorProto.
if len(p) == 0 {
// This is a comment for a service
t.state.ServiceByID[sFQN].Documentation = doc
state.ServiceByID[sFQN].Documentation = doc
} else if len(p) == 2 && p[0] == 2 {
// This is a comment for a method
t.state.ServiceByID[sFQN].Methods[p[1]].Documentation = doc
state.ServiceByID[sFQN].Methods[p[1]].Documentation = doc
} else {
slog.Warn("service dropped documentation", "loc", p, "docs", doc)
}
}

func (t *Translator) addMessageDocumentation(m *descriptorpb.DescriptorProto, p []int32, doc string, mFQN string) {
func addMessageDocumentation(state *genclient.APIState, m *descriptorpb.DescriptorProto, p []int32, doc string, mFQN string) {
// These magic numbers come from reading the proto docs. They come
// from field numbers of the different descriptor types. See struct
// tags on https://pkg.go.dev/google.golang.org/protobuf/types/descriptorpb#DescriptorProto.
if len(p) == 0 {
// This is a comment for a top level message
t.state.MessageByID[mFQN].Documentation = doc
state.MessageByID[mFQN].Documentation = doc
} else if p[0] == 3 {
// This indicates a nested message, recurse.
nmsg := m.GetNestedType()[p[1]]
nmFQN := mFQN + "." + nmsg.GetName()
t.addMessageDocumentation(nmsg, p[2:], doc, nmFQN)
addMessageDocumentation(state, nmsg, p[2:], doc, nmFQN)
} else if len(p) == 2 && p[0] == 2 {
// This is a comment for a field of a message
t.state.MessageByID[mFQN].Fields[p[1]].Documentation = doc
state.MessageByID[mFQN].Fields[p[1]].Documentation = doc
} else if p[0] == 4 {
// This is a comment for a enum of a message
eFQN := mFQN + "." + m.GetEnumType()[p[1]].GetName()
t.addEnumDocumentation(p[2:], doc, eFQN)
addEnumDocumentation(state, p[2:], doc, eFQN)
} else if len(p) == 2 && p[0] == 8 {
// This is a comment for a field of a message one-of, skipping
} else {
Expand All @@ -279,13 +263,13 @@ func (t *Translator) addMessageDocumentation(m *descriptorpb.DescriptorProto, p
}

// addEnumDocumentation adds documentation to an enum.
func (t *Translator) addEnumDocumentation(p []int32, doc string, eFQN string) {
func addEnumDocumentation(state *genclient.APIState, p []int32, doc string, eFQN string) {
if len(p) == 0 {
// This is a comment for an enum
t.state.EnumByID[eFQN].Documentation = doc
state.EnumByID[eFQN].Documentation = doc
} else if len(p) == 2 {
// This is a comment for an enum value
t.state.EnumByID[eFQN].Values[p[1]].Documentation = doc
state.EnumByID[eFQN].Values[p[1]].Documentation = doc
} else {
slog.Warn("enum dropped documentation", "loc", p, "docs", doc)
}
Expand Down

0 comments on commit f01db53

Please sign in to comment.