Repository: googleapis/gapic-generator-go
Pull request #26 (2018-10-05 00:38:35)

sample: make CLI type aware


Patch:
diff --git a/cmd/gen-go-sample/inittree.go b/cmd/gen-go-sample/inittree.go
index be5bcec..793b256 100644
--- a/cmd/gen-go-sample/inittree.go
+++ b/cmd/gen-go-sample/inittree.go
@@ -34,10 +34,10 @@ import (
 // initType treats a type in a way similar to how Go treats them,
 // so that it's easy to generate initialization code.
 type initType struct {
-	// If the type is a message, desc describes the type.
-	// If the type is a leaf value, valValid reports whether the token is valid for the type.
-	desc     pbinfo.ProtoType
-	valValid func(string) bool
+	// If the type is a protobuf "compound type", desc describes the type.
+	// If the type is a primitive, prim tells us what the type is.
+	desc pbinfo.ProtoType
+	prim descriptor.FieldDescriptorProto_Type
 
 	// valFmt, if not nil, post-processes values to be included into init struct.
 	// NOTE(pongad): This func signature might seem too general. I think it is just general enough
@@ -83,29 +83,17 @@ func (t *initTree) get(k string, info pbinfo.Info) (*initTree, error) {
 
 		if tn := f.GetTypeName(); tn == "" {
 			// We're a primitive type.
-
-			// Since the tokens are given to us by scanner, they must already be a valid token of some type,
-			// no need to check exhaustively.
-			switch f.GetType() {
-			case descriptor.FieldDescriptorProto_TYPE_BOOL:
-				v.typ.valValid = func(s string) bool { return s == "true" || s == "false" }
-			case descriptor.FieldDescriptorProto_TYPE_BYTES, descriptor.FieldDescriptorProto_TYPE_STRING:
-				v.typ.valValid = func(s string) bool { return s[0] == '"' }
-			case descriptor.FieldDescriptorProto_TYPE_DOUBLE, descriptor.FieldDescriptorProto_TYPE_FLOAT:
-				v.typ.valValid = func(s string) bool { return s == "inf" || s == "nan" || strings.Trim(s, "+-.0123456789") == "" }
-			default:
-				v.typ.valValid = func(s string) bool { return strings.Trim(s, "0123456789") == "" }
-			}
+			v.typ.prim = f.GetType()
 		} else if typ := info.Type[tn]; typ == nil {
 			return nil, errors.E(nil, "cannot find descriptor of %q", f.GetTypeName())
-		} else if enum, ok := typ.(*descriptor.EnumDescriptorProto); ok {
-			v.typ.valValid, v.typ.valFmt = describeEnum(info, enum)
 		} else {
 			// type is a message
 			v.typ.desc = typ
 		}
+
+		break
 	}
-	if v.typ.desc == nil && v.typ.valValid == nil {
+	if v.typ.desc == nil && v.typ.prim == 0 {
 		return nil, errors.E(nil, "type %q does not have field %q", t.typ.desc.GetName(), k)
 	}
 
@@ -138,16 +126,27 @@ func (t *initTree) parseInit(txt string, info pbinfo.Info) error {
 		if lv := t.leafVal; lv != "" {
 			return report(errors.E(nil, "value already set to %q", lv))
 		}
-		if t.typ.valValid == nil {
-			return report(errors.E(nil, "not a leaf field"))
-		}
 
 		tok := sc.TokenText()
-		if !t.typ.valValid(tok) {
-			// TODO(pongad): we should probably tell user what type the field is.
-			return report(errors.E(nil, "invalid value for type: %q", tok))
+
+		if enum, ok := t.typ.desc.(*descriptor.EnumDescriptorProto); ok {
+			validEnum, eFmt := describeEnum(info, enum)
+			if !validEnum(tok) {
+				return report(errors.E(nil, "invalid value for type %q: %q", enum.GetName(), tok))
+			}
+			t.typ.valFmt = eFmt
+		} else {
+			pType := t.typ.prim
+			validPrim := validPrims[pType]
+			if validPrim == nil {
+				return report(errors.E(nil, "not a primitive type? %q", pType))
+			}
+			if !validPrim(tok) {
+				return report(errors.E(nil, "invalid value for type %q: %q", pType, tok))
+			}
 		}
 		t.leafVal = tok
+
 	default:
 		return report(errors.E(nil, "expected value, found %q", sc.TokenText()))
 	}
diff --git a/cmd/gen-go-sample/main.go b/cmd/gen-go-sample/main.go
index bdcf833..61cdda9 100644
--- a/cmd/gen-go-sample/main.go
+++ b/cmd/gen-go-sample/main.go
@@ -245,7 +245,11 @@ func (g *generator) genSample(ifaceName, methName, regTag string, valSet SampleV
 
 	var argStr string
 	if len(argNames) > 0 {
-		argStr = strings.Join(argNames, ",") + " string"
+		var sb strings.Builder
+		for i, n := range argNames {
+			fmt.Fprintf(&sb, ", %s %s", n, pbinfo.GoTypeForPrim[argTrees[i].typ.prim])
+		}
+		argStr = sb.String()[2:]
 	}
 
 	p("func sample%s(%s) {", methName, argStr)
@@ -305,8 +309,16 @@ func (g *generator) genSample(ifaceName, methName, regTag string, valSet SampleV
 	p("")
 
 	p("func main() {")
+
+	for i, name := range argNames {
+		// TODO(pongad): some types, like int32, are not supported by flag package.
+		// We have to convert.
+		typ := pbinfo.GoTypeForPrim[argTrees[i].typ.prim]
+		p(`%s := flag.%s(%q, %s, "")`, name, snakeToCapCamel(typ), name, argTrees[i].leafVal)
+	}
+
 	p("  flag.Parse()")
-	p("  sample%s(%s)", methName, flagArgs(len(argNames)))
+	p("  sample%s(%s)", methName, flagArgs(argNames))
 	p("}")
 	p("")
 
@@ -316,14 +328,14 @@ func (g *generator) genSample(ifaceName, methName, regTag string, valSet SampleV
 	return nil
 }
 
-func flagArgs(n int) string {
-	if n == 0 {
+func flagArgs(names []string) string {
+	if len(names) == 0 {
 		return ""
 	}
 
 	var sb strings.Builder
-	for i := 0; i < n; i++ {
-		fmt.Fprintf(&sb, ", flag.Arg(%d)", i)
+	for _, n := range names {
+		fmt.Fprintf(&sb, ", *%s", n)
 	}
 	return sb.String()[2:]
 }
diff --git a/cmd/gen-go-sample/primval.go b/cmd/gen-go-sample/primval.go
new file mode 100644
index 0000000..fb6d723
--- /dev/null
+++ b/cmd/gen-go-sample/primval.go
@@ -0,0 +1,57 @@
+// 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"
+
+	"github.com/golang/protobuf/protoc-gen-go/descriptor"
+)
+
+// validPrims contains functions to check value-validity for primitive types.
+// Functions only perform cursory checking; since we use scanner for tokenizing,
+// they must already be a valid token of some type.
+var validPrims = [...]func(string) bool{
+	descriptor.FieldDescriptorProto_TYPE_BOOL: func(s string) bool { return s == "true" || s == "false" },
+
+	descriptor.FieldDescriptorProto_TYPE_BYTES:  validStrings,
+	descriptor.FieldDescriptorProto_TYPE_STRING: validStrings,
+
+	descriptor.FieldDescriptorProto_TYPE_DOUBLE: validFloat,
+	descriptor.FieldDescriptorProto_TYPE_FLOAT:  validFloat,
+
+	descriptor.FieldDescriptorProto_TYPE_INT64:    validInt,
+	descriptor.FieldDescriptorProto_TYPE_UINT64:   validInt,
+	descriptor.FieldDescriptorProto_TYPE_INT32:    validInt,
+	descriptor.FieldDescriptorProto_TYPE_FIXED64:  validInt,
+	descriptor.FieldDescriptorProto_TYPE_FIXED32:  validInt,
+	descriptor.FieldDescriptorProto_TYPE_UINT32:   validInt,
+	descriptor.FieldDescriptorProto_TYPE_SFIXED32: validInt,
+	descriptor.FieldDescriptorProto_TYPE_SFIXED64: validInt,
+	descriptor.FieldDescriptorProto_TYPE_SINT32:   validInt,
+	descriptor.FieldDescriptorProto_TYPE_SINT64:   validInt,
+}
+
+func validStrings(s string) bool {
+	return s[0] == '"'
+}
+
+func validFloat(s string) bool {
+	return strings.Trim(s, "+-.0123456789") == ""
+}
+
+func validInt(s string) bool {
+	return strings.Trim(s, "+-0123456789") == ""
+}
diff --git a/internal/gengapic/paging.go b/internal/gengapic/paging.go
index be25836..1a50cfa 100644
--- a/internal/gengapic/paging.go
+++ b/internal/gengapic/paging.go
@@ -23,24 +23,6 @@ import (
 	"github.com/googleapis/gapic-generator-go/internal/pbinfo"
 )
 
-var primitiveFieldToGoType = [...]string{
-	descriptor.FieldDescriptorProto_TYPE_DOUBLE:   "float64",
-	descriptor.FieldDescriptorProto_TYPE_FLOAT:    "float32",
-	descriptor.FieldDescriptorProto_TYPE_INT64:    "int64",
-	descriptor.FieldDescriptorProto_TYPE_UINT64:   "uint64",
-	descriptor.FieldDescriptorProto_TYPE_INT32:    "int32",
-	descriptor.FieldDescriptorProto_TYPE_FIXED64:  "uint64",
-	descriptor.FieldDescriptorProto_TYPE_FIXED32:  "uint32",
-	descriptor.FieldDescriptorProto_TYPE_BOOL:     "bool",
-	descriptor.FieldDescriptorProto_TYPE_STRING:   "string",
-	descriptor.FieldDescriptorProto_TYPE_BYTES:    "[]byte",
-	descriptor.FieldDescriptorProto_TYPE_UINT32:   "uint32",
-	descriptor.FieldDescriptorProto_TYPE_SFIXED32: "int32",
-	descriptor.FieldDescriptorProto_TYPE_SFIXED64: "int64",
-	descriptor.FieldDescriptorProto_TYPE_SINT32:   "int32",
-	descriptor.FieldDescriptorProto_TYPE_SINT64:   "int64",
-}
-
 // iterType describes iterators used by paging RPCs.
 type iterType struct {
 	iterTypeName, elemTypeName string
@@ -76,11 +58,12 @@ func (g *generator) iterTypeOf(elemField *descriptor.FieldDescriptorProto) (iter
 		pt.elemTypeName = "[]byte"
 		pt.iterTypeName = "BytesIterator"
 
-	case t < 0 || int(t) >= len(primitiveFieldToGoType) || primitiveFieldToGoType[t] == "":
-		log.Panicf("unrecognized type: %v", t)
-
 	default:
-		pt.elemTypeName = primitiveFieldToGoType[t]
+		pType := pbinfo.GoTypeForPrim[t]
+		if pType == "" {
+			log.Panicf("unrecognized type: %v", t)
+		}
+		pt.elemTypeName = pType
 		pt.iterTypeName = upperFirst(pt.elemTypeName) + "Iterator"
 	}
 	return pt, nil
diff --git a/internal/pbinfo/prim2go.go b/internal/pbinfo/prim2go.go
new file mode 100644
index 0000000..9629844
--- /dev/null
+++ b/internal/pbinfo/prim2go.go
@@ -0,0 +1,35 @@
+// 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 pbinfo
+
+import "github.com/golang/protobuf/protoc-gen-go/descriptor"
+
+var GoTypeForPrim = map[descriptor.FieldDescriptorProto_Type]string{
+	descriptor.FieldDescriptorProto_TYPE_DOUBLE:   "float64",
+	descriptor.FieldDescriptorProto_TYPE_FLOAT:    "float32",
+	descriptor.FieldDescriptorProto_TYPE_INT64:    "int64",
+	descriptor.FieldDescriptorProto_TYPE_UINT64:   "uint64",
+	descriptor.FieldDescriptorProto_TYPE_INT32:    "int32",
+	descriptor.FieldDescriptorProto_TYPE_FIXED64:  "uint64",
+	descriptor.FieldDescriptorProto_TYPE_FIXED32:  "uint32",
+	descriptor.FieldDescriptorProto_TYPE_BOOL:     "bool",
+	descriptor.FieldDescriptorProto_TYPE_STRING:   "string",
+	descriptor.FieldDescriptorProto_TYPE_BYTES:    "[]byte",
+	descriptor.FieldDescriptorProto_TYPE_UINT32:   "uint32",
+	descriptor.FieldDescriptorProto_TYPE_SFIXED32: "int32",
+	descriptor.FieldDescriptorProto_TYPE_SFIXED64: "int64",
+	descriptor.FieldDescriptorProto_TYPE_SINT32:   "int32",
+	descriptor.FieldDescriptorProto_TYPE_SINT64:   "int64",
+}


Test patch:
diff --git a/cmd/gen-go-sample/main_test.go b/cmd/gen-go-sample/main_test.go
index c2660af..1784fbe 100644
--- a/cmd/gen-go-sample/main_test.go
+++ b/cmd/gen-go-sample/main_test.go
@@ -124,6 +124,7 @@ func TestSample(t *testing.T) {
 			},
 			Attributes: []SampleAttribute{
 				{"a.x", true},
+				{"b", true},
 			},
 		},
 	}
diff --git a/cmd/gen-go-sample/testdata/sample.want b/cmd/gen-go-sample/testdata/sample.want
index 0006387..a6d0d85 100644
--- a/cmd/gen-go-sample/testdata/sample.want
+++ b/cmd/gen-go-sample/testdata/sample.want
@@ -26,7 +26,7 @@ foopb "path.to/pb/foo"
 )
 // [START awesome_region]
 
-func sampleMyMethod(arg0 string) {
+func sampleMyMethod(arg0 int64, arg1 string) {
 	ctx := context.Background()
 	c, err := foo.NewClient(ctx)
 	if err != nil {
@@ -34,12 +34,13 @@ func sampleMyMethod(arg0 string) {
 	}
 
 // arg0 := 42
+// arg1 := "foobar"
 req := &foopb.InputType{
 	A: &foopb.AType{
 		X: arg0,
 		Y: 3.14159,
 	},
-	B: "foobar",
+	B: arg1,
 	E: foopb.AType_FOO,
 	Group: &foopb.InputType_F{
 		F: "in a oneof",
@@ -55,7 +56,9 @@ req := &foopb.InputType{
 // [END awesome_region]
 
 func main() {
+	arg0 := flag.Int64("arg0", 42, "")
+	arg1 := flag.String("arg1", "foobar", "")
 	flag.Parse()
-	sampleMyMethod(flag.Arg(0))
+	sampleMyMethod(*arg0, *arg1)
 }