Skip to content
Merged
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
158 changes: 158 additions & 0 deletions cmd/gen-go-sample/inittree.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// Copyright 2018 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
//
// https://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 (
"bufio"
"io"
"strings"
"text/scanner"
"unicode"

"github.com/googleapis/gapic-generator-go/internal/errors"
)

// initTree represents a node in the initialization tree.
type initTree struct {
// T is either a composite value (struct/array/map), where keys and vals are set,
// or a simple value, where leafVal is set.

// Use array representation; we need order, and we probably won't
// have many pairs anyway.
keys []string
vals []*initTree

// Text of the literal. If the literal is a string, it's already quoted.
leafVal string
}

func (t *initTree) get(k string) *initTree {
for i, key := range t.keys {
if k == key {
return t.vals[i]
}
}

v := new(initTree)
t.keys = append(t.keys, k)
t.vals = append(t.vals, v)
return v
}

func (t *initTree) Parse(txt string) error {
var sc scanner.Scanner
sc.Init(strings.NewReader(txt))
sc.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanFloats | scanner.ScanStrings

// Scanner reports error by calling sc.Error; we save the error so we always report
// the first.
var err error
report := func(e error) error {
if err == nil {
err = e
}
return err
}
sc.Error = func(_ *scanner.Scanner, msg string) {
e := errors.E(nil, msg)
e = errors.E(e, "while scaning: %q", txt)
report(e)
}

// Just do simple structs for now.
// TODO(pongad): allow map and array index.
// spec = ident { '.' ident } [ '=' value ] .

if sc.Scan() != scanner.Ident {
return report(errors.E(nil, "expected ident, found %q", sc.TokenText()))
}
t = t.get(sc.TokenText())

for {
switch sc.Scan() {
case '=':
goto equal
case scanner.EOF:
// TODO: set t.leafVal to zero value of type.
return report(nil)

case '.':
if sc.Scan() != scanner.Ident {
return report(errors.E(nil, "expected ident, found %q", sc.TokenText()))
}
t = t.get(sc.TokenText())

default:
return report(errors.E(nil, "unexpected %q", sc.TokenText()))
}
}

// TODO(pongad): check that value and type match
// TODO(pongad): check that we don't write two values onto the same node
// TODO(pongad): handle resource names
// TODO(pongad): properly validate and print enums
equal:
switch r := sc.Scan(); r {
case scanner.Int, scanner.Float, scanner.String, scanner.Ident:
t.leafVal = sc.TokenText()
default:
return report(errors.E(nil, "expected value, found %q", sc.TokenText()))
}

if sc.Scan() != scanner.EOF {
return report(errors.E(nil, "expected EOF, found %q", sc.TokenText()))
}
return err
}

func (t *initTree) Print(w io.Writer) error {
bw := bufio.NewWriter(w)
t.print(bw, 1)
return bw.Flush()
}

func (t *initTree) print(w *bufio.Writer, ind int) {
if v := t.leafVal; v != "" {
w.WriteString(v)
return
}

// TODO(pongad): Figure out how to print type.
w.WriteString("TYPE{\n")
for i, k := range t.keys {
for i := 0; i < ind; i++ {
w.WriteByte('\t')
}
snakeToCapCamel(w, k)
w.WriteString(": ")
t.vals[i].print(w, ind+1)
w.WriteString(",\n")
}
w.WriteString("}")
}

func snakeToCapCamel(w *bufio.Writer, s string) {
cap := true
for _, r := range s {
if r == '_' {
cap = true
} else if cap {
cap = false
w.WriteRune(unicode.ToUpper(r))
} else {
w.WriteRune(r)
}
}
}
65 changes: 65 additions & 0 deletions cmd/gen-go-sample/inittree_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2018 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
//
// https://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 (
"strings"
"testing"
)

