You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

208 lines
7.2 KiB

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