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