// 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 ( ) 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. // // This function is a best effort attempt; parsing errors are ignored. func ( string, reflect.Type, protoreflect.EnumValueDescriptors) protoreflect.FieldDescriptor { := new(filedesc.Field) .L0.ParentFile = filedesc.SurrogateProto2 .L1.EditionFeatures = .L0.ParentFile.L1.EditionFeatures for len() > 0 { := strings.IndexByte(, ',') if < 0 { = len() } switch := [:]; { case strings.HasPrefix(, "name="): .L0.FullName = protoreflect.FullName([len("name="):]) case strings.Trim(, "0123456789") == "": , := strconv.ParseUint(, 10, 32) .L1.Number = protoreflect.FieldNumber() case == "opt": .L1.Cardinality = protoreflect.Optional case == "req": .L1.Cardinality = protoreflect.Required case == "rep": .L1.Cardinality = protoreflect.Repeated case == "varint": switch .Kind() { case reflect.Bool: .L1.Kind = protoreflect.BoolKind case reflect.Int32: .L1.Kind = protoreflect.Int32Kind case reflect.Int64: .L1.Kind = protoreflect.Int64Kind case reflect.Uint32: .L1.Kind = protoreflect.Uint32Kind case reflect.Uint64: .L1.Kind = protoreflect.Uint64Kind } case == "zigzag32": if .Kind() == reflect.Int32 { .L1.Kind = protoreflect.Sint32Kind } case == "zigzag64": if .Kind() == reflect.Int64 { .L1.Kind = protoreflect.Sint64Kind } case == "fixed32": switch .Kind() { case reflect.Int32: .L1.Kind = protoreflect.Sfixed32Kind case reflect.Uint32: .L1.Kind = protoreflect.Fixed32Kind case reflect.Float32: .L1.Kind = protoreflect.FloatKind } case == "fixed64": switch .Kind() { case reflect.Int64: .L1.Kind = protoreflect.Sfixed64Kind case reflect.Uint64: .L1.Kind = protoreflect.Fixed64Kind case reflect.Float64: .L1.Kind = protoreflect.DoubleKind } case == "bytes": switch { case .Kind() == reflect.String: .L1.Kind = protoreflect.StringKind case .Kind() == reflect.Slice && .Elem() == byteType: .L1.Kind = protoreflect.BytesKind default: .L1.Kind = protoreflect.MessageKind } case == "group": .L1.Kind = protoreflect.GroupKind case strings.HasPrefix(, "enum="): .L1.Kind = protoreflect.EnumKind case strings.HasPrefix(, "json="): := [len("json="):] if != strs.JSONCamelCase(string(.L0.FullName.Name())) { .L1.StringName.InitJSON() } case == "packed": .L1.EditionFeatures.IsPacked = true case strings.HasPrefix(, "def="): // The default tag is special in that everything afterwards is the // default regardless of the presence of commas. , = [len("def="):], len() , , := defval.Unmarshal(, .L1.Kind, , defval.GoTag) .L1.Default = filedesc.DefaultValue(, ) case == "proto3": .L0.ParentFile = filedesc.SurrogateProto3 } = strings.TrimPrefix([:], ",") } // The generator uses the group message name instead of the field name. // We obtain the real field name by lowercasing the group name. if .L1.Kind == protoreflect.GroupKind { .L0.FullName = protoreflect.FullName(strings.ToLower(string(.L0.FullName))) } return } // 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 ( protoreflect.FieldDescriptor, string) string { var []string switch .Kind() { case protoreflect.BoolKind, protoreflect.EnumKind, protoreflect.Int32Kind, protoreflect.Uint32Kind, protoreflect.Int64Kind, protoreflect.Uint64Kind: = append(, "varint") case protoreflect.Sint32Kind: = append(, "zigzag32") case protoreflect.Sint64Kind: = append(, "zigzag64") case protoreflect.Sfixed32Kind, protoreflect.Fixed32Kind, protoreflect.FloatKind: = append(, "fixed32") case protoreflect.Sfixed64Kind, protoreflect.Fixed64Kind, protoreflect.DoubleKind: = append(, "fixed64") case protoreflect.StringKind, protoreflect.BytesKind, protoreflect.MessageKind: = append(, "bytes") case protoreflect.GroupKind: = append(, "group") } = append(, strconv.Itoa(int(.Number()))) switch .Cardinality() { case protoreflect.Optional: = append(, "opt") case protoreflect.Required: = append(, "req") case protoreflect.Repeated: = append(, "rep") } if .IsPacked() { = append(, "packed") } := string(.Name()) if .Kind() == protoreflect.GroupKind { // The name of the FieldDescriptor for a group field is // lowercased. To find the original capitalization, we // look in the field's MessageType. = string(.Message().Name()) } = append(, "name="+) if := .JSONName(); != "" && != && !.IsExtension() { // NOTE: The jsonName != name condition is suspect, but it preserve // the exact same semantics from the previous generator. = append(, "json="+) } // 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 .Syntax() == protoreflect.Proto3 && !.IsExtension() { = append(, "proto3") } if .Kind() == protoreflect.EnumKind && != "" { = append(, "enum="+) } if .ContainingOneof() != nil { = append(, "oneof") } // This must appear last in the tag, since commas in strings aren't escaped. if .HasDefault() { , := defval.Marshal(.Default(), .DefaultEnumValue(), .Kind(), defval.GoTag) = append(, "def="+) } return strings.Join(, ",") }