323 lines
11 KiB
Go
323 lines
11 KiB
Go
package descriptor
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
|
|
gogen "github.com/golang/protobuf/protoc-gen-go/generator"
|
|
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/httprule"
|
|
)
|
|
|
|
// GoPackage represents a golang package
|
|
type GoPackage struct {
|
|
// Path is the package path to the package.
|
|
Path string
|
|
// Name is the package name of the package
|
|
Name string
|
|
// Alias is an alias of the package unique within the current invokation of grpc-gateway generator.
|
|
Alias string
|
|
}
|
|
|
|
// Standard returns whether the import is a golang standard package.
|
|
func (p GoPackage) Standard() bool {
|
|
return !strings.Contains(p.Path, ".")
|
|
}
|
|
|
|
// String returns a string representation of this package in the form of import line in golang.
|
|
func (p GoPackage) String() string {
|
|
if p.Alias == "" {
|
|
return fmt.Sprintf("%q", p.Path)
|
|
}
|
|
return fmt.Sprintf("%s %q", p.Alias, p.Path)
|
|
}
|
|
|
|
// File wraps descriptor.FileDescriptorProto for richer features.
|
|
type File struct {
|
|
*descriptor.FileDescriptorProto
|
|
// GoPkg is the go package of the go file generated from this file..
|
|
GoPkg GoPackage
|
|
// Messages is the list of messages defined in this file.
|
|
Messages []*Message
|
|
// Enums is the list of enums defined in this file.
|
|
Enums []*Enum
|
|
// Services is the list of services defined in this file.
|
|
Services []*Service
|
|
}
|
|
|
|
// proto2 determines if the syntax of the file is proto2.
|
|
func (f *File) proto2() bool {
|
|
return f.Syntax == nil || f.GetSyntax() == "proto2"
|
|
}
|
|
|
|
// Message describes a protocol buffer message types
|
|
type Message struct {
|
|
// File is the file where the message is defined
|
|
File *File
|
|
// Outers is a list of outer messages if this message is a nested type.
|
|
Outers []string
|
|
*descriptor.DescriptorProto
|
|
Fields []*Field
|
|
|
|
// Index is proto path index of this message in File.
|
|
Index int
|
|
}
|
|
|
|
// FQMN returns a fully qualified message name of this message.
|
|
func (m *Message) FQMN() string {
|
|
components := []string{""}
|
|
if m.File.Package != nil {
|
|
components = append(components, m.File.GetPackage())
|
|
}
|
|
components = append(components, m.Outers...)
|
|
components = append(components, m.GetName())
|
|
return strings.Join(components, ".")
|
|
}
|
|
|
|
// GoType returns a go type name for the message type.
|
|
// It prefixes the type name with the package alias if
|
|
// its belonging package is not "currentPackage".
|
|
func (m *Message) GoType(currentPackage string) string {
|
|
var components []string
|
|
components = append(components, m.Outers...)
|
|
components = append(components, m.GetName())
|
|
|
|
name := strings.Join(components, "_")
|
|
if m.File.GoPkg.Path == currentPackage {
|
|
return name
|
|
}
|
|
pkg := m.File.GoPkg.Name
|
|
if alias := m.File.GoPkg.Alias; alias != "" {
|
|
pkg = alias
|
|
}
|
|
return fmt.Sprintf("%s.%s", pkg, name)
|
|
}
|
|
|
|
// Enum describes a protocol buffer enum types
|
|
type Enum struct {
|
|
// File is the file where the enum is defined
|
|
File *File
|
|
// Outers is a list of outer messages if this enum is a nested type.
|
|
Outers []string
|
|
*descriptor.EnumDescriptorProto
|
|
|
|
Index int
|
|
}
|
|
|
|
// FQEN returns a fully qualified enum name of this enum.
|
|
func (e *Enum) FQEN() string {
|
|
components := []string{""}
|
|
if e.File.Package != nil {
|
|
components = append(components, e.File.GetPackage())
|
|
}
|
|
components = append(components, e.Outers...)
|
|
components = append(components, e.GetName())
|
|
return strings.Join(components, ".")
|
|
}
|
|
|
|
// Service wraps descriptor.ServiceDescriptorProto for richer features.
|
|
type Service struct {
|
|
// File is the file where this service is defined.
|
|
File *File
|
|
*descriptor.ServiceDescriptorProto
|
|
// Methods is the list of methods defined in this service.
|
|
Methods []*Method
|
|
}
|
|
|
|
// Method wraps descriptor.MethodDescriptorProto for richer features.
|
|
type Method struct {
|
|
// Service is the service which this method belongs to.
|
|
Service *Service
|
|
*descriptor.MethodDescriptorProto
|
|
|
|
// RequestType is the message type of requests to this method.
|
|
RequestType *Message
|
|
// ResponseType is the message type of responses from this method.
|
|
ResponseType *Message
|
|
Bindings []*Binding
|
|
}
|
|
|
|
// Binding describes how an HTTP endpoint is bound to a gRPC method.
|
|
type Binding struct {
|
|
// Method is the method which the endpoint is bound to.
|
|
Method *Method
|
|
// Index is a zero-origin index of the binding in the target method
|
|
Index int
|
|
// PathTmpl is path template where this method is mapped to.
|
|
PathTmpl httprule.Template
|
|
// HTTPMethod is the HTTP method which this method is mapped to.
|
|
HTTPMethod string
|
|
// PathParams is the list of parameters provided in HTTP request paths.
|
|
PathParams []Parameter
|
|
// Body describes parameters provided in HTTP request body.
|
|
Body *Body
|
|
}
|
|
|
|
// ExplicitParams returns a list of explicitly bound parameters of "b",
|
|
// i.e. a union of field path for body and field paths for path parameters.
|
|
func (b *Binding) ExplicitParams() []string {
|
|
var result []string
|
|
if b.Body != nil {
|
|
result = append(result, b.Body.FieldPath.String())
|
|
}
|
|
for _, p := range b.PathParams {
|
|
result = append(result, p.FieldPath.String())
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Field wraps descriptor.FieldDescriptorProto for richer features.
|
|
type Field struct {
|
|
// Message is the message type which this field belongs to.
|
|
Message *Message
|
|
// FieldMessage is the message type of the field.
|
|
FieldMessage *Message
|
|
*descriptor.FieldDescriptorProto
|
|
}
|
|
|
|
// Parameter is a parameter provided in http requests
|
|
type Parameter struct {
|
|
// FieldPath is a path to a proto field which this parameter is mapped to.
|
|
FieldPath
|
|
// Target is the proto field which this parameter is mapped to.
|
|
Target *Field
|
|
// Method is the method which this parameter is used for.
|
|
Method *Method
|
|
}
|
|
|
|
// ConvertFuncExpr returns a go expression of a converter function.
|
|
// The converter function converts a string into a value for the parameter.
|
|
func (p Parameter) ConvertFuncExpr() (string, error) {
|
|
tbl := proto3ConvertFuncs
|
|
if p.Target.Message.File.proto2() {
|
|
tbl = proto2ConvertFuncs
|
|
}
|
|
typ := p.Target.GetType()
|
|
conv, ok := tbl[typ]
|
|
if !ok {
|
|
return "", fmt.Errorf("unsupported field type %s of parameter %s in %s.%s", typ, p.FieldPath, p.Method.Service.GetName(), p.Method.GetName())
|
|
}
|
|
return conv, nil
|
|
}
|
|
|
|
// Body describes a http requtest body to be sent to the method.
|
|
type Body struct {
|
|
// FieldPath is a path to a proto field which the request body is mapped to.
|
|
// The request body is mapped to the request type itself if FieldPath is empty.
|
|
FieldPath FieldPath
|
|
}
|
|
|
|
// RHS returns a right-hand-side expression in go to be used to initialize method request object.
|
|
// It starts with "msgExpr", which is the go expression of the method request object.
|
|
func (b Body) RHS(msgExpr string) string {
|
|
return b.FieldPath.RHS(msgExpr)
|
|
}
|
|
|
|
// FieldPath is a path to a field from a request message.
|
|
type FieldPath []FieldPathComponent
|
|
|
|
// String returns a string representation of the field path.
|
|
func (p FieldPath) String() string {
|
|
var components []string
|
|
for _, c := range p {
|
|
components = append(components, c.Name)
|
|
}
|
|
return strings.Join(components, ".")
|
|
}
|
|
|
|
// IsNestedProto3 indicates whether the FieldPath is a nested Proto3 path.
|
|
func (p FieldPath) IsNestedProto3() bool {
|
|
if len(p) > 1 && !p[0].Target.Message.File.proto2() {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// RHS is a right-hand-side expression in go to be used to assign a value to the target field.
|
|
// It starts with "msgExpr", which is the go expression of the method request object.
|
|
func (p FieldPath) RHS(msgExpr string) string {
|
|
l := len(p)
|
|
if l == 0 {
|
|
return msgExpr
|
|
}
|
|
components := []string{msgExpr}
|
|
for i, c := range p {
|
|
if i == l-1 {
|
|
components = append(components, c.RHS())
|
|
continue
|
|
}
|
|
components = append(components, c.LHS())
|
|
}
|
|
return strings.Join(components, ".")
|
|
}
|
|
|
|
// FieldPathComponent is a path component in FieldPath
|
|
type FieldPathComponent struct {
|
|
// Name is a name of the proto field which this component corresponds to.
|
|
// TODO(yugui) is this necessary?
|
|
Name string
|
|
// Target is the proto field which this component corresponds to.
|
|
Target *Field
|
|
}
|
|
|
|
// RHS returns a right-hand-side expression in go for this field.
|
|
func (c FieldPathComponent) RHS() string {
|
|
return gogen.CamelCase(c.Name)
|
|
}
|
|
|
|
// LHS returns a left-hand-side expression in go for this field.
|
|
func (c FieldPathComponent) LHS() string {
|
|
if c.Target.Message.File.proto2() {
|
|
return fmt.Sprintf("Get%s()", gogen.CamelCase(c.Name))
|
|
}
|
|
return gogen.CamelCase(c.Name)
|
|
}
|
|
|
|
var (
|
|
proto3ConvertFuncs = map[descriptor.FieldDescriptorProto_Type]string{
|
|
descriptor.FieldDescriptorProto_TYPE_DOUBLE: "runtime.Float64",
|
|
descriptor.FieldDescriptorProto_TYPE_FLOAT: "runtime.Float32",
|
|
descriptor.FieldDescriptorProto_TYPE_INT64: "runtime.Int64",
|
|
descriptor.FieldDescriptorProto_TYPE_UINT64: "runtime.Uint64",
|
|
descriptor.FieldDescriptorProto_TYPE_INT32: "runtime.Int32",
|
|
descriptor.FieldDescriptorProto_TYPE_FIXED64: "runtime.Uint64",
|
|
descriptor.FieldDescriptorProto_TYPE_FIXED32: "runtime.Uint32",
|
|
descriptor.FieldDescriptorProto_TYPE_BOOL: "runtime.Bool",
|
|
descriptor.FieldDescriptorProto_TYPE_STRING: "runtime.String",
|
|
// FieldDescriptorProto_TYPE_GROUP
|
|
// FieldDescriptorProto_TYPE_MESSAGE
|
|
// FieldDescriptorProto_TYPE_BYTES
|
|
// TODO(yugui) Handle bytes
|
|
descriptor.FieldDescriptorProto_TYPE_UINT32: "runtime.Uint32",
|
|
// FieldDescriptorProto_TYPE_ENUM
|
|
// TODO(yugui) Handle Enum
|
|
descriptor.FieldDescriptorProto_TYPE_SFIXED32: "runtime.Int32",
|
|
descriptor.FieldDescriptorProto_TYPE_SFIXED64: "runtime.Int64",
|
|
descriptor.FieldDescriptorProto_TYPE_SINT32: "runtime.Int32",
|
|
descriptor.FieldDescriptorProto_TYPE_SINT64: "runtime.Int64",
|
|
}
|
|
|
|
proto2ConvertFuncs = map[descriptor.FieldDescriptorProto_Type]string{
|
|
descriptor.FieldDescriptorProto_TYPE_DOUBLE: "runtime.Float64P",
|
|
descriptor.FieldDescriptorProto_TYPE_FLOAT: "runtime.Float32P",
|
|
descriptor.FieldDescriptorProto_TYPE_INT64: "runtime.Int64P",
|
|
descriptor.FieldDescriptorProto_TYPE_UINT64: "runtime.Uint64P",
|
|
descriptor.FieldDescriptorProto_TYPE_INT32: "runtime.Int32P",
|
|
descriptor.FieldDescriptorProto_TYPE_FIXED64: "runtime.Uint64P",
|
|
descriptor.FieldDescriptorProto_TYPE_FIXED32: "runtime.Uint32P",
|
|
descriptor.FieldDescriptorProto_TYPE_BOOL: "runtime.BoolP",
|
|
descriptor.FieldDescriptorProto_TYPE_STRING: "runtime.StringP",
|
|
// FieldDescriptorProto_TYPE_GROUP
|
|
// FieldDescriptorProto_TYPE_MESSAGE
|
|
// FieldDescriptorProto_TYPE_BYTES
|
|
// TODO(yugui) Handle bytes
|
|
descriptor.FieldDescriptorProto_TYPE_UINT32: "runtime.Uint32P",
|
|
// FieldDescriptorProto_TYPE_ENUM
|
|
// TODO(yugui) Handle Enum
|
|
descriptor.FieldDescriptorProto_TYPE_SFIXED32: "runtime.Int32P",
|
|
descriptor.FieldDescriptorProto_TYPE_SFIXED64: "runtime.Int64P",
|
|
descriptor.FieldDescriptorProto_TYPE_SINT32: "runtime.Int32P",
|
|
descriptor.FieldDescriptorProto_TYPE_SINT64: "runtime.Int64P",
|
|
}
|
|
)
|