123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- // Copyright 2018 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 tag marshals and unmarshals the legacy struct tags as generated
- // by historical versions of protoc-gen-go.
- package tag
- import (
- "reflect"
- "strconv"
- "strings"
- defval "google.golang.org/protobuf/internal/encoding/defval"
- fdesc "google.golang.org/protobuf/internal/filedesc"
- "google.golang.org/protobuf/internal/strs"
- pref "google.golang.org/protobuf/reflect/protoreflect"
- )
- var byteType = reflect.TypeOf(byte(0))
- // Unmarshal decodes the tag into a prototype.Field.
- //
- // The goType is needed to determine the original protoreflect.Kind since the
- // tag does not record sufficient information to determine that.
- // The type is the underlying field type (e.g., a repeated field may be
- // represented by []T, but the Go type passed in is just T).
- // A list of enum value descriptors must be provided for enum fields.
- // This does not populate the Enum or Message (except for weak message).
- //
- // This function is a best effort attempt; parsing errors are ignored.
- func Unmarshal(tag string, goType reflect.Type, evs pref.EnumValueDescriptors) pref.FieldDescriptor {
- f := new(fdesc.Field)
- f.L0.ParentFile = fdesc.SurrogateProto2
- for len(tag) > 0 {
- i := strings.IndexByte(tag, ',')
- if i < 0 {
- i = len(tag)
- }
- switch s := tag[:i]; {
- case strings.HasPrefix(s, "name="):
- f.L0.FullName = pref.FullName(s[len("name="):])
- case strings.Trim(s, "0123456789") == "":
- n, _ := strconv.ParseUint(s, 10, 32)
- f.L1.Number = pref.FieldNumber(n)
- case s == "opt":
- f.L1.Cardinality = pref.Optional
- case s == "req":
- f.L1.Cardinality = pref.Required
- case s == "rep":
- f.L1.Cardinality = pref.Repeated
- case s == "varint":
- switch goType.Kind() {
- case reflect.Bool:
- f.L1.Kind = pref.BoolKind
- case reflect.Int32:
- f.L1.Kind = pref.Int32Kind
- case reflect.Int64:
- f.L1.Kind = pref.Int64Kind
- case reflect.Uint32:
- f.L1.Kind = pref.Uint32Kind
- case reflect.Uint64:
- f.L1.Kind = pref.Uint64Kind
- }
- case s == "zigzag32":
- if goType.Kind() == reflect.Int32 {
- f.L1.Kind = pref.Sint32Kind
- }
- case s == "zigzag64":
- if goType.Kind() == reflect.Int64 {
- f.L1.Kind = pref.Sint64Kind
- }
- case s == "fixed32":
- switch goType.Kind() {
- case reflect.Int32:
- f.L1.Kind = pref.Sfixed32Kind
- case reflect.Uint32:
- f.L1.Kind = pref.Fixed32Kind
- case reflect.Float32:
- f.L1.Kind = pref.FloatKind
- }
- case s == "fixed64":
- switch goType.Kind() {
- case reflect.Int64:
- f.L1.Kind = pref.Sfixed64Kind
- case reflect.Uint64:
- f.L1.Kind = pref.Fixed64Kind
- case reflect.Float64:
- f.L1.Kind = pref.DoubleKind
- }
- case s == "bytes":
- switch {
- case goType.Kind() == reflect.String:
- f.L1.Kind = pref.StringKind
- case goType.Kind() == reflect.Slice && goType.Elem() == byteType:
- f.L1.Kind = pref.BytesKind
- default:
- f.L1.Kind = pref.MessageKind
- }
- case s == "group":
- f.L1.Kind = pref.GroupKind
- case strings.HasPrefix(s, "enum="):
- f.L1.Kind = pref.EnumKind
- case strings.HasPrefix(s, "json="):
- jsonName := s[len("json="):]
- if jsonName != strs.JSONCamelCase(string(f.L0.FullName.Name())) {
- f.L1.StringName.InitJSON(jsonName)
- }
- case s == "packed":
- f.L1.HasPacked = true
- f.L1.IsPacked = true
- case strings.HasPrefix(s, "weak="):
- f.L1.IsWeak = true
- f.L1.Message = fdesc.PlaceholderMessage(pref.FullName(s[len("weak="):]))
- case strings.HasPrefix(s, "def="):
- // The default tag is special in that everything afterwards is the
- // default regardless of the presence of commas.
- s, i = tag[len("def="):], len(tag)
- v, ev, _ := defval.Unmarshal(s, f.L1.Kind, evs, defval.GoTag)
- f.L1.Default = fdesc.DefaultValue(v, ev)
- case s == "proto3":
- f.L0.ParentFile = fdesc.SurrogateProto3
- }
- tag = strings.TrimPrefix(tag[i:], ",")
- }
- // The generator uses the group message name instead of the field name.
- // We obtain the real field name by lowercasing the group name.
- if f.L1.Kind == pref.GroupKind {
- f.L0.FullName = pref.FullName(strings.ToLower(string(f.L0.FullName)))
- }
- return f
- }
- // Marshal encodes the protoreflect.FieldDescriptor as a tag.
- //
- // The enumName must be provided if the kind is an enum.
- // Historically, the formulation of the enum "name" was the proto package
- // dot-concatenated with the generated Go identifier for the enum type.
- // Depending on the context on how Marshal is called, there are different ways
- // through which that information is determined. As such it is the caller's
- // responsibility to provide a function to obtain that information.
- func Marshal(fd pref.FieldDescriptor, enumName string) string {
- var tag []string
- switch fd.Kind() {
- case pref.BoolKind, pref.EnumKind, pref.Int32Kind, pref.Uint32Kind, pref.Int64Kind, pref.Uint64Kind:
- tag = append(tag, "varint")
- case pref.Sint32Kind:
- tag = append(tag, "zigzag32")
- case pref.Sint64Kind:
- tag = append(tag, "zigzag64")
- case pref.Sfixed32Kind, pref.Fixed32Kind, pref.FloatKind:
- tag = append(tag, "fixed32")
- case pref.Sfixed64Kind, pref.Fixed64Kind, pref.DoubleKind:
- tag = append(tag, "fixed64")
- case pref.StringKind, pref.BytesKind, pref.MessageKind:
- tag = append(tag, "bytes")
- case pref.GroupKind:
- tag = append(tag, "group")
- }
- tag = append(tag, strconv.Itoa(int(fd.Number())))
- switch fd.Cardinality() {
- case pref.Optional:
- tag = append(tag, "opt")
- case pref.Required:
- tag = append(tag, "req")
- case pref.Repeated:
- tag = append(tag, "rep")
- }
- if fd.IsPacked() {
- tag = append(tag, "packed")
- }
- name := string(fd.Name())
- if fd.Kind() == pref.GroupKind {
- // The name of the FieldDescriptor for a group field is
- // lowercased. To find the original capitalization, we
- // look in the field's MessageType.
- name = string(fd.Message().Name())
- }
- tag = append(tag, "name="+name)
- if jsonName := fd.JSONName(); jsonName != "" && jsonName != name && !fd.IsExtension() {
- // NOTE: The jsonName != name condition is suspect, but it preserve
- // the exact same semantics from the previous generator.
- tag = append(tag, "json="+jsonName)
- }
- if fd.IsWeak() {
- tag = append(tag, "weak="+string(fd.Message().FullName()))
- }
- // The previous implementation does not tag extension fields as proto3,
- // even when the field is defined in a proto3 file. Match that behavior
- // for consistency.
- if fd.Syntax() == pref.Proto3 && !fd.IsExtension() {
- tag = append(tag, "proto3")
- }
- if fd.Kind() == pref.EnumKind && enumName != "" {
- tag = append(tag, "enum="+enumName)
- }
- if fd.ContainingOneof() != nil {
- tag = append(tag, "oneof")
- }
- // This must appear last in the tag, since commas in strings aren't escaped.
- if fd.HasDefault() {
- def, _ := defval.Marshal(fd.Default(), fd.DefaultEnumValue(), fd.Kind(), defval.GoTag)
- tag = append(tag, "def="+def)
- }
- return strings.Join(tag, ",")
- }
|