github.com/whiteCcinn/protobuf-go@v1.0.9/internal/encoding/tag/tag.go (about) 1 // Copyright 2018 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 tag marshals and unmarshals the legacy struct tags as generated 6 // by historical versions of protoc-gen-go. 7 package tag 8 9 import ( 10 "reflect" 11 "strconv" 12 "strings" 13 14 "github.com/whiteCcinn/protobuf-go/internal/encoding/defval" 15 "github.com/whiteCcinn/protobuf-go/internal/filedesc" 16 "github.com/whiteCcinn/protobuf-go/internal/strs" 17 "github.com/whiteCcinn/protobuf-go/reflect/protoreflect" 18 ) 19 20 var byteType = reflect.TypeOf(byte(0)) 21 22 // Unmarshal decodes the tag into a prototype.Field. 23 // 24 // The goType is needed to determine the original protoreflect.Kind since the 25 // tag does not record sufficient information to determine that. 26 // The type is the underlying field type (e.g., a repeated field may be 27 // represented by []T, but the Go type passed in is just T). 28 // A list of enum value descriptors must be provided for enum fields. 29 // This does not populate the Enum or Message (except for weak message). 30 // 31 // This function is a best effort attempt; parsing errors are ignored. 32 func Unmarshal(tag string, goType reflect.Type, evs protoreflect.EnumValueDescriptors) protoreflect.FieldDescriptor { 33 f := new(filedesc.Field) 34 f.L0.ParentFile = filedesc.SurrogateProto2 35 for len(tag) > 0 { 36 i := strings.IndexByte(tag, ',') 37 if i < 0 { 38 i = len(tag) 39 } 40 switch s := tag[:i]; { 41 case strings.HasPrefix(s, "name="): 42 f.L0.FullName = protoreflect.FullName(s[len("name="):]) 43 case strings.Trim(s, "0123456789") == "": 44 n, _ := strconv.ParseUint(s, 10, 32) 45 f.L1.Number = protoreflect.FieldNumber(n) 46 case s == "opt": 47 f.L1.Cardinality = protoreflect.Optional 48 case s == "req": 49 f.L1.Cardinality = protoreflect.Required 50 case s == "rep": 51 f.L1.Cardinality = protoreflect.Repeated 52 case s == "varint": 53 switch goType.Kind() { 54 case reflect.Bool: 55 f.L1.Kind = protoreflect.BoolKind 56 case reflect.Int32: 57 f.L1.Kind = protoreflect.Int32Kind 58 case reflect.Int64: 59 f.L1.Kind = protoreflect.Int64Kind 60 case reflect.Uint32: 61 f.L1.Kind = protoreflect.Uint32Kind 62 case reflect.Uint64: 63 f.L1.Kind = protoreflect.Uint64Kind 64 } 65 case s == "zigzag32": 66 if goType.Kind() == reflect.Int32 { 67 f.L1.Kind = protoreflect.Sint32Kind 68 } 69 case s == "zigzag64": 70 if goType.Kind() == reflect.Int64 { 71 f.L1.Kind = protoreflect.Sint64Kind 72 } 73 case s == "fixed32": 74 switch goType.Kind() { 75 case reflect.Int32: 76 f.L1.Kind = protoreflect.Sfixed32Kind 77 case reflect.Uint32: 78 f.L1.Kind = protoreflect.Fixed32Kind 79 case reflect.Float32: 80 f.L1.Kind = protoreflect.FloatKind 81 } 82 case s == "fixed64": 83 switch goType.Kind() { 84 case reflect.Int64: 85 f.L1.Kind = protoreflect.Sfixed64Kind 86 case reflect.Uint64: 87 f.L1.Kind = protoreflect.Fixed64Kind 88 case reflect.Float64: 89 f.L1.Kind = protoreflect.DoubleKind 90 } 91 case s == "bytes": 92 switch { 93 case goType.Kind() == reflect.String: 94 f.L1.Kind = protoreflect.StringKind 95 case goType.Kind() == reflect.Slice && goType.Elem() == byteType: 96 f.L1.Kind = protoreflect.BytesKind 97 default: 98 f.L1.Kind = protoreflect.MessageKind 99 } 100 case s == "group": 101 f.L1.Kind = protoreflect.GroupKind 102 case strings.HasPrefix(s, "enum="): 103 f.L1.Kind = protoreflect.EnumKind 104 case strings.HasPrefix(s, "json="): 105 jsonName := s[len("json="):] 106 if jsonName != strs.JSONCamelCase(string(f.L0.FullName.Name())) { 107 f.L1.StringName.InitJSON(jsonName) 108 } 109 case s == "packed": 110 f.L1.HasPacked = true 111 f.L1.IsPacked = true 112 case strings.HasPrefix(s, "weak="): 113 f.L1.IsWeak = true 114 f.L1.Message = filedesc.PlaceholderMessage(protoreflect.FullName(s[len("weak="):])) 115 case strings.HasPrefix(s, "def="): 116 // The default tag is special in that everything afterwards is the 117 // default regardless of the presence of commas. 118 s, i = tag[len("def="):], len(tag) 119 v, ev, _ := defval.Unmarshal(s, f.L1.Kind, evs, defval.GoTag) 120 f.L1.Default = filedesc.DefaultValue(v, ev) 121 case s == "proto3": 122 f.L0.ParentFile = filedesc.SurrogateProto3 123 } 124 tag = strings.TrimPrefix(tag[i:], ",") 125 } 126 127 // The generator uses the group message name instead of the field name. 128 // We obtain the real field name by lowercasing the group name. 129 if f.L1.Kind == protoreflect.GroupKind { 130 f.L0.FullName = protoreflect.FullName(strings.ToLower(string(f.L0.FullName))) 131 } 132 return f 133 } 134 135 // Marshal encodes the protoreflect.FieldDescriptor as a tag. 136 // 137 // The enumName must be provided if the kind is an enum. 138 // Historically, the formulation of the enum "name" was the proto package 139 // dot-concatenated with the generated Go identifier for the enum type. 140 // Depending on the context on how Marshal is called, there are different ways 141 // through which that information is determined. As such it is the caller's 142 // responsibility to provide a function to obtain that information. 143 func Marshal(fd protoreflect.FieldDescriptor, enumName string) string { 144 var tag []string 145 switch fd.Kind() { 146 case protoreflect.BoolKind, protoreflect.EnumKind, protoreflect.Int32Kind, protoreflect.Uint32Kind, protoreflect.Int64Kind, protoreflect.Uint64Kind: 147 tag = append(tag, "varint") 148 case protoreflect.Sint32Kind: 149 tag = append(tag, "zigzag32") 150 case protoreflect.Sint64Kind: 151 tag = append(tag, "zigzag64") 152 case protoreflect.Sfixed32Kind, protoreflect.Fixed32Kind, protoreflect.FloatKind: 153 tag = append(tag, "fixed32") 154 case protoreflect.Sfixed64Kind, protoreflect.Fixed64Kind, protoreflect.DoubleKind: 155 tag = append(tag, "fixed64") 156 case protoreflect.StringKind, protoreflect.BytesKind, protoreflect.MessageKind: 157 tag = append(tag, "bytes") 158 case protoreflect.GroupKind: 159 tag = append(tag, "group") 160 } 161 tag = append(tag, strconv.Itoa(int(fd.Number()))) 162 switch fd.Cardinality() { 163 case protoreflect.Optional: 164 tag = append(tag, "opt") 165 case protoreflect.Required: 166 tag = append(tag, "req") 167 case protoreflect.Repeated: 168 tag = append(tag, "rep") 169 } 170 if fd.IsPacked() { 171 tag = append(tag, "packed") 172 } 173 name := string(fd.Name()) 174 if fd.Kind() == protoreflect.GroupKind { 175 // The name of the FieldDescriptor for a group field is 176 // lowercased. To find the original capitalization, we 177 // look in the field's MessageType. 178 name = string(fd.Message().Name()) 179 } 180 tag = append(tag, "name="+name) 181 if jsonName := fd.JSONName(); jsonName != "" && jsonName != name && !fd.IsExtension() { 182 // NOTE: The jsonName != name condition is suspect, but it preserve 183 // the exact same semantics from the previous generator. 184 tag = append(tag, "json="+jsonName) 185 } 186 if fd.IsWeak() { 187 tag = append(tag, "weak="+string(fd.Message().FullName())) 188 } 189 // The previous implementation does not tag extension fields as proto3, 190 // even when the field is defined in a proto3 file. Match that behavior 191 // for consistency. 192 if fd.Syntax() == protoreflect.Proto3 && !fd.IsExtension() { 193 tag = append(tag, "proto3") 194 } 195 if fd.Kind() == protoreflect.EnumKind && enumName != "" { 196 tag = append(tag, "enum="+enumName) 197 } 198 if fd.ContainingOneof() != nil { 199 tag = append(tag, "oneof") 200 } 201 // This must appear last in the tag, since commas in strings aren't escaped. 202 if fd.HasDefault() { 203 def, _ := defval.Marshal(fd.Default(), fd.DefaultEnumValue(), fd.Kind(), defval.GoTag) 204 tag = append(tag, "def="+def) 205 } 206 return strings.Join(tag, ",") 207 }