Test Failed
Push — main ( 973aa1...436074 )
by Christian
02:37
created

tag.Unmarshal   F

Complexity

Conditions 40

Size

Total Lines 101
Code Lines 85

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 40
eloc 85
nop 3
dl 0
loc 101
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like tag.Unmarshal often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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
}
208