Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

impl(internal/api): create package #338

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Package genclient is a Schema and Language agnostic code generator that applies
// an API model to a mustache template.
package genclient

import (
"io/fs"
"log/slog"
"os"
"path/filepath"
"strings"

"github.com/cbroglie/mustache"
)
package api

// LanguageCodec is an adapter used to transform values into language idiomatic
// representations. This is used to manipulate the data the is fed into
Expand Down Expand Up @@ -127,80 +115,3 @@ type LanguageCodec interface {
// Imports to add.
Imports() []string
}

type ParserOptions struct {
// The location where the specification can be found.
Source string
// The location of the service configuration file.
ServiceConfig string
// Additional options.
Options map[string]string
}

type CodecOptions struct {
// The output location within ProjectRoot.
OutDir string
// Additional options.
Options map[string]string
}

// GenerateRequest used to generate clients.
type GenerateRequest struct {
// The in memory representation of a parsed input.
API *API
// An adapter to transform values into language idiomatic representations.
Codec LanguageCodec
// OutDir is the path to the output directory.
OutDir string
// Template directory
TemplateDir string
}

func (r *GenerateRequest) outDir() string {
if r.OutDir == "" {
wd, _ := os.Getwd()
return wd
}
return r.OutDir
}

// Generate takes some state and applies it to a template to create a client
// library.
func Generate(req *GenerateRequest) error {
data := newTemplateData(req.API, req.Codec)
root := filepath.Join(req.TemplateDir, req.Codec.TemplateDir())
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
dn := filepath.Join(req.outDir(), strings.TrimPrefix(path, root))
os.MkdirAll(dn, 0777) // Ignore errors
return nil
}
if filepath.Ext(path) != ".mustache" {
return nil
}
if strings.Count(d.Name(), ".") == 1 {
// skipping partials
return nil
}
var context []any
context = append(context, data)
if req.Codec.AdditionalContext() != nil {
context = append(context, req.Codec.AdditionalContext())
}
s, err := mustache.RenderFile(path, context...)
if err != nil {
return err
}
fn := filepath.Join(req.outDir(), filepath.Dir(strings.TrimPrefix(path, root)), strings.TrimSuffix(d.Name(), ".mustache"))
return os.WriteFile(fn, []byte(s), os.ModePerm)
})
if err != nil {
slog.Error("error walking templates", "err", err.Error())
return err
}

return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package genclient
// Package api provides an internal representation of a Google API.
package api

