870 lines
22 KiB
Go
870 lines
22 KiB
Go
package pgghelpers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
"regexp"
|
|
"strings"
|
|
"text/template"
|
|
|
|
"github.com/Masterminds/sprig"
|
|
"github.com/golang/protobuf/proto"
|
|
"github.com/golang/protobuf/protoc-gen-go/descriptor"
|
|
ggdescriptor "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
|
|
"github.com/huandu/xstrings"
|
|
options "google.golang.org/genproto/googleapis/api/annotations"
|
|
)
|
|
|
|
var jsReservedRe = regexp.MustCompile(`(^|[^A-Za-z])(do|if|in|for|let|new|try|var|case|else|enum|eval|false|null|this|true|void|with|break|catch|class|const|super|throw|while|yield|delete|export|import|public|return|static|switch|typeof|default|extends|finally|package|private|continue|debugger|function|arguments|interface|protected|implements|instanceof)($|[^A-Za-z])`)
|
|
|
|
var (
|
|
registry *ggdescriptor.Registry // some helpers need access to registry
|
|
)
|
|
|
|
func SetRegistry(reg *ggdescriptor.Registry) {
|
|
registry = reg
|
|
}
|
|
|
|
var ProtoHelpersFuncMap = template.FuncMap{
|
|
"string": func(i interface {
|
|
String() string
|
|
}) string {
|
|
return i.String()
|
|
},
|
|
"json": func(v interface{}) string {
|
|
a, err := json.Marshal(v)
|
|
if err != nil {
|
|
return err.Error()
|
|
}
|
|
return string(a)
|
|
},
|
|
"prettyjson": func(v interface{}) string {
|
|
a, err := json.MarshalIndent(v, "", " ")
|
|
if err != nil {
|
|
return err.Error()
|
|
}
|
|
return string(a)
|
|
},
|
|
"splitArray": func(sep string, s string) []interface{} {
|
|
var r []interface{}
|
|
t := strings.Split(s, sep)
|
|
for i := range t {
|
|
if t[i] != "" {
|
|
r = append(r, t[i])
|
|
}
|
|
}
|
|
return r
|
|
},
|
|
"first": func(a []string) string {
|
|
return a[0]
|
|
},
|
|
"last": func(a []string) string {
|
|
return a[len(a)-1]
|
|
},
|
|
"upperFirst": func(s string) string {
|
|
return strings.ToUpper(s[:1]) + s[1:]
|
|
},
|
|
"lowerFirst": func(s string) string {
|
|
return strings.ToLower(s[:1]) + s[1:]
|
|
},
|
|
"camelCase": func(s string) string {
|
|
if len(s) > 1 {
|
|
return xstrings.ToCamelCase(s)
|
|
}
|
|
|
|
return strings.ToUpper(s[:1])
|
|
},
|
|
"lowerCamelCase": func(s string) string {
|
|
if len(s) > 1 {
|
|
s = xstrings.ToCamelCase(s)
|
|
}
|
|
|
|
return strings.ToLower(s[:1]) + s[1:]
|
|
},
|
|
"kebabCase": func(s string) string {
|
|
return strings.Replace(xstrings.ToSnakeCase(s), "_", "-", -1)
|
|
},
|
|
"contains": func(sub, s string) bool {
|
|
return strings.Contains(s, sub)
|
|
},
|
|
"trimstr": func(cutset, s string) string {
|
|
return strings.Trim(s, cutset)
|
|
},
|
|
"index": func(array interface{}, i int32) interface{} {
|
|
slice := reflect.ValueOf(array)
|
|
if slice.Kind() != reflect.Slice {
|
|
panic("Error in index(): given a non-slice type")
|
|
}
|
|
if i < 0 || int(i) >= slice.Len() {
|
|
panic("Error in index(): index out of bounds")
|
|
}
|
|
return slice.Index(int(i)).Interface()
|
|
},
|
|
"snakeCase": xstrings.ToSnakeCase,
|
|
"getProtoFile": getProtoFile,
|
|
"getMessageType": getMessageType,
|
|
"getEnumValue": getEnumValue,
|
|
"isFieldMessage": isFieldMessage,
|
|
"isFieldMessageTimeStamp": isFieldMessageTimeStamp,
|
|
"isFieldRepeated": isFieldRepeated,
|
|
"haskellType": haskellType,
|
|
"goType": goType,
|
|
"goZeroValue": goZeroValue,
|
|
"goTypeWithPackage": goTypeWithPackage,
|
|
"jsType": jsType,
|
|
"jsSuffixReserved": jsSuffixReservedKeyword,
|
|
"namespacedFlowType": namespacedFlowType,
|
|
"httpVerb": httpVerb,
|
|
"httpPath": httpPath,
|
|
"httpPathsAdditionalBindings": httpPathsAdditionalBindings,
|
|
"httpBody": httpBody,
|
|
"shortType": shortType,
|
|
"urlHasVarsFromMessage": urlHasVarsFromMessage,
|
|
"lowerGoNormalize": lowerGoNormalize,
|
|
"goNormalize": goNormalize,
|
|
"leadingComment": leadingComment,
|
|
"trailingComment": trailingComment,
|
|
"leadingDetachedComments": leadingDetachedComments,
|
|
"stringFieldExtension": stringFieldExtension,
|
|
"boolFieldExtension": boolFieldExtension,
|
|
"isFieldMap": isFieldMap,
|
|
"fieldMapKeyType": fieldMapKeyType,
|
|
"fieldMapValueType": fieldMapValueType,
|
|
"replaceDict": replaceDict,
|
|
}
|
|
|
|
var pathMap map[interface{}]*descriptor.SourceCodeInfo_Location
|
|
|
|
func InitPathMap(file *descriptor.FileDescriptorProto) {
|
|
pathMap = make(map[interface{}]*descriptor.SourceCodeInfo_Location)
|
|
addToPathMap(file.GetSourceCodeInfo(), file, []int32{})
|
|
}
|
|
|
|
func InitPathMaps(files []*descriptor.FileDescriptorProto) {
|
|
pathMap = make(map[interface{}]*descriptor.SourceCodeInfo_Location)
|
|
for _, file := range files {
|
|
addToPathMap(file.GetSourceCodeInfo(), file, []int32{})
|
|
}
|
|
}
|
|
|
|
// addToPathMap traverses through the AST adding SourceCodeInfo_Location entries to the pathMap.
|
|
// Since the AST is a tree, the recursion finishes once it has gone through all the nodes.
|
|
func addToPathMap(info *descriptor.SourceCodeInfo, i interface{}, path []int32) {
|
|
loc := findLoc(info, path)
|
|
if loc != nil {
|
|
pathMap[i] = loc
|
|
}
|
|
switch d := i.(type) {
|
|
case *descriptor.FileDescriptorProto:
|
|
for index, descriptor := range d.MessageType {
|
|
addToPathMap(info, descriptor, newPath(path, 4, index))
|
|
}
|
|
for index, descriptor := range d.EnumType {
|
|
addToPathMap(info, descriptor, newPath(path, 5, index))
|
|
}
|
|
for index, descriptor := range d.Service {
|
|
addToPathMap(info, descriptor, newPath(path, 6, index))
|
|
}
|
|
case *descriptor.DescriptorProto:
|
|
for index, descriptor := range d.Field {
|
|
addToPathMap(info, descriptor, newPath(path, 2, index))
|
|
}
|
|
for index, descriptor := range d.NestedType {
|
|
addToPathMap(info, descriptor, newPath(path, 3, index))
|
|
}
|
|
for index, descriptor := range d.EnumType {
|
|
addToPathMap(info, descriptor, newPath(path, 4, index))
|
|
}
|
|
case *descriptor.EnumDescriptorProto:
|
|
for index, descriptor := range d.Value {
|
|
addToPathMap(info, descriptor, newPath(path, 2, index))
|
|
}
|
|
case *descriptor.ServiceDescriptorProto:
|
|
for index, descriptor := range d.Method {
|
|
addToPathMap(info, descriptor, newPath(path, 2, index))
|
|
}
|
|
}
|
|
}
|
|
|
|
func newPath(base []int32, field int32, index int) []int32 {
|
|
p := append([]int32{}, base...)
|
|
p = append(p, field, int32(index))
|
|
return p
|
|
}
|
|
|
|
func findLoc(info *descriptor.SourceCodeInfo, path []int32) *descriptor.SourceCodeInfo_Location {
|
|
for _, loc := range info.GetLocation() {
|
|
if samePath(loc.Path, path) {
|
|
return loc
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func samePath(a, b []int32) bool {
|
|
if len(a) != len(b) {
|
|
return false
|
|
}
|
|
for i, p := range a {
|
|
if p != b[i] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func findSourceInfoLocation(i interface{}) *descriptor.SourceCodeInfo_Location {
|
|
if pathMap == nil {
|
|
return nil
|
|
}
|
|
return pathMap[i]
|
|
}
|
|
|
|
func leadingComment(i interface{}) string {
|
|
loc := pathMap[i]
|
|
return loc.GetLeadingComments()
|
|
}
|
|
func trailingComment(i interface{}) string {
|
|
loc := pathMap[i]
|
|
return loc.GetTrailingComments()
|
|
}
|
|
func leadingDetachedComments(i interface{}) []string {
|
|
loc := pathMap[i]
|
|
return loc.GetLeadingDetachedComments()
|
|
}
|
|
|
|
func stringFieldExtension(fieldID int32, f *descriptor.FieldDescriptorProto) string {
|
|
if f == nil {
|
|
return ""
|
|
}
|
|
if f.Options == nil {
|
|
return ""
|
|
}
|
|
var extendedType *descriptor.FieldOptions
|
|
var extensionType *string
|
|
|
|
eds := proto.RegisteredExtensions(f.Options)
|
|
if eds[fieldID] == nil {
|
|
ed := &proto.ExtensionDesc{
|
|
ExtendedType: extendedType,
|
|
ExtensionType: extensionType,
|
|
Field: fieldID,
|
|
Tag: fmt.Sprintf("bytes,%d", fieldID),
|
|
}
|
|
proto.RegisterExtension(ed)
|
|
eds = proto.RegisteredExtensions(f.Options)
|
|
}
|
|
|
|
ext, err := proto.GetExtension(f.Options, eds[fieldID])
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
|
|
str, ok := ext.(*string)
|
|
if !ok {
|
|
return ""
|
|
}
|
|
|
|
return *str
|
|
}
|
|
|
|
func boolFieldExtension(fieldID int32, f *descriptor.FieldDescriptorProto) bool {
|
|
if f == nil {
|
|
return false
|
|
}
|
|
if f.Options == nil {
|
|
return false
|
|
}
|
|
var extendedType *descriptor.FieldOptions
|
|
var extensionType *bool
|
|
|
|
eds := proto.RegisteredExtensions(f.Options)
|
|
if eds[fieldID] == nil {
|
|
ed := &proto.ExtensionDesc{
|
|
ExtendedType: extendedType,
|
|
ExtensionType: extensionType,
|
|
Field: fieldID,
|
|
Tag: fmt.Sprintf("varint,%d", fieldID),
|
|
}
|
|
proto.RegisterExtension(ed)
|
|
eds = proto.RegisteredExtensions(f.Options)
|
|
}
|
|
|
|
ext, err := proto.GetExtension(f.Options, eds[fieldID])
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
str, ok := ext.(*bool)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
return *str
|
|
}
|
|
|
|
func init() {
|
|
for k, v := range sprig.TxtFuncMap() {
|
|
ProtoHelpersFuncMap[k] = v
|
|
}
|
|
}
|
|
|
|
func getProtoFile(name string) *ggdescriptor.File {
|
|
if registry == nil {
|
|
return nil
|
|
}
|
|
file, err := registry.LookupFile(name)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return file
|
|
}
|
|
|
|
func getMessageType(f *descriptor.FileDescriptorProto, name string) *ggdescriptor.Message {
|
|
if registry != nil {
|
|
msg, err := registry.LookupMsg(".", name)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return msg
|
|
}
|
|
|
|
// name is in the form .packageName.MessageTypeName.InnerMessageTypeName...
|
|
// e.g. .article.ProductTag
|
|
splits := strings.Split(name, ".")
|
|
target := splits[len(splits)-1]
|
|
for _, m := range f.MessageType {
|
|
if target == *m.Name {
|
|
return &ggdescriptor.Message{
|
|
DescriptorProto: m,
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getEnumValue(f []*descriptor.EnumDescriptorProto, name string) []*descriptor.EnumValueDescriptorProto {
|
|
for _, item := range f {
|
|
if strings.EqualFold(*item.Name, name) {
|
|
return item.GetValue()
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func isFieldMessageTimeStamp(f *descriptor.FieldDescriptorProto) bool {
|
|
if f.Type != nil && *f.Type == descriptor.FieldDescriptorProto_TYPE_MESSAGE {
|
|
if strings.Compare(*f.TypeName, ".google.protobuf.Timestamp") == 0 {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isFieldMessage(f *descriptor.FieldDescriptorProto) bool {
|
|
if f.Type != nil && *f.Type == descriptor.FieldDescriptorProto_TYPE_MESSAGE {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func isFieldRepeated(f *descriptor.FieldDescriptorProto) bool {
|
|
if f == nil {
|
|
return false
|
|
}
|
|
if f.Type != nil && f.Label != nil && *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func isFieldMap(f *descriptor.FieldDescriptorProto, m *descriptor.DescriptorProto) bool {
|
|
if f.TypeName == nil {
|
|
return false
|
|
}
|
|
|
|
shortName := shortType(*f.TypeName)
|
|
var nt *descriptor.DescriptorProto
|
|
for _, t := range m.NestedType {
|
|
if *t.Name == shortName {
|
|
nt = t
|
|
break
|
|
}
|
|
}
|
|
|
|
if nt == nil {
|
|
return false
|
|
}
|
|
|
|
for _, f := range nt.Field {
|
|
switch *f.Name {
|
|
case "key":
|
|
if *f.Number != 1 {
|
|
return false
|
|
}
|
|
case "value":
|
|
if *f.Number != 2 {
|
|
return false
|
|
}
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func fieldMapKeyType(f *descriptor.FieldDescriptorProto, m *descriptor.DescriptorProto) *descriptor.FieldDescriptorProto {
|
|
if f.TypeName == nil {
|
|
return nil
|
|
}
|
|
|
|
shortName := shortType(*f.TypeName)
|
|
var nt *descriptor.DescriptorProto
|
|
for _, t := range m.NestedType {
|
|
if *t.Name == shortName {
|
|
nt = t
|
|
break
|
|
}
|
|
}
|
|
|
|
if nt == nil {
|
|
return nil
|
|
}
|
|
|
|
for _, f := range nt.Field {
|
|
if *f.Name == "key" {
|
|
return f
|
|
}
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func fieldMapValueType(f *descriptor.FieldDescriptorProto, m *descriptor.DescriptorProto) *descriptor.FieldDescriptorProto {
|
|
if f.TypeName == nil {
|
|
return nil
|
|
}
|
|
|
|
shortName := shortType(*f.TypeName)
|
|
var nt *descriptor.DescriptorProto
|
|
for _, t := range m.NestedType {
|
|
if *t.Name == shortName {
|
|
nt = t
|
|
break
|
|
}
|
|
}
|
|
|
|
if nt == nil {
|
|
return nil
|
|
}
|
|
|
|
for _, f := range nt.Field {
|
|
if *f.Name == "value" {
|
|
return f
|
|
}
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func goTypeWithPackage(f *descriptor.FieldDescriptorProto) string {
|
|
pkg := ""
|
|
if *f.Type == descriptor.FieldDescriptorProto_TYPE_MESSAGE || *f.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
|
|
pkg = getPackageTypeName(*f.TypeName)
|
|
}
|
|
return goType(pkg, f)
|
|
}
|
|
|
|
func haskellType(pkg string, f *descriptor.FieldDescriptorProto) string {
|
|
switch *f.Type {
|
|
case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
|
|
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return "[Float]"
|
|
}
|
|
return "Float"
|
|
case descriptor.FieldDescriptorProto_TYPE_FLOAT:
|
|
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return "[Float]"
|
|
}
|
|
return "Float"
|
|
case descriptor.FieldDescriptorProto_TYPE_INT64:
|
|
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return "[Int64]"
|
|
}
|
|
return "Int64"
|
|
case descriptor.FieldDescriptorProto_TYPE_UINT64:
|
|
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return "[Word]"
|
|
}
|
|
return "Word"
|
|
case descriptor.FieldDescriptorProto_TYPE_INT32:
|
|
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return "[Int]"
|
|
}
|
|
return "Int"
|
|
case descriptor.FieldDescriptorProto_TYPE_UINT32:
|
|
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return "[Word]"
|
|
}
|
|
return "Word"
|
|
case descriptor.FieldDescriptorProto_TYPE_BOOL:
|
|
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return "[Bool]"
|
|
}
|
|
return "Bool"
|
|
case descriptor.FieldDescriptorProto_TYPE_STRING:
|
|
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return "[Text]"
|
|
}
|
|
return "Text"
|
|
case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
|
|
if pkg != "" {
|
|
pkg = pkg + "."
|
|
}
|
|
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return fmt.Sprintf("[%s%s]", pkg, shortType(*f.TypeName))
|
|
}
|
|
return fmt.Sprintf("%s%s", pkg, shortType(*f.TypeName))
|
|
case descriptor.FieldDescriptorProto_TYPE_BYTES:
|
|
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return "[Word8]"
|
|
}
|
|
return "Word8"
|
|
case descriptor.FieldDescriptorProto_TYPE_ENUM:
|
|
return fmt.Sprintf("%s%s", pkg, shortType(*f.TypeName))
|
|
default:
|
|
return "Generic"
|
|
}
|
|
}
|
|
|
|
func goType(pkg string, f *descriptor.FieldDescriptorProto) string {
|
|
if pkg != "" {
|
|
pkg = pkg + "."
|
|
}
|
|
switch *f.Type {
|
|
case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
|
|
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return "[]float64"
|
|
}
|
|
return "float64"
|
|
case descriptor.FieldDescriptorProto_TYPE_FLOAT:
|
|
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return "[]float32"
|
|
}
|
|
return "float32"
|
|
case descriptor.FieldDescriptorProto_TYPE_INT64:
|
|
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return "[]int64"
|
|
}
|
|
return "int64"
|
|
case descriptor.FieldDescriptorProto_TYPE_UINT64:
|
|
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return "[]uint64"
|
|
}
|
|
return "uint64"
|
|
case descriptor.FieldDescriptorProto_TYPE_INT32:
|
|
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return "[]int32"
|
|
}
|
|
return "int32"
|
|
case descriptor.FieldDescriptorProto_TYPE_UINT32:
|
|
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return "[]uint32"
|
|
}
|
|
return "uint32"
|
|
case descriptor.FieldDescriptorProto_TYPE_BOOL:
|
|
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return "[]bool"
|
|
}
|
|
return "bool"
|
|
case descriptor.FieldDescriptorProto_TYPE_STRING:
|
|
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return "[]string"
|
|
}
|
|
return "string"
|
|
case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
|
|
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return fmt.Sprintf("[]*%s%s", pkg, shortType(*f.TypeName))
|
|
}
|
|
return fmt.Sprintf("*%s%s", pkg, shortType(*f.TypeName))
|
|
case descriptor.FieldDescriptorProto_TYPE_BYTES:
|
|
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return "[]byte"
|
|
}
|
|
return "byte"
|
|
case descriptor.FieldDescriptorProto_TYPE_ENUM:
|
|
return fmt.Sprintf("*%s%s", pkg, shortType(*f.TypeName))
|
|
default:
|
|
return "interface{}"
|
|
}
|
|
}
|
|
|
|
func goZeroValue(f *descriptor.FieldDescriptorProto) string {
|
|
const nilString = "nil"
|
|
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
|
|
return nilString
|
|
}
|
|
switch *f.Type {
|
|
case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
|
|
return "0.0"
|
|
case descriptor.FieldDescriptorProto_TYPE_FLOAT:
|
|
return "0.0"
|
|
case descriptor.FieldDescriptorProto_TYPE_INT64:
|
|
return "0"
|
|
case descriptor.FieldDescriptorProto_TYPE_UINT64:
|
|
return "0"
|
|
case descriptor.FieldDescriptorProto_TYPE_INT32:
|
|
return "0"
|
|
case descriptor.FieldDescriptorProto_TYPE_UINT32:
|
|
return "0"
|
|
case descriptor.FieldDescriptorProto_TYPE_BOOL:
|
|
return "false"
|
|
case descriptor.FieldDescriptorProto_TYPE_STRING:
|
|
return "\"\""
|
|
case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
|
|
return nilString
|
|
case descriptor.FieldDescriptorProto_TYPE_BYTES:
|
|
return "0"
|
|
case descriptor.FieldDescriptorProto_TYPE_ENUM:
|
|
return nilString
|
|
default:
|
|
return nilString
|
|
}
|
|
}
|
|
|
|
func jsType(f *descriptor.FieldDescriptorProto) string {
|
|
template := "%s"
|
|
if isFieldRepeated(f) {
|
|
template = "Array<%s>"
|
|
}
|
|
|
|
switch *f.Type {
|
|
case descriptor.FieldDescriptorProto_TYPE_MESSAGE,
|
|
descriptor.FieldDescriptorProto_TYPE_ENUM:
|
|
return fmt.Sprintf(template, namespacedFlowType(*f.TypeName))
|
|
case descriptor.FieldDescriptorProto_TYPE_DOUBLE,
|
|
descriptor.FieldDescriptorProto_TYPE_FLOAT,
|
|
descriptor.FieldDescriptorProto_TYPE_INT64,
|
|
descriptor.FieldDescriptorProto_TYPE_UINT64,
|
|
descriptor.FieldDescriptorProto_TYPE_INT32,
|
|
descriptor.FieldDescriptorProto_TYPE_FIXED64,
|
|
descriptor.FieldDescriptorProto_TYPE_FIXED32,
|
|
descriptor.FieldDescriptorProto_TYPE_UINT32,
|
|
descriptor.FieldDescriptorProto_TYPE_SFIXED32,
|
|
descriptor.FieldDescriptorProto_TYPE_SFIXED64,
|
|
descriptor.FieldDescriptorProto_TYPE_SINT32,
|
|
descriptor.FieldDescriptorProto_TYPE_SINT64:
|
|
return fmt.Sprintf(template, "number")
|
|
case descriptor.FieldDescriptorProto_TYPE_BOOL:
|
|
return fmt.Sprintf(template, "boolean")
|
|
case descriptor.FieldDescriptorProto_TYPE_BYTES:
|
|
return fmt.Sprintf(template, "Uint8Array")
|
|
case descriptor.FieldDescriptorProto_TYPE_STRING:
|
|
return fmt.Sprintf(template, "string")
|
|
default:
|
|
return fmt.Sprintf(template, "any")
|
|
}
|
|
}
|
|
|
|
func jsSuffixReservedKeyword(s string) string {
|
|
return jsReservedRe.ReplaceAllString(s, "${1}${2}_${3}")
|
|
}
|
|
|
|
func getPackageTypeName(s string) string {
|
|
if strings.Compare(s, ".google.protobuf.Timestamp") == 0 {
|
|
return "timestamp"
|
|
}
|
|
if strings.Contains(s, ".") {
|
|
return strings.Split(s, ".")[1]
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func shortType(s string) string {
|
|
t := strings.Split(s, ".")
|
|
return t[len(t)-1]
|
|
}
|
|
|
|
func namespacedFlowType(s string) string {
|
|
trimmed := strings.TrimLeft(s, ".")
|
|
splitted := strings.Split(trimmed, ".")
|
|
return strings.Join(splitted, "$")
|
|
}
|
|
|
|
func httpPath(m *descriptor.MethodDescriptorProto) string {
|
|
|
|
ext, err := proto.GetExtension(m.Options, options.E_Http)
|
|
if err != nil {
|
|
return err.Error()
|
|
}
|
|
opts, ok := ext.(*options.HttpRule)
|
|
if !ok {
|
|
return fmt.Sprintf("extension is %T; want an HttpRule", ext)
|
|
}
|
|
|
|
switch t := opts.Pattern.(type) {
|
|
default:
|
|
return ""
|
|
case *options.HttpRule_Get:
|
|
return t.Get
|
|
case *options.HttpRule_Post:
|
|
return t.Post
|
|
case *options.HttpRule_Put:
|
|
return t.Put
|
|
case *options.HttpRule_Delete:
|
|
return t.Delete
|
|
case *options.HttpRule_Patch:
|
|
return t.Patch
|
|
case *options.HttpRule_Custom:
|
|
return t.Custom.Path
|
|
}
|
|
}
|
|
|
|
func httpPathsAdditionalBindings(m *descriptor.MethodDescriptorProto) []string {
|
|
ext, err := proto.GetExtension(m.Options, options.E_Http)
|
|
if err != nil {
|
|
panic(err.Error())
|
|
}
|
|
opts, ok := ext.(*options.HttpRule)
|
|
if !ok {
|
|
panic(fmt.Sprintf("extension is %T; want an HttpRule", ext))
|
|
}
|
|
|
|
var httpPaths []string
|
|
var optsAdditionalBindings = opts.GetAdditionalBindings()
|
|
for _, optAdditionalBindings := range optsAdditionalBindings {
|
|
switch t := optAdditionalBindings.Pattern.(type) {
|
|
case *options.HttpRule_Get:
|
|
httpPaths = append(httpPaths, t.Get)
|
|
case *options.HttpRule_Post:
|
|
httpPaths = append(httpPaths, t.Post)
|
|
case *options.HttpRule_Put:
|
|
httpPaths = append(httpPaths, t.Put)
|
|
case *options.HttpRule_Delete:
|
|
httpPaths = append(httpPaths, t.Delete)
|
|
case *options.HttpRule_Patch:
|
|
httpPaths = append(httpPaths, t.Patch)
|
|
case *options.HttpRule_Custom:
|
|
httpPaths = append(httpPaths, t.Custom.Path)
|
|
default:
|
|
// nothing
|
|
}
|
|
}
|
|
|
|
return httpPaths
|
|
}
|
|
|
|
func httpVerb(m *descriptor.MethodDescriptorProto) string {
|
|
|
|
ext, err := proto.GetExtension(m.Options, options.E_Http)
|
|
if err != nil {
|
|
return err.Error()
|
|
}
|
|
opts, ok := ext.(*options.HttpRule)
|
|
if !ok {
|
|
return fmt.Sprintf("extension is %T; want an HttpRule", ext)
|
|
}
|
|
|
|
switch t := opts.Pattern.(type) {
|
|
default:
|
|
return ""
|
|
case *options.HttpRule_Get:
|
|
return "GET"
|
|
case *options.HttpRule_Post:
|
|
return "POST"
|
|
case *options.HttpRule_Put:
|
|
return "PUT"
|
|
case *options.HttpRule_Delete:
|
|
return "DELETE"
|
|
case *options.HttpRule_Patch:
|
|
return "PATCH"
|
|
case *options.HttpRule_Custom:
|
|
return t.Custom.Kind
|
|
}
|
|
}
|
|
|
|
func httpBody(m *descriptor.MethodDescriptorProto) string {
|
|
|
|
ext, err := proto.GetExtension(m.Options, options.E_Http)
|
|
if err != nil {
|
|
return err.Error()
|
|
}
|
|
opts, ok := ext.(*options.HttpRule)
|
|
if !ok {
|
|
return fmt.Sprintf("extension is %T; want an HttpRule", ext)
|
|
}
|
|
return opts.Body
|
|
}
|
|
|
|
func urlHasVarsFromMessage(path string, d *ggdescriptor.Message) bool {
|
|
for _, field := range d.Field {
|
|
if !isFieldMessage(field) {
|
|
if strings.Contains(path, fmt.Sprintf("{%s}", *field.Name)) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// lowerGoNormalize takes a string and applies formatting
|
|
// rules to conform to Golang convention. It applies a camel
|
|
// case filter, lowers the first character and formats fields
|
|
// with `id` to `ID`.
|
|
func lowerGoNormalize(s string) string {
|
|
fmtd := xstrings.ToCamelCase(s)
|
|
fmtd = xstrings.FirstRuneToLower(fmtd)
|
|
return formatID(s, fmtd)
|
|
}
|
|
|
|
// goNormalize takes a string and applies formatting rules
|
|
// to conform to Golang convention. It applies a camel case
|
|
// filter and formats fields with `id` to `ID`.
|
|
func goNormalize(s string) string {
|
|
fmtd := xstrings.ToCamelCase(s)
|
|
return formatID(s, fmtd)
|
|
}
|
|
|
|
// formatID takes a base string alonsgide a formatted string.
|
|
// It acts as a transformation filter for fields containing
|
|
// `id` in order to conform to Golang convention.
|
|
func formatID(base string, formatted string) string {
|
|
if formatted == "" {
|
|
return formatted
|
|
}
|
|
switch {
|
|
case base == "id":
|
|
// id -> ID
|
|
return "ID"
|
|
case strings.HasPrefix(base, "id_"):
|
|
// id_some -> IDSome
|
|
return "ID" + formatted[2:]
|
|
case strings.HasSuffix(base, "_id"):
|
|
// some_id -> SomeID
|
|
return formatted[:len(formatted)-2] + "ID"
|
|
case strings.HasSuffix(base, "_ids"):
|
|
// some_ids -> SomeIDs
|
|
return formatted[:len(formatted)-3] + "IDs"
|
|
}
|
|
return formatted
|
|
}
|
|
|
|
func replaceDict(src string, dict map[string]interface{}) string {
|
|
for old, v := range dict {
|
|
new, ok := v.(string)
|
|
if !ok {
|
|
continue
|
|
}
|
|
src = strings.Replace(src, old, new, -1)
|
|
}
|
|
return src
|
|
}
|