properties.go (9108B)
1 // 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. 4 5 package proto 6 7 import ( 8 "fmt" 9 "reflect" 10 "strconv" 11 "strings" 12 "sync" 13 14 "google.golang.org/protobuf/reflect/protoreflect" 15 "google.golang.org/protobuf/runtime/protoimpl" 16 ) 17 18 // StructProperties represents protocol buffer type information for a 19 // generated protobuf message in the open-struct API. 20 // 21 // Deprecated: Do not use. 22 type StructProperties struct { 23 // 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 32 33 // OneofTypes contains information about the oneof fields in this message. 34 // It is keyed by the protobuf field name. 35 OneofTypes map[string]*OneofProperties 36 } 37 38 // Properties represents the type information for a protobuf message field. 39 // 40 // Deprecated: Do not use. 41 type Properties struct { 42 // 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. 58 WireType int 59 // Tag is the protobuf field number. 60 Tag int 61 // Required reports whether this is a required field. 62 Required bool 63 // Optional reports whether this is a optional field. 64 Optional bool 65 // Repeated reports whether this is a repeated field. 66 Repeated bool 67 // 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 73 74 // Default is the default value in string form. 75 Default string 76 // HasDefault reports whether the field has a default value. 77 HasDefault bool 78 79 // 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 } 84 85 // OneofProperties represents the type information for a protobuf oneof. 86 // 87 // Deprecated: Do not use. 88 type 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 96 } 97 98 // String formats the properties in the protobuf struct field tag style. 99 func (p *Properties) String() string { 100 s := p.Wire 101 s += "," + strconv.Itoa(p.Tag) 102 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 115 if p.JSONName != "" { 116 s += ",json=" + p.JSONName 117 } 118 if len(p.Enum) > 0 { 119 s += ",enum=" + p.Enum 120 } 121 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 } 130 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. 137 func (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": 157 p.Optional = true 158 case s == "req": 159 p.Required = true 160 case s == "rep": 161 p.Repeated = true 162 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": 178 p.Packed = true 179 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. 186 p.HasDefault = true 187 p.Default, i = tag[len("def="):], len(tag) 188 } 189 tag = strings.TrimPrefix(tag[i:], ",") 190 } 191 } 192 193 // Init populates the properties from a protocol buffer struct tag. 194 // 195 // Deprecated: Do not use. 196 func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) { 197 p.Name = name 198 p.OrigName = name 199 if tag == "" { 200 return 201 } 202 p.Parse(tag) 203 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 } 210 } 211 212 var propertiesCache sync.Map // map[reflect.Type]*StructProperties 213 214 // 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. 219 func GetProperties(t reflect.Type) *StructProperties { 220 if p, ok := propertiesCache.Load(t); ok { 221 return p.(*StructProperties) 222 } 223 p, _ := propertiesCache.LoadOrStore(t, newProperties(t)) 224 return p.(*StructProperties) 225 } 226 227 func 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)) 230 } 231 232 var hasOneof bool 233 prop := new(StructProperties) 234 235 // Construct a list of properties for each field in the struct. 236 for i := 0; i < t.NumField(); i++ { 237 p := new(Properties) 238 f := t.Field(i) 239 tagField := f.Tag.Get("protobuf") 240 p.Init(f.Type, f.Name, tagField, &f) 241 242 tagOneof := f.Tag.Get("protobuf_oneof") 243 if tagOneof != "" { 244 hasOneof = true 245 p.OrigName = tagOneof 246 } 247 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 272 } 273 } 274 275 prop.OneofTypes = make(map[string]*OneofProperties) 276 for _, wrapper := range oneofWrappers { 277 p := &OneofProperties{ 278 Type: reflect.ValueOf(wrapper).Type(), // *T 279 Prop: new(Properties), 280 } 281 f := p.Type.Elem().Field(0) 282 p.Prop.Name = f.Name 283 p.Prop.Parse(f.Tag.Get("protobuf")) 284 285 // 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 298 } 299 } 300 301 return prop 302 } 303 304 func (sp *StructProperties) Len() int { return len(sp.Prop) } 305 func (sp *StructProperties) Less(i, j int) bool { return false } 306 func (sp *StructProperties) Swap(i, j int) { return }