306 lines
8.9 KiB
Go
306 lines
8.9 KiB
Go
// Copyright 2010 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package proto
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
|
|
"google.golang.org/protobuf/reflect/protoreflect"
|
|
"google.golang.org/protobuf/runtime/protoimpl"
|
|
)
|
|
|
|
// StructProperties represents protocol buffer type information for a
|
|
// generated protobuf message in the open-struct API.
|
|
//
|
|
// Deprecated: Do not use.
|
|
type StructProperties struct {
|
|
// Prop are the properties for each field.
|
|
//
|
|
// Fields belonging to a oneof are stored in OneofTypes instead, with a
|
|
// single Properties representing the parent oneof held here.
|
|
//
|
|
// The order of Prop matches the order of fields in the Go struct.
|
|
// Struct fields that are not related to protobufs have a "XXX_" prefix
|
|
// in the Properties.Name and must be ignored by the user.
|
|
Prop []*Properties
|
|
|
|
// OneofTypes contains information about the oneof fields in this message.
|
|
// It is keyed by the protobuf field name.
|
|
OneofTypes map[string]*OneofProperties
|
|
}
|
|
|
|
// Properties represents the type information for a protobuf message field.
|
|
//
|
|
// Deprecated: Do not use.
|
|
type Properties struct {
|
|
// Name is a placeholder name with little meaningful semantic value.
|
|
// If the name has an "XXX_" prefix, the entire Properties must be ignored.
|
|
Name string
|
|
// OrigName is the protobuf field name or oneof name.
|
|
OrigName string
|
|
// JSONName is the JSON name for the protobuf field.
|
|
JSONName string
|
|
// Enum is a placeholder name for enums.
|
|
// For historical reasons, this is neither the Go name for the enum,
|
|
// nor the protobuf name for the enum.
|
|
Enum string // Deprecated: Do not use.
|
|
// Weak contains the full name of the weakly referenced message.
|
|
Weak string
|
|
// Wire is a string representation of the wire type.
|
|
Wire string
|
|
// WireType is the protobuf wire type for the field.
|
|
WireType int
|
|
// Tag is the protobuf field number.
|
|
Tag int
|
|
// Required reports whether this is a required field.
|
|
Required bool
|
|
// Optional reports whether this is a optional field.
|
|
Optional bool
|
|
// Repeated reports whether this is a repeated field.
|
|
Repeated bool
|
|
// Packed reports whether this is a packed repeated field of scalars.
|
|
Packed bool
|
|
// Proto3 reports whether this field operates under the proto3 syntax.
|
|
Proto3 bool
|
|
// Oneof reports whether this field belongs within a oneof.
|
|
Oneof bool
|
|
|
|
// Default is the default value in string form.
|
|
Default string
|
|
// HasDefault reports whether the field has a default value.
|
|
HasDefault bool
|
|
|
|
// MapKeyProp is the properties for the key field for a map field.
|
|
MapKeyProp *Properties
|
|
// MapValProp is the properties for the value field for a map field.
|
|
MapValProp *Properties
|
|
}
|
|
|
|
// OneofProperties represents the type information for a protobuf oneof.
|
|
//
|
|
// Deprecated: Do not use.
|
|
type OneofProperties struct {
|
|
// Type is a pointer to the generated wrapper type for the field value.
|
|
// This is nil for messages that are not in the open-struct API.
|
|
Type reflect.Type
|
|
// Field is the index into StructProperties.Prop for the containing oneof.
|
|
Field int
|
|
// Prop is the properties for the field.
|
|
Prop *Properties
|
|
}
|
|
|
|
// String formats the properties in the protobuf struct field tag style.
|
|
func (p *Properties) String() string {
|
|
s := p.Wire
|
|
s += "," + strconv.Itoa(p.Tag)
|
|
if p.Required {
|
|
s += ",req"
|
|
}
|
|
if p.Optional {
|
|
s += ",opt"
|
|
}
|
|
if p.Repeated {
|
|
s += ",rep"
|
|
}
|
|
if p.Packed {
|
|
s += ",packed"
|
|
}
|
|
s += ",name=" + p.OrigName
|
|
if p.JSONName != "" {
|
|
s += ",json=" + p.JSONName
|
|
}
|
|
if len(p.Enum) > 0 {
|
|
s += ",enum=" + p.Enum
|
|
}
|
|
if len(p.Weak) > 0 {
|
|
s += ",weak=" + p.Weak
|
|
}
|
|
if p.Proto3 {
|
|
s += ",proto3"
|
|
}
|
|
if p.Oneof {
|
|
s += ",oneof"
|
|
}
|
|
if p.HasDefault {
|
|
s += ",def=" + p.Default
|
|
}
|
|
return s
|
|
}
|
|
|
|
// Parse populates p by parsing a string in the protobuf struct field tag style.
|
|
func (p *Properties) Parse(tag string) {
|
|
// For example: "bytes,49,opt,name=foo,def=hello!"
|
|
for len(tag) > 0 {
|
|
i := strings.IndexByte(tag, ',')
|
|
if i < 0 {
|
|
i = len(tag)
|
|
}
|
|
switch s := tag[:i]; {
|
|
case strings.HasPrefix(s, "name="):
|
|
p.OrigName = s[len("name="):]
|
|
case strings.HasPrefix(s, "json="):
|
|
p.JSONName = s[len("json="):]
|
|
case strings.HasPrefix(s, "enum="):
|
|
p.Enum = s[len("enum="):]
|
|
case strings.HasPrefix(s, "weak="):
|
|
p.Weak = s[len("weak="):]
|
|
case strings.Trim(s, "0123456789") == "":
|
|
n, _ := strconv.ParseUint(s, 10, 32)
|
|
p.Tag = int(n)
|
|
case s == "opt":
|
|
p.Optional = true
|
|
case s == "req":
|
|
p.Required = true
|
|
case s == "rep":
|
|
p.Repeated = true
|
|
case s == "varint" || s == "zigzag32" || s == "zigzag64":
|
|
p.Wire = s
|
|
p.WireType = WireVarint
|
|
case s == "fixed32":
|
|
p.Wire = s
|
|
p.WireType = WireFixed32
|
|
case s == "fixed64":
|
|
p.Wire = s
|
|
p.WireType = WireFixed64
|
|
case s == "bytes":
|
|
p.Wire = s
|
|
p.WireType = WireBytes
|
|
case s == "group":
|
|
p.Wire = s
|
|
p.WireType = WireStartGroup
|
|
case s == "packed":
|
|
p.Packed = true
|
|
case s == "proto3":
|
|
p.Proto3 = true
|
|
case s == "oneof":
|
|
p.Oneof = true
|
|
case strings.HasPrefix(s, "def="):
|
|
// The default tag is special in that everything afterwards is the
|
|
// default regardless of the presence of commas.
|
|
p.HasDefault = true
|
|
p.Default, i = tag[len("def="):], len(tag)
|
|
}
|
|
tag = strings.TrimPrefix(tag[i:], ",")
|
|
}
|
|
}
|
|
|
|
// Init populates the properties from a protocol buffer struct tag.
|
|
//
|
|
// Deprecated: Do not use.
|
|
func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) {
|
|
p.Name = name
|
|
p.OrigName = name
|
|
if tag == "" {
|
|
return
|
|
}
|
|
p.Parse(tag)
|
|
|
|
if typ != nil && typ.Kind() == reflect.Map {
|
|
p.MapKeyProp = new(Properties)
|
|
p.MapKeyProp.Init(nil, "Key", f.Tag.Get("protobuf_key"), nil)
|
|
p.MapValProp = new(Properties)
|
|
p.MapValProp.Init(nil, "Value", f.Tag.Get("protobuf_val"), nil)
|
|
}
|
|
}
|
|
|
|
var propertiesCache sync.Map // map[reflect.Type]*StructProperties
|
|
|
|
// GetProperties returns the list of properties for the type represented by t,
|
|
// which must be a generated protocol buffer message in the open-struct API,
|
|
// where protobuf message fields are represented by exported Go struct fields.
|
|
//
|
|
// Deprecated: Use protobuf reflection instead.
|
|
func GetProperties(t reflect.Type) *StructProperties {
|
|
if p, ok := propertiesCache.Load(t); ok {
|
|
return p.(*StructProperties)
|
|
}
|
|
p, _ := propertiesCache.LoadOrStore(t, newProperties(t))
|
|
return p.(*StructProperties)
|
|
}
|
|
|
|
func newProperties(t reflect.Type) *StructProperties {
|
|
if t.Kind() != reflect.Struct {
|
|
panic(fmt.Sprintf("%v is not a generated message in the open-struct API", t))
|
|
}
|
|
|
|
var hasOneof bool
|
|
prop := new(StructProperties)
|
|
|
|
// Construct a list of properties for each field in the struct.
|
|
for i := 0; i < t.NumField(); i++ {
|
|
p := new(Properties)
|
|
f := t.Field(i)
|
|
tagField := f.Tag.Get("protobuf")
|
|
p.Init(f.Type, f.Name, tagField, &f)
|
|
|
|
tagOneof := f.Tag.Get("protobuf_oneof")
|
|
if tagOneof != "" {
|
|
hasOneof = true
|
|
p.OrigName = tagOneof
|
|
}
|
|
|
|
// Rename unrelated struct fields with the "XXX_" prefix since so much
|
|
// user code simply checks for this to exclude special fields.
|
|
if tagField == "" && tagOneof == "" && !strings.HasPrefix(p.Name, "XXX_") {
|
|
p.Name = "XXX_" + p.Name
|
|
p.OrigName = "XXX_" + p.OrigName
|
|
} else if p.Weak != "" {
|
|
p.Name = p.OrigName // avoid possible "XXX_" prefix on weak field
|
|
}
|
|
|
|
prop.Prop = append(prop.Prop, p)
|
|
}
|
|
|
|
// Construct a mapping of oneof field names to properties.
|
|
if hasOneof {
|
|
var oneofWrappers []interface{}
|
|
if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofFuncs"); ok {
|
|
oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[3].Interface().([]interface{})
|
|
}
|
|
if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofWrappers"); ok {
|
|
oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[0].Interface().([]interface{})
|
|
}
|
|
if m, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(protoreflect.ProtoMessage); ok {
|
|
if m, ok := m.ProtoReflect().(interface{ ProtoMessageInfo() *protoimpl.MessageInfo }); ok {
|
|
oneofWrappers = m.ProtoMessageInfo().OneofWrappers
|
|
}
|
|
}
|
|
|
|
prop.OneofTypes = make(map[string]*OneofProperties)
|
|
for _, wrapper := range oneofWrappers {
|
|
p := &OneofProperties{
|
|
Type: reflect.ValueOf(wrapper).Type(), // *T
|
|
Prop: new(Properties),
|
|
}
|
|
f := p.Type.Elem().Field(0)
|
|
p.Prop.Name = f.Name
|
|
p.Prop.Parse(f.Tag.Get("protobuf"))
|
|
|
|
// Determine the struct field that contains this oneof.
|
|
// Each wrapper is assignable to exactly one parent field.
|
|
var foundOneof bool
|
|
for i := 0; i < t.NumField() && !foundOneof; i++ {
|
|
if p.Type.AssignableTo(t.Field(i).Type) {
|
|
p.Field = i
|
|
foundOneof = true
|
|
}
|
|
}
|
|
if !foundOneof {
|
|
panic(fmt.Sprintf("%v is not a generated message in the open-struct API", t))
|
|
}
|
|
prop.OneofTypes[p.Prop.OrigName] = p
|
|
}
|
|
}
|
|
|
|
return prop
|
|
}
|
|
|
|
func (sp *StructProperties) Len() int { return len(sp.Prop) }
|
|
func (sp *StructProperties) Less(i, j int) bool { return false }
|
|
func (sp *StructProperties) Swap(i, j int) { return }
|