blob: dcdc2202fada8b87c063dbc721c83f44a728dea4 [file] [log] [blame]
amit.ghosh258d14c2020-10-02 15:13:38 +02001// Copyright 2010 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -07004
5package proto
6
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -07007import (
8 "fmt"
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -07009 "reflect"
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070010 "strconv"
11 "strings"
12 "sync"
amit.ghosh258d14c2020-10-02 15:13:38 +020013
14 "google.golang.org/protobuf/reflect/protoreflect"
15 "google.golang.org/protobuf/runtime/protoimpl"
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070016)
17
amit.ghosh258d14c2020-10-02 15:13:38 +020018// StructProperties represents protocol buffer type information for a
19// generated protobuf message in the open-struct API.
20//
21// Deprecated: Do not use.
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070022type StructProperties struct {
amit.ghosh258d14c2020-10-02 15:13:38 +020023 // Prop are the properties for each field.
24 //
25 // Fields belonging to a oneof are stored in OneofTypes instead, with a
26 // single Properties representing the parent oneof held here.
27 //
28 // The order of Prop matches the order of fields in the Go struct.
29 // Struct fields that are not related to protobufs have a "XXX_" prefix
30 // in the Properties.Name and must be ignored by the user.
31 Prop []*Properties
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070032
33 // OneofTypes contains information about the oneof fields in this message.
amit.ghosh258d14c2020-10-02 15:13:38 +020034 // It is keyed by the protobuf field name.
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070035 OneofTypes map[string]*OneofProperties
36}
37
amit.ghosh258d14c2020-10-02 15:13:38 +020038// Properties represents the type information for a protobuf message field.
39//
40// Deprecated: Do not use.
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070041type Properties struct {
amit.ghosh258d14c2020-10-02 15:13:38 +020042 // Name is a placeholder name with little meaningful semantic value.
43 // If the name has an "XXX_" prefix, the entire Properties must be ignored.
44 Name string
45 // OrigName is the protobuf field name or oneof name.
46 OrigName string
47 // JSONName is the JSON name for the protobuf field.
48 JSONName string
49 // Enum is a placeholder name for enums.
50 // For historical reasons, this is neither the Go name for the enum,
51 // nor the protobuf name for the enum.
52 Enum string // Deprecated: Do not use.
53 // Weak contains the full name of the weakly referenced message.
54 Weak string
55 // Wire is a string representation of the wire type.
56 Wire string
57 // WireType is the protobuf wire type for the field.
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070058 WireType int
amit.ghosh258d14c2020-10-02 15:13:38 +020059 // Tag is the protobuf field number.
60 Tag int
61 // Required reports whether this is a required field.
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070062 Required bool
amit.ghosh258d14c2020-10-02 15:13:38 +020063 // Optional reports whether this is a optional field.
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070064 Optional bool
amit.ghosh258d14c2020-10-02 15:13:38 +020065 // Repeated reports whether this is a repeated field.
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070066 Repeated bool
amit.ghosh258d14c2020-10-02 15:13:38 +020067 // Packed reports whether this is a packed repeated field of scalars.
68 Packed bool
69 // Proto3 reports whether this field operates under the proto3 syntax.
70 Proto3 bool
71 // Oneof reports whether this field belongs within a oneof.
72 Oneof bool
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070073
amit.ghosh258d14c2020-10-02 15:13:38 +020074 // Default is the default value in string form.
75 Default string
76 // HasDefault reports whether the field has a default value.
77 HasDefault bool
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070078
amit.ghosh258d14c2020-10-02 15:13:38 +020079 // MapKeyProp is the properties for the key field for a map field.
80 MapKeyProp *Properties
81 // MapValProp is the properties for the value field for a map field.
82 MapValProp *Properties
83}
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070084
amit.ghosh258d14c2020-10-02 15:13:38 +020085// OneofProperties represents the type information for a protobuf oneof.
86//
87// Deprecated: Do not use.
88type OneofProperties struct {
89 // Type is a pointer to the generated wrapper type for the field value.
90 // This is nil for messages that are not in the open-struct API.
91 Type reflect.Type
92 // Field is the index into StructProperties.Prop for the containing oneof.
93 Field int
94 // Prop is the properties for the field.
95 Prop *Properties
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070096}
97
98// String formats the properties in the protobuf struct field tag style.
99func (p *Properties) String() string {
100 s := p.Wire
amit.ghosh258d14c2020-10-02 15:13:38 +0200101 s += "," + strconv.Itoa(p.Tag)
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700102 if p.Required {
103 s += ",req"
104 }
105 if p.Optional {
106 s += ",opt"
107 }
108 if p.Repeated {
109 s += ",rep"
110 }
111 if p.Packed {
112 s += ",packed"
113 }
114 s += ",name=" + p.OrigName
amit.ghosh258d14c2020-10-02 15:13:38 +0200115 if p.JSONName != "" {
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700116 s += ",json=" + p.JSONName
117 }
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700118 if len(p.Enum) > 0 {
119 s += ",enum=" + p.Enum
120 }
amit.ghosh258d14c2020-10-02 15:13:38 +0200121 if len(p.Weak) > 0 {
122 s += ",weak=" + p.Weak
123 }
124 if p.Proto3 {
125 s += ",proto3"
126 }
127 if p.Oneof {
128 s += ",oneof"
129 }
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700130 if p.HasDefault {
131 s += ",def=" + p.Default
132 }
133 return s
134}
135
136// Parse populates p by parsing a string in the protobuf struct field tag style.
amit.ghosh258d14c2020-10-02 15:13:38 +0200137func (p *Properties) Parse(tag string) {
138 // For example: "bytes,49,opt,name=foo,def=hello!"
139 for len(tag) > 0 {
140 i := strings.IndexByte(tag, ',')
141 if i < 0 {
142 i = len(tag)
143 }
144 switch s := tag[:i]; {
145 case strings.HasPrefix(s, "name="):
146 p.OrigName = s[len("name="):]
147 case strings.HasPrefix(s, "json="):
148 p.JSONName = s[len("json="):]
149 case strings.HasPrefix(s, "enum="):
150 p.Enum = s[len("enum="):]
151 case strings.HasPrefix(s, "weak="):
152 p.Weak = s[len("weak="):]
153 case strings.Trim(s, "0123456789") == "":
154 n, _ := strconv.ParseUint(s, 10, 32)
155 p.Tag = int(n)
156 case s == "opt":
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700157 p.Optional = true
amit.ghosh258d14c2020-10-02 15:13:38 +0200158 case s == "req":
159 p.Required = true
160 case s == "rep":
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700161 p.Repeated = true
amit.ghosh258d14c2020-10-02 15:13:38 +0200162 case s == "varint" || s == "zigzag32" || s == "zigzag64":
163 p.Wire = s
164 p.WireType = WireVarint
165 case s == "fixed32":
166 p.Wire = s
167 p.WireType = WireFixed32
168 case s == "fixed64":
169 p.Wire = s
170 p.WireType = WireFixed64
171 case s == "bytes":
172 p.Wire = s
173 p.WireType = WireBytes
174 case s == "group":
175 p.Wire = s
176 p.WireType = WireStartGroup
177 case s == "packed":
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700178 p.Packed = true
amit.ghosh258d14c2020-10-02 15:13:38 +0200179 case s == "proto3":
180 p.Proto3 = true
181 case s == "oneof":
182 p.Oneof = true
183 case strings.HasPrefix(s, "def="):
184 // The default tag is special in that everything afterwards is the
185 // default regardless of the presence of commas.
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700186 p.HasDefault = true
amit.ghosh258d14c2020-10-02 15:13:38 +0200187 p.Default, i = tag[len("def="):], len(tag)
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700188 }
amit.ghosh258d14c2020-10-02 15:13:38 +0200189 tag = strings.TrimPrefix(tag[i:], ",")
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700190 }
191}
192
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700193// Init populates the properties from a protocol buffer struct tag.
amit.ghosh258d14c2020-10-02 15:13:38 +0200194//
195// Deprecated: Do not use.
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700196func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) {
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700197 p.Name = name
198 p.OrigName = name
199 if tag == "" {
200 return
201 }
202 p.Parse(tag)
amit.ghosh258d14c2020-10-02 15:13:38 +0200203
204 if typ != nil && typ.Kind() == reflect.Map {
205 p.MapKeyProp = new(Properties)
206 p.MapKeyProp.Init(nil, "Key", f.Tag.Get("protobuf_key"), nil)
207 p.MapValProp = new(Properties)
208 p.MapValProp.Init(nil, "Value", f.Tag.Get("protobuf_val"), nil)
209 }
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700210}
211
amit.ghosh258d14c2020-10-02 15:13:38 +0200212var propertiesCache sync.Map // map[reflect.Type]*StructProperties
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700213
amit.ghosh258d14c2020-10-02 15:13:38 +0200214// GetProperties returns the list of properties for the type represented by t,
215// which must be a generated protocol buffer message in the open-struct API,
216// where protobuf message fields are represented by exported Go struct fields.
217//
218// Deprecated: Use protobuf reflection instead.
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700219func GetProperties(t reflect.Type) *StructProperties {
amit.ghosh258d14c2020-10-02 15:13:38 +0200220 if p, ok := propertiesCache.Load(t); ok {
221 return p.(*StructProperties)
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700222 }
amit.ghosh258d14c2020-10-02 15:13:38 +0200223 p, _ := propertiesCache.LoadOrStore(t, newProperties(t))
224 return p.(*StructProperties)
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700225}
226
amit.ghosh258d14c2020-10-02 15:13:38 +0200227func newProperties(t reflect.Type) *StructProperties {
228 if t.Kind() != reflect.Struct {
229 panic(fmt.Sprintf("%v is not a generated message in the open-struct API", t))
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700230 }
231
amit.ghosh258d14c2020-10-02 15:13:38 +0200232 var hasOneof bool
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700233 prop := new(StructProperties)
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700234
amit.ghosh258d14c2020-10-02 15:13:38 +0200235 // Construct a list of properties for each field in the struct.
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700236 for i := 0; i < t.NumField(); i++ {
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700237 p := new(Properties)
amit.ghosh258d14c2020-10-02 15:13:38 +0200238 f := t.Field(i)
239 tagField := f.Tag.Get("protobuf")
240 p.Init(f.Type, f.Name, tagField, &f)
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700241
amit.ghosh258d14c2020-10-02 15:13:38 +0200242 tagOneof := f.Tag.Get("protobuf_oneof")
243 if tagOneof != "" {
244 hasOneof = true
245 p.OrigName = tagOneof
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700246 }
amit.ghosh258d14c2020-10-02 15:13:38 +0200247
248 // Rename unrelated struct fields with the "XXX_" prefix since so much
249 // user code simply checks for this to exclude special fields.
250 if tagField == "" && tagOneof == "" && !strings.HasPrefix(p.Name, "XXX_") {
251 p.Name = "XXX_" + p.Name
252 p.OrigName = "XXX_" + p.OrigName
253 } else if p.Weak != "" {
254 p.Name = p.OrigName // avoid possible "XXX_" prefix on weak field
255 }
256
257 prop.Prop = append(prop.Prop, p)
258 }
259
260 // Construct a mapping of oneof field names to properties.
261 if hasOneof {
262 var oneofWrappers []interface{}
263 if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofFuncs"); ok {
264 oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[3].Interface().([]interface{})
265 }
266 if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofWrappers"); ok {
267 oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[0].Interface().([]interface{})
268 }
269 if m, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(protoreflect.ProtoMessage); ok {
270 if m, ok := m.ProtoReflect().(interface{ ProtoMessageInfo() *protoimpl.MessageInfo }); ok {
271 oneofWrappers = m.ProtoMessageInfo().OneofWrappers
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700272 }
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700273 }
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700274
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700275 prop.OneofTypes = make(map[string]*OneofProperties)
amit.ghosh258d14c2020-10-02 15:13:38 +0200276 for _, wrapper := range oneofWrappers {
277 p := &OneofProperties{
278 Type: reflect.ValueOf(wrapper).Type(), // *T
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700279 Prop: new(Properties),
280 }
amit.ghosh258d14c2020-10-02 15:13:38 +0200281 f := p.Type.Elem().Field(0)
282 p.Prop.Name = f.Name
283 p.Prop.Parse(f.Tag.Get("protobuf"))
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700284
amit.ghosh258d14c2020-10-02 15:13:38 +0200285 // Determine the struct field that contains this oneof.
286 // Each wrapper is assignable to exactly one parent field.
287 var foundOneof bool
288 for i := 0; i < t.NumField() && !foundOneof; i++ {
289 if p.Type.AssignableTo(t.Field(i).Type) {
290 p.Field = i
291 foundOneof = true
292 }
293 }
294 if !foundOneof {
295 panic(fmt.Sprintf("%v is not a generated message in the open-struct API", t))
296 }
297 prop.OneofTypes[p.Prop.OrigName] = p
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700298 }
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700299 }
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700300
301 return prop
302}
303
amit.ghosh258d14c2020-10-02 15:13:38 +0200304func (sp *StructProperties) Len() int { return len(sp.Prop) }
305func (sp *StructProperties) Less(i, j int) bool { return false }
306func (sp *StructProperties) Swap(i, j int) { return }