Skip to content

Commit

Permalink
chore(internal/protoveneer): add protoveneer tool (#9352)
Browse files Browse the repository at this point in the history
Add the protoveneer tool, used to generate the vertexai/genai
client.

Add an entry to CODEOWNERS so the current maintainers, members of
the Go team, can approve and merge PRs.
---------

Co-authored-by: Cody Oss <[email protected]>
  • Loading branch information
jba and codyoss authored Feb 5, 2024
1 parent 5f8e21f commit b7bf165
Show file tree
Hide file tree
Showing 15 changed files with 1,851 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
/logging/ @googleapis/api-logging @googleapis/api-logging-partners @googleapis/yoshi-go-admins
/profiler/ @googleapis/api-profiler @googleapis/yoshi-go-admins
/vertexai/ @googleapis/go-vertexai @googleapis/yoshi-go-admins
/internal/protoveneer/ @googleapis/yoshi-go-admins @jba @eliben

# individual release versions manifest is unowned (to avoid notifying every team)
.release-please-manifest-individual.json
Expand Down
1 change: 1 addition & 0 deletions go.work
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ use (
./internal/generated/snippets
./internal/godocfx
./internal/postprocessor
./internal/protoveneer
./iot
./kms
./language
Expand Down
23 changes: 23 additions & 0 deletions internal/protoveneer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# The protoveneer tool

Protoveneer is an experimental tool that generates idiomatic Go types that
correspond to protocol buffer messages and enums -- a veneer on top of the proto
layer.

## Usage

Call protoveneer with a config file and a directory containing *.pb.go files:

protoveneer config.yaml ../ai/generativelanguage/apiv1beta/generativelanguagepb

That will write Go source code to the current directory, or the one specified by -outdir.

To add a license to the generated code, pass the -license flag with a filename.

See testdata/basic/config.yaml for a sample config file.

The generated code requires the "support" package. Copy this package to your
project and provide its import path as the value of `supportImportPath` in the
config file.


13 changes: 13 additions & 0 deletions internal/protoveneer/cmd/protoveneer/apache-llc.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
108 changes: 108 additions & 0 deletions internal/protoveneer/cmd/protoveneer/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"errors"
"fmt"
"os"

"gopkg.in/yaml.v3"
)

// config holds the configuration for a package.
type config struct {
Package string
ProtoImportPath string `yaml:"protoImportPath"`
// Import path for the support package needed by the generated code.
SupportImportPath string `yaml:"supportImportPath"`

// The types to process. Only these types and the types they depend
// on will be output.
// The key is the name of the proto type.
Types map[string]*typeConfig
// Omit the types in this list, even if they would normally be output.
// Elements can be globs.
OmitTypes []string `yaml:"omitTypes"`
// Converter functions for types not in the proto package.
// Each value should be "tofunc, fromfunc"
Converters map[string]string
}

type typeConfig struct {
// The name for the veneer type, if different.
Name string
// The prefix of the proto enum values. It will be removed.
ProtoPrefix string `yaml:"protoPrefix"`
// The prefix for the veneer enum values, if different from the type name.
VeneerPrefix string `yaml:"veneerPrefix"`
// Overrides for enum values.
ValueNames map[string]string `yaml:"valueNames"`
// Overrides for field types. Map key is proto field name.
Fields map[string]fieldConfig
// Custom conversion functions: "tofunc, fromfunc"
ConvertToFrom string `yaml:"convertToFrom"`
// Doc string for the type, omitting the initial type name.
Doc string
// Verb to place after type name in doc. Default: "is".
// Ignored if Doc is non-empty.
DocVerb string `yaml:"docVerb"`
}

type fieldConfig struct {
Name string // veneer name
Type string // veneer type
// Omit from output.
Omit bool
}

func (c *config) init() {
for protoName, tc := range c.Types {
if tc == nil {
tc = &typeConfig{Name: protoName}
c.Types[protoName] = tc
}
if tc.Name == "" {
tc.Name = protoName
}
tc.init()
}
}

func (tc *typeConfig) init() {
if tc.VeneerPrefix == "" {
tc.VeneerPrefix = tc.Name
}
}

func readConfigFile(filename string) (*config, error) {
if filename == "" {
return nil, errors.New("missing config file")
}
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
dec := yaml.NewDecoder(f)
dec.KnownFields(true)

var c config
if err := dec.Decode(&c); err != nil {
return nil, fmt.Errorf("reading %s: %w", filename, err)
}
c.init()
return &c, nil
}
153 changes: 153 additions & 0 deletions internal/protoveneer/cmd/protoveneer/converters.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import "fmt"

// A converter generates code to convert between a proto type and a veneer type.
type converter interface {
// genFrom returns code to convert from proto to veneer.
genFrom(string) string
// genTo returns code to convert to proto from veneer.
genTo(string) string
// These return the function argument to Transform{Slice,MapValues}, or "" if we don't need it.
genTransformFrom() string
genTransformTo() string
}

// An identityConverter does no conversion.
type identityConverter struct{}

func (identityConverter) genFrom(arg string) string { return arg }
func (identityConverter) genTo(arg string) string { return arg }
func (identityConverter) genTransformFrom() string { return "" }
func (identityConverter) genTransformTo() string { return "" }

// A derefConverter converts between T in the veneer and *T in the proto.
type derefConverter struct{}

func (derefConverter) genFrom(arg string) string { return fmt.Sprintf("support.DerefOrZero(%s)", arg) }
func (derefConverter) genTo(arg string) string { return fmt.Sprintf("support.AddrOrNil(%s)", arg) }
func (derefConverter) genTransformFrom() string { panic("can't handle deref slices") }
func (derefConverter) genTransformTo() string { panic("can't handle deref slices") }

type enumConverter struct {
protoName, veneerName string
}

func (c enumConverter) genFrom(arg string) string {
return fmt.Sprintf("%s(%s)", c.veneerName, arg)
}

func (c enumConverter) genTransformFrom() string {
return fmt.Sprintf("func(p pb.%s) %s { return %s }", c.protoName, c.veneerName, c.genFrom("p"))
}

func (c enumConverter) genTo(arg string) string {
return fmt.Sprintf("pb.%s(%s)", c.protoName, arg)
}

func (c enumConverter) genTransformTo() string {
return fmt.Sprintf("func(v %s) pb.%s { return %s }", c.veneerName, c.protoName, c.genTo("v"))
}

type protoConverter struct {
veneerName string
}

func (c protoConverter) genFrom(arg string) string {
return fmt.Sprintf("(%s{}).fromProto(%s)", c.veneerName, arg)
}

func (c protoConverter) genTransformFrom() string {
return fmt.Sprintf("(%s{}).fromProto", c.veneerName)
}

func (c protoConverter) genTo(arg string) string {
return fmt.Sprintf("%s.toProto()", arg)
}

func (c protoConverter) genTransformTo() string {
return fmt.Sprintf("(*%s).toProto", c.veneerName)
}

type customConverter struct {
toFunc, fromFunc string
}

func (c customConverter) genFrom(arg string) string {
return fmt.Sprintf("%s(%s)", c.fromFunc, arg)
}

func (c customConverter) genTransformFrom() string { return c.fromFunc }

func (c customConverter) genTo(arg string) string {
return fmt.Sprintf("%s(%s)", c.toFunc, arg)
}

func (c customConverter) genTransformTo() string { return c.toFunc }

type sliceConverter struct {
eltConverter converter
}

func (c sliceConverter) genFrom(arg string) string {
if fn := c.eltConverter.genTransformFrom(); fn != "" {
return fmt.Sprintf("support.TransformSlice(%s, %s)", arg, fn)
}
return c.eltConverter.genFrom(arg)
}

func (c sliceConverter) genTo(arg string) string {
if fn := c.eltConverter.genTransformTo(); fn != "" {
return fmt.Sprintf("support.TransformSlice(%s, %s)", arg, fn)
}
return c.eltConverter.genTo(arg)
}

func (c sliceConverter) genTransformTo() string {
panic("sliceConverter.genToSlice called")
}

func (c sliceConverter) genTransformFrom() string {
panic("sliceConverter.genFromSlice called")
}

// Only the values are converted.
type mapConverter struct {
valueConverter converter
}

func (c mapConverter) genFrom(arg string) string {
if fn := c.valueConverter.genTransformFrom(); fn != "" {
return fmt.Sprintf("support.TransformMapValues(%s, %s)", arg, fn)
}
return c.valueConverter.genFrom(arg)
}

func (c mapConverter) genTo(arg string) string {
if fn := c.valueConverter.genTransformTo(); fn != "" {
return fmt.Sprintf("support.TransformMapValues(%s, %s)", arg, fn)
}
return c.valueConverter.genTo(arg)
}

func (c mapConverter) genTransformTo() string {
panic("mapConverter.genToSlice called")
}

func (c mapConverter) genTransformFrom() string {
panic("mapConverter.genFromSlice called")
}
Loading

0 comments on commit b7bf165

Please sign in to comment.