func TestTree(t *testing.T) {
specs := []string{
`a.b = 1`,
`a.c = "xyz"`,
`a.d = 2.718281828`,
`x = 123`,
}

var root initTree
for _, s := range specs {
if err := root.Parse(s); err != nil {
t.Fatal(err)
}
}

for _, tst := range []struct {
path []string
val string
}{
{[]string{"a", "b"}, "1"},
{[]string{"a", "c"}, `"xyz"`},
{[]string{"a", "d"}, "2.718281828"},
{[]string{"x"}, "123"},
} {
node := &root
for _, p := range tst.path {
node = node.get(p)
}
if node.leafVal != tst.val {
t.Errorf("%s = %q, want %q", strings.Join(tst.path, "->"), node.leafVal, tst.val)
}
}

if t.Failed() {
t.SkipNow()
}

var buf strings.Builder
buf.WriteByte('\n')
if err := root.Print(&buf); err != nil {
t.Error(err)
}
t.Log(buf.String())
}
24 changes: 19 additions & 5 deletions cmd/gen-go-sample/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func main() {
log.Fatal(errors.E(err, "value set: %q", vs))
}
if err := gen.commit(!*nofmt); err != nil {
log.Fatal(errors.E(err, "value set: %q", vs))
log.Fatal(errors.E(err, "can't commit value set: %q", vs))
}
}
}
Expand Down Expand Up @@ -161,7 +161,7 @@ func (g *generator) commit(gofmt bool) error {
if gofmt {
b2, err := format.Source(b)
if err != nil {
return err
return errors.E(err, "syntax error, run with -nofmt to find out why?")
}
b = b2
}
Expand Down Expand Up @@ -214,11 +214,25 @@ func (g *generator) genSample(ifaceName, methName, regTag string, valSet SampleV
return errors.E(err, "can't import input type: %q", inType)
}

p(" req := %s.%s{", inSpec.Name, inType.GetName())
var itree initTree
for _, def := range valSet.Parameters.Defaults {
p("// %s", def)
if err := itree.Parse(def); err != nil {
return errors.E(err, "can't set default value: %q", def)
}
}
{
w := g.pt.Writer()

if _, err := w.Write([]byte("req := ")); err != nil {
return err
}
if err := itree.Print(g.pt.Writer()); err != nil {
return err
}
if _, err := w.Write([]byte{'\n'}); err != nil {
return err
}
}
p(" }")

// TODO(pongad): handle non-unary
p(" resp, err := c.%s(ctx, req)", methName)
Expand Down
6 changes: 5 additions & 1 deletion cmd/gen-go-sample/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ func TestSample(t *testing.T) {
vs := SampleValueSet{
ID: "my_value_set",
Parameters: SampleParameter{
Defaults: []string{"foo=bar"},
Defaults: []string{
`a.x = 42`,
`a.y = 3.14159`,
`b = "foobar"`,
},
},
}
if err := g.genSample("MyService", "MyMethod", "awesome_region", vs); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/gen-go-sample/test.bash
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ if [ -z $COMMON_PROTO ]; then
exit 1
fi

./gen-go-sample \
./gen-go-sample $* \
-gapic "$GOOGLEAPIS/google/cloud/language/v1/language_gapic.yaml" \
-desc <(protoc -o /dev/stdout --include_imports -I "$COMMON_PROTO" -I "$GOOGLEAPIS" "$GOOGLEAPIS"/google/cloud/language/v1/*.proto)
10 changes: 7 additions & 3 deletions cmd/gen-go-sample/testdata/sample.want
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ func sampleMyMethod() {
ctx := context.Background()
c := mypackagepb.NewClient(ctx)

req := mypackagepb.InputType{
// foo=bar
}
req := TYPE{
A: TYPE{
X: 42,
Y: 3.14159,
},
B: "foobar",
}
resp, err := c.MyMethod(ctx, req)
if err != nil {
// TODO: Handle error.
Expand Down
6 changes: 6 additions & 0 deletions internal/printer/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package printer
import (
"bytes"
"fmt"
"io"
"strings"
)

Expand Down Expand Up @@ -65,6 +66,11 @@ func (p *P) Printf(s string, args ...interface{}) {
}
}

// Writer returns a writer that writes to p's underlying buffer without performing indentation.
func (p *P) Writer() io.Writer {
return &p.buf
}

// Bytes returns the bytes written by Printf. It is valid up to the next call to Printf or Reset.
func (p *P) Bytes() []byte {
return p.buf.Bytes()
Expand Down