import (
"regexp"
Expand Down
14 changes: 7 additions & 7 deletions generator/internal/language/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ package language
import (
"strings"

"github.com/googleapis/google-cloud-rust/generator/internal/genclient"
"github.com/googleapis/google-cloud-rust/generator/internal/api"
)

func newTestAPI(messages []*genclient.Message, enums []*genclient.Enum, services []*genclient.Service) *genclient.API {
state := &genclient.APIState{
MessageByID: make(map[string]*genclient.Message),
EnumByID: make(map[string]*genclient.Enum),
ServiceByID: make(map[string]*genclient.Service),
func newTestAPI(messages []*api.Message, enums []*api.Enum, services []*api.Service) *api.API {
state := &api.APIState{
MessageByID: make(map[string]*api.Message),
EnumByID: make(map[string]*api.Enum),
ServiceByID: make(map[string]*api.Service),
}
for _, m := range messages {
state.MessageByID[m.ID] = m
Expand All @@ -51,7 +51,7 @@ func newTestAPI(messages []*genclient.Message, enums []*genclient.Enum, services
}
}

return &genclient.API{
return &api.API{
Name: "Test",
Messages: messages,
Enums: enums,
Expand Down
62 changes: 31 additions & 31 deletions generator/internal/language/golang.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ import (
"time"
"unicode"

"github.com/googleapis/google-cloud-rust/generator/internal/genclient"
"github.com/googleapis/google-cloud-rust/generator/internal/api"
"github.com/iancoleman/strcase"
)

func NewGoCodec(copts *genclient.CodecOptions) (*GoCodec, error) {
func NewGoCodec(copts *CodecOptions) (*GoCodec, error) {
year, _, _ := time.Now().Date()
codec := &GoCodec{
GenerationYear: fmt.Sprintf("%04d", year),
Expand Down Expand Up @@ -74,13 +74,13 @@ type GoImport struct {
Name string
}

func (c *GoCodec) LoadWellKnownTypes(s *genclient.APIState) {
timestamp := &genclient.Message{
func (c *GoCodec) LoadWellKnownTypes(s *api.APIState) {
timestamp := &api.Message{
ID: ".google.protobuf.Timestamp",
Name: "Time",
Package: "time",
}
duration := &genclient.Message{
duration := &api.Message{
ID: ".google.protobuf.Duration",
Name: "Duration",
Package: "time",
Expand All @@ -89,24 +89,24 @@ func (c *GoCodec) LoadWellKnownTypes(s *genclient.APIState) {
s.MessageByID[duration.ID] = duration
}

func (*GoCodec) FieldAttributes(*genclient.Field, *genclient.APIState) []string {
func (*GoCodec) FieldAttributes(*api.Field, *api.APIState) []string {
return []string{}
}

func (c *GoCodec) FieldType(f *genclient.Field, state *genclient.APIState) string {
func (c *GoCodec) FieldType(f *api.Field, state *api.APIState) string {
var out string
switch f.Typez {
case genclient.STRING_TYPE:
case api.STRING_TYPE:
out = "string"
case genclient.INT64_TYPE:
case api.INT64_TYPE:
out = "int64"
case genclient.INT32_TYPE:
case api.INT32_TYPE:
out = "int32"
case genclient.BOOL_TYPE:
case api.BOOL_TYPE:
out = "bool"
case genclient.BYTES_TYPE:
case api.BYTES_TYPE:
out = "[]byte"
case genclient.MESSAGE_TYPE:
case api.MESSAGE_TYPE:
m, ok := state.MessageByID[f.TypezID]
if !ok {
slog.Error("unable to lookup type", "id", f.TypezID)
Expand All @@ -119,7 +119,7 @@ func (c *GoCodec) FieldType(f *genclient.Field, state *genclient.APIState) strin
break
}
out = "*" + c.MessageName(m, state)
case genclient.ENUM_TYPE:
case api.ENUM_TYPE:
e, ok := state.EnumByID[f.TypezID]
if !ok {
slog.Error("unable to lookup type", "id", f.TypezID)
Expand All @@ -132,15 +132,15 @@ func (c *GoCodec) FieldType(f *genclient.Field, state *genclient.APIState) strin
return out
}

func (c *GoCodec) AsQueryParameter(f *genclient.Field, state *genclient.APIState) string {
func (c *GoCodec) AsQueryParameter(f *api.Field, state *api.APIState) string {
return fmt.Sprintf("req.%s.to_str()", c.ToCamel(f.Name))
}

func (c *GoCodec) TemplateDir() string {
return "go"
}

func (c *GoCodec) MethodInOutTypeName(id string, s *genclient.APIState) string {
func (c *GoCodec) MethodInOutTypeName(id string, s *api.APIState) string {
if id == "" {
return ""
}
Expand All @@ -152,11 +152,11 @@ func (c *GoCodec) MethodInOutTypeName(id string, s *genclient.APIState) string {
return strcase.ToCamel(m.Name)
}

func (*GoCodec) MessageAttributes(*genclient.Message, *genclient.APIState) []string {
func (*GoCodec) MessageAttributes(*api.Message, *api.APIState) []string {
return []string{}
}

func (c *GoCodec) MessageName(m *genclient.Message, state *genclient.APIState) string {
func (c *GoCodec) MessageName(m *api.Message, state *api.APIState) string {
if m.Parent != nil {
return c.MessageName(m.Parent, state) + "_" + strcase.ToCamel(m.Name)
}
Expand All @@ -166,45 +166,45 @@ func (c *GoCodec) MessageName(m *genclient.Message, state *genclient.APIState) s
return c.ToPascal(m.Name)
}

func (c *GoCodec) FQMessageName(m *genclient.Message, state *genclient.APIState) string {
func (c *GoCodec) FQMessageName(m *api.Message, state *api.APIState) string {
return c.MessageName(m, state)
}

func (c *GoCodec) EnumName(e *genclient.Enum, state *genclient.APIState) string {
func (c *GoCodec) EnumName(e *api.Enum, state *api.APIState) string {
if e.Parent != nil {
return c.MessageName(e.Parent, state) + "_" + strcase.ToCamel(e.Name)
}
return strcase.ToCamel(e.Name)
}

func (c *GoCodec) FQEnumName(e *genclient.Enum, state *genclient.APIState) string {
func (c *GoCodec) FQEnumName(e *api.Enum, state *api.APIState) string {
return c.EnumName(e, state)
}

func (c *GoCodec) EnumValueName(e *genclient.EnumValue, state *genclient.APIState) string {
func (c *GoCodec) EnumValueName(e *api.EnumValue, state *api.APIState) string {
if e.Parent.Parent != nil {
return c.MessageName(e.Parent.Parent, state) + "_" + strings.ToUpper(e.Name)
}
return strings.ToUpper(e.Name)
}

func (c *GoCodec) FQEnumValueName(v *genclient.EnumValue, state *genclient.APIState) string {
func (c *GoCodec) FQEnumValueName(v *api.EnumValue, state *api.APIState) string {
return c.EnumValueName(v, state)
}

func (c *GoCodec) OneOfType(o *genclient.OneOf, _ *genclient.APIState) string {
func (c *GoCodec) OneOfType(o *api.OneOf, _ *api.APIState) string {
panic("not needed for Go")
}

func (c *GoCodec) BodyAccessor(m *genclient.Method, state *genclient.APIState) string {
func (c *GoCodec) BodyAccessor(m *api.Method, state *api.APIState) string {
if m.PathInfo.BodyFieldPath == "*" {
// no accessor needed, use the whole request
return ""
}
return "." + strcase.ToCamel(m.PathInfo.BodyFieldPath)
}

func (c *GoCodec) HTTPPathFmt(m *genclient.PathInfo, state *genclient.APIState) string {
func (c *GoCodec) HTTPPathFmt(m *api.PathInfo, state *api.APIState) string {
fmt := ""
for _, segment := range m.PathTemplate {
if segment.Literal != nil {
Expand All @@ -218,7 +218,7 @@ func (c *GoCodec) HTTPPathFmt(m *genclient.PathInfo, state *genclient.APIState)
return fmt
}

func (c *GoCodec) HTTPPathArgs(h *genclient.PathInfo, state *genclient.APIState) []string {
func (c *GoCodec) HTTPPathArgs(h *api.PathInfo, state *api.APIState) []string {
var args []string
// TODO(codyoss): https://github.com/googleapis/google-cloud-rust/issues/34
for _, segment := range h.PathTemplate {
Expand All @@ -230,14 +230,14 @@ func (c *GoCodec) HTTPPathArgs(h *genclient.PathInfo, state *genclient.APIState)
return args
}

func (c *GoCodec) QueryParams(m *genclient.Method, state *genclient.APIState) []*genclient.Field {
func (c *GoCodec) QueryParams(m *api.Method, state *api.APIState) []*api.Field {
msg, ok := state.MessageByID[m.InputTypeID]
if !ok {
slog.Error("unable to lookup type", "id", m.InputTypeID)
return nil
}

var queryParams []*genclient.Field
var queryParams []*api.Field
for _, field := range msg.Fields {
if !m.PathInfo.QueryParameters[field.JSONName] {
continue
Expand Down Expand Up @@ -282,7 +282,7 @@ func (c *GoCodec) CopyrightYear() string {
return c.GenerationYear
}

func (c *GoCodec) PackageName(api *genclient.API) string {
func (c *GoCodec) PackageName(api *api.API) string {
if len(c.PackageNameOverride) > 0 {
return c.PackageNameOverride
}
Expand All @@ -306,7 +306,7 @@ func (c *GoCodec) validatePackageName(newPackage, elementName string) error {
c.SourceSpecificationPackageName, newPackage, elementName)
}

func (c *GoCodec) Validate(api *genclient.API) error {
func (c *GoCodec) Validate(api *api.API) error {
// Set the source package. We should always take the first service registered
// as the source package. Services with mixes will register those after the
// source package.
Expand Down
Loading
Loading