256
|
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 |
|
260
|
14 |
"google.golang.org/protobuf/internal/encoding/defval" |
|
15 |
"google.golang.org/protobuf/internal/filedesc" |
256
|
16 |
"google.golang.org/protobuf/internal/strs" |
260
|
17 |
"google.golang.org/protobuf/reflect/protoreflect" |
256
|
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. |
260
|
32 |
func Unmarshal(tag string, goType reflect.Type, evs protoreflect.EnumValueDescriptors) protoreflect.FieldDescriptor { |
|
33 |
f := new(filedesc.Field) |
|
34 |
f.L0.ParentFile = filedesc.SurrogateProto2 |
256
|
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="): |
260
|
42 |
f.L0.FullName = protoreflect.FullName(s[len("name="):]) |
256
|
43 |
case strings.Trim(s, "0123456789") == "": |
|
44 |
n, _ := strconv.ParseUint(s, 10, 32) |
260
|
45 |
f.L1.Number = protoreflect.FieldNumber(n) |
256
|
46 |
case s == "opt": |
260
|
47 |
f.L1.Cardinality = protoreflect.Optional |
256
|
48 |
case s == "req": |
260
|
49 |
f.L1.Cardinality = protoreflect.Required |
256
|
50 |
case s == "rep": |
260
|
51 |
f.L1.Cardinality = protoreflect.Repeated |
256
|
52 |
case s == "varint": |
|
53 |
switch goType.Kind() { |
|
54 |
case reflect.Bool: |
260
|
55 |
f.L1.Kind = protoreflect.BoolKind |
256
|
56 |
case reflect.Int32: |
260
|
57 |
f.L1.Kind = protoreflect.Int32Kind |
256
|
58 |
case reflect.Int64: |
260
|
59 |
f.L1.Kind = protoreflect.Int64Kind |
256
|
60 |
case reflect.Uint32: |
260
|
61 |
f.L1.Kind = protoreflect.Uint32Kind |
256
|
62 |
case reflect.Uint64: |
260
|
63 |
f.L1.Kind = protoreflect.Uint64Kind |
256
|
64 |
} |
|
65 |
case s == "zigzag32": |
|
66 |
if goType.Kind() == reflect.Int32 { |
260
|
67 |
f.L1.Kind = protoreflect.Sint32Kind |
256
|
68 |
} |
|
69 |
case s == "zigzag64": |
|
70 |
if goType.Kind() == reflect.Int64 { |
260
|
71 |
f.L1.Kind = protoreflect.Sint64Kind |
256
|
72 |
} |
|
73 |
case s == "fixed32": |
|
74 |
switch goType.Kind() { |
|
75 |
case reflect.Int32: |
260
|
76 |
f.L1.Kind = protoreflect.Sfixed32Kind |
256
|
77 |
case reflect.Uint32: |
260
|
78 |
f.L1.Kind = protoreflect.Fixed32Kind |
256
|
79 |
case reflect.Float32: |
260
|
80 |
f.L1.Kind = protoreflect.FloatKind |
256
|
81 |
} |
|
82 |
case s == "fixed64": |
|
83 |
switch goType.Kind() { |
|
84 |
case reflect.Int64: |
260
|
85 |
f.L1.Kind = protoreflect.Sfixed64Kind |
256
|
86 |
case reflect.Uint64: |
260
|
87 |
f.L1.Kind = protoreflect.Fixed64Kind |
256
|
88 |
case reflect.Float64: |
260
|
89 |
f.L1.Kind = protoreflect.DoubleKind |
256
|
90 |
} |
|
91 |
case s == "bytes": |
|
92 |
switch { |
|
93 |
case goType.Kind() == reflect.String: |
260
|
94 |
f.L1.Kind = protoreflect.StringKind |
256
|
95 |
case goType.Kind() == reflect.Slice && goType.Elem() == byteType: |
260
|
96 |
f.L1.Kind = protoreflect.BytesKind |
256
|
97 |
default: |
260
|
98 |
f.L1.Kind = protoreflect.MessageKind |
256
|
99 |
} |
|
100 |
case s == "group": |
260
|
101 |
f.L1.Kind = protoreflect.GroupKind |
256
|
102 |
case strings.HasPrefix(s, "enum="): |
260
|
103 |
f.L1.Kind = protoreflect.EnumKind |
256
|
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 |
260
|
114 |
f.L1.Message = filedesc.PlaceholderMessage(protoreflect.FullName(s[len("weak="):])) |
256
|
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) |
260
|
120 |
f.L1.Default = filedesc.DefaultValue(v, ev) |
256
|
121 |
case s == "proto3": |
260
|
122 |
f.L0.ParentFile = filedesc.SurrogateProto3 |
256
|
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. |
260
|
129 |
if f.L1.Kind == protoreflect.GroupKind { |
|
130 |
f.L0.FullName = protoreflect.FullName(strings.ToLower(string(f.L0.FullName))) |
256
|
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. |
260
|
143 |
func Marshal(fd protoreflect.FieldDescriptor, enumName string) string { |
256
|
144 |
var tag []string |
|
145 |
switch fd.Kind() { |
260
|
146 |
case protoreflect.BoolKind, protoreflect.EnumKind, protoreflect.Int32Kind, protoreflect.Uint32Kind, protoreflect.Int64Kind, protoreflect.Uint64Kind: |
256
|
147 |
tag = append(tag, "varint") |
260
|
148 |
case protoreflect.Sint32Kind: |
256
|
149 |
tag = append(tag, "zigzag32") |
260
|
150 |
case protoreflect.Sint64Kind: |
256
|
151 |
tag = append(tag, "zigzag64") |
260
|
152 |
case protoreflect.Sfixed32Kind, protoreflect.Fixed32Kind, protoreflect.FloatKind: |
256
|
153 |
tag = append(tag, "fixed32") |
260
|
154 |
case protoreflect.Sfixed64Kind, protoreflect.Fixed64Kind, protoreflect.DoubleKind: |
256
|
155 |
tag = append(tag, "fixed64") |
260
|
156 |
case protoreflect.StringKind, protoreflect.BytesKind, protoreflect.MessageKind: |
256
|
157 |
tag = append(tag, "bytes") |
260
|
158 |
case protoreflect.GroupKind: |
256
|
159 |
tag = append(tag, "group") |
|
160 |
} |
|
161 |
tag = append(tag, strconv.Itoa(int(fd.Number()))) |
|
162 |
switch fd.Cardinality() { |
260
|
163 |
case protoreflect.Optional: |
256
|
164 |
tag = append(tag, "opt") |
260
|
165 |
case protoreflect.Required: |
256
|
166 |
tag = append(tag, "req") |
260
|
167 |
case protoreflect.Repeated: |
256
|
168 |
tag = append(tag, "rep") |
|
169 |
} |
|
170 |
if fd.IsPacked() { |
|
171 |
tag = append(tag, "packed") |
|
172 |
} |
|
173 |
name := string(fd.Name()) |
260
|
174 |
if fd.Kind() == protoreflect.GroupKind { |
256
|
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. |
260
|
192 |
if fd.Syntax() == protoreflect.Proto3 && !fd.IsExtension() { |
256
|
193 |
tag = append(tag, "proto3") |
|
194 |
} |
260
|
195 |
if fd.Kind() == protoreflect.EnumKind && enumName != "" { |
256
|
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 |
} |