func goTypeWithPackage(f *descriptor.FieldDescriptorProto) string {
	pkg := ""
	if *f.Type == descriptor.FieldDescriptorProto_TYPE_MESSAGE || *f.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
		if isTimestampPackage(*f.TypeName) {
			pkg = timestamp
		} else {
			pkg = getPackageTypeName(*f.TypeName)
		}
	}
	return goType(pkg, f)
}
Warning does not handle message embedded like goTypeWithGoPackage does.
package pgghelpers
import (
	"encoding/json"
	"fmt"
	"reflect"
	"regexp"
	"strings"
	"sync"
	"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  
)
const timestamp = "timestamp"
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]
	},
	"concat": func(a string, b ...string) string {
		return strings.Join(append([]string{a}, b...), "")
	},
	"join": func(sep string, a ...string) string {
		return strings.Join(a, sep)
	},
	"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:]
	},
	"upperCase": func(s string) string {
		return strings.ToUpper(s)
	},
	"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 int) interface{} {
		slice := reflect.ValueOf(array)
		if slice.Kind() != reflect.Slice {
			panic("Error in index(): given a non-slice type")
		}
		if i < 0 || i >= slice.Len() {
			panic("Error in index(): index out of bounds")
		}
		return slice.Index(i).Interface()
	},
	"add": func(a int, b int) int {
		return a + b
	},
	"subtract": func(a int, b int) int {
		return a - b
	},
	"multiply": func(a int, b int) int {
		return a * b
	},
	"divide": func(a int, b int) int {
		if b == 0 {
			panic("psssst ... little help here ... you cannot divide by 0")
		}
		return a / b
	},
	"snakeCase":                    xstrings.ToSnakeCase,
	"getProtoFile":                 getProtoFile,
	"getMessageType":               getMessageType,
	"getEnumValue":                 getEnumValue,
	"isFieldMessage":               isFieldMessage,
	"isFieldMessageTimeStamp":      isFieldMessageTimeStamp,
	"isFieldRepeated":              isFieldRepeated,
	"haskellType":                  haskellType,
	"goType":                       goType,
	"goZeroValue":                  goZeroValue,
	"goTypeWithPackage":            goTypeWithPackage,
	"goTypeWithGoPackage":          goTypeWithGoPackage,
	"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,
	"stringFileOptionsExtension":   stringFileOptionsExtension,
	"stringMessageExtension":       stringMessageExtension,
	"stringFieldExtension":         stringFieldExtension,
	"int64FieldExtension":          int64FieldExtension,
	"int64MessageExtension":        int64MessageExtension,
	"stringMethodOptionsExtension": stringMethodOptionsExtension,
	"boolMethodOptionsExtension":   boolMethodOptionsExtension,
	"boolMessageExtension":         boolMessageExtension,
	"boolFieldExtension":           boolFieldExtension,
	"isFieldMap":                   isFieldMap,
	"fieldMapKeyType":              fieldMapKeyType,
	"fieldMapValueType":            fieldMapValueType,
	"replaceDict":                  replaceDict,
	"setStore":                     setStore,
	"getStore":                     getStore,
	"goPkg":                        goPkg,
	"goPkgLastElement":             goPkgLastElement,
	"cppType":                      cppType,
	"cppTypeWithPackage":           cppTypeWithPackage,
	"rustType":                     rustType,
	"rustTypeWithPackage":          rustTypeWithPackage,
}
var pathMap map[interface{}]*descriptor.SourceCodeInfo_Location
var store = newStore()
type globalStore struct {
	store map[string]interface{}
	mu    sync.Mutex
}
func newStore() *globalStore {
	return &globalStore{
		store: make(map[string]interface{}),
	}
}
func (s *globalStore) getData(key string) interface{} {
	s.mu.Lock()
	defer s.mu.Unlock()
	if v, ok := s.store[key]; ok {
		return v
	}
	return false
}
func (s *globalStore) setData(key string, o interface{}) {
	s.mu.Lock()
	s.store[key] = o
	s.mu.Unlock()
}
func setStore(key string, o interface{}) string {
	store.setData(key, o)
	return ""
}
func getStore(key string) interface{} {
	return store.getData(key)
}
func SetRegistry(reg *ggdescriptor.Registry) {
	registry = reg
}
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{})
	}
}
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 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 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
	}
	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 goTypeWithGoPackage(p *descriptor.FileDescriptorProto, f *descriptor.FieldDescriptorProto) string {
	pkg := ""
	if *f.Type == descriptor.FieldDescriptorProto_TYPE_MESSAGE || *f.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
		if isTimestampPackage(*f.TypeName) {
			pkg = timestamp
		} else {
			pkg = *p.GetOptions().GoPackage
			if strings.Contains(*p.GetOptions().GoPackage, ";") {
				pkg = strings.Split(*p.GetOptions().GoPackage, ";")[1]
			}
		}
	}
	return goTypeWithEmbedded(pkg, f, p)
}