...

Source file src/google.golang.org/protobuf/internal/encoding/tag/tag.go

Documentation: google.golang.org/protobuf/internal/encoding/tag

     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  	"google.golang.org/protobuf/internal/encoding/defval"
    15  	"google.golang.org/protobuf/internal/filedesc"
    16  	"google.golang.org/protobuf/internal/strs"
    17  	"google.golang.org/protobuf/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  }
   208  

View as plain text