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

logfmt.*Decoder.ScanKeyval   F

Complexity

Conditions 35

Size

Total Lines 136
Code Lines 102

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 35
eloc 102
nop 0
dl 0
loc 136
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 logfmt.*Decoder.ScanKeyval 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
package logfmt
2
3
import (
4
	"bufio"
5
	"bytes"
6
	"fmt"
7
	"io"
8
	"unicode/utf8"
9
)
10
11
// A Decoder reads and decodes logfmt records from an input stream.
12
type Decoder struct {
13
	pos     int
14
	key     []byte
15
	value   []byte
16
	lineNum int
17
	s       *bufio.Scanner
18
	err     error
19
}
20
21
// NewDecoder returns a new decoder that reads from r.
22
//
23
// The decoder introduces its own buffering and may read data from r beyond
24
// the logfmt records requested.
25
func NewDecoder(r io.Reader) *Decoder {
26
	dec := &Decoder{
27
		s: bufio.NewScanner(r),
28
	}
29
	return dec
30
}
31
32
// ScanRecord advances the Decoder to the next record, which can then be
33
// parsed with the ScanKeyval method. It returns false when decoding stops,
34
// either by reaching the end of the input or an error. After ScanRecord
35
// returns false, the Err method will return any error that occurred during
36
// decoding, except that if it was io.EOF, Err will return nil.
37
func (dec *Decoder) ScanRecord() bool {
38
	if dec.err != nil {
39
		return false
40
	}
41
	if !dec.s.Scan() {
42
		dec.err = dec.s.Err()
43
		return false
44
	}
45
	dec.lineNum++
46
	dec.pos = 0
47
	return true
48
}
49
50
// ScanKeyval advances the Decoder to the next key/value pair of the current
51
// record, which can then be retrieved with the Key and Value methods. It
52
// returns false when decoding stops, either by reaching the end of the
53
// current record or an error.
54
func (dec *Decoder) ScanKeyval() bool {
55
	dec.key, dec.value = nil, nil
56
	if dec.err != nil {
57
		return false
58
	}
59
60
	line := dec.s.Bytes()
61
62
	// garbage
63
	for p, c := range line[dec.pos:] {
64
		if c > ' ' {
65
			dec.pos += p
66
			goto key
67
		}
68
	}
69
	dec.pos = len(line)
70
	return false
71
72
key:
73
	const invalidKeyError = "invalid key"
74
75
	start, multibyte := dec.pos, false
76
	for p, c := range line[dec.pos:] {
77
		switch {
78
		case c == '=':
79
			dec.pos += p
80
			if dec.pos > start {
81
				dec.key = line[start:dec.pos]
82
				if multibyte && bytes.ContainsRune(dec.key, utf8.RuneError) {
83
					dec.syntaxError(invalidKeyError)
84
					return false
85
				}
86
			}
87
			if dec.key == nil {
88
				dec.unexpectedByte(c)
89
				return false
90
			}
91
			goto equal
92
		case c == '"':
93
			dec.pos += p
94
			dec.unexpectedByte(c)
95
			return false
96
		case c <= ' ':
97
			dec.pos += p
98
			if dec.pos > start {
99
				dec.key = line[start:dec.pos]
100
				if multibyte && bytes.ContainsRune(dec.key, utf8.RuneError) {
101
					dec.syntaxError(invalidKeyError)
102
					return false
103
				}
104
			}
105
			return true
106
		case c >= utf8.RuneSelf:
107
			multibyte = true
108
		}
109
	}
110
	dec.pos = len(line)
111
	if dec.pos > start {
112
		dec.key = line[start:dec.pos]
113
		if multibyte && bytes.ContainsRune(dec.key, utf8.RuneError) {
114
			dec.syntaxError(invalidKeyError)
115
			return false
116
		}
117
	}
118
	return true
119
120
equal:
121
	dec.pos++
122
	if dec.pos >= len(line) {
123
		return true
124
	}
125
	switch c := line[dec.pos]; {
126
	case c <= ' ':
127
		return true
128
	case c == '"':
129
		goto qvalue
130
	}
131
132
	// value
133
	start = dec.pos
134
	for p, c := range line[dec.pos:] {
135
		switch {
136
		case c == '=' || c == '"':
137
			dec.pos += p
138
			dec.unexpectedByte(c)
139
			return false
140
		case c <= ' ':
141
			dec.pos += p
142
			if dec.pos > start {
143
				dec.value = line[start:dec.pos]
144
			}
145
			return true
146
		}
147
	}
148
	dec.pos = len(line)
149
	if dec.pos > start {
150
		dec.value = line[start:dec.pos]
151
	}
152
	return true
153
154
qvalue:
155
	const (
156
		untermQuote  = "unterminated quoted value"
157
		invalidQuote = "invalid quoted value"
158
	)
159
160
	hasEsc, esc := false, false
161
	start = dec.pos
162
	for p, c := range line[dec.pos+1:] {
163
		switch {
164
		case esc:
165
			esc = false
166
		case c == '\\':
167
			hasEsc, esc = true, true
168
		case c == '"':
169
			dec.pos += p + 2
170
			if hasEsc {
171
				v, ok := unquoteBytes(line[start:dec.pos])
172
				if !ok {
173
					dec.syntaxError(invalidQuote)
174
					return false
175
				}
176
				dec.value = v
177
			} else {
178
				start++
179
				end := dec.pos - 1
180
				if end > start {
181
					dec.value = line[start:end]
182
				}
183
			}
184
			return true
185
		}
186
	}
187
	dec.pos = len(line)
188
	dec.syntaxError(untermQuote)
189
	return false
190
}
191
192
// Key returns the most recent key found by a call to ScanKeyval. The returned
193
// slice may point to internal buffers and is only valid until the next call
194
// to ScanRecord.  It does no allocation.
195
func (dec *Decoder) Key() []byte {
196
	return dec.key
197
}
198
199
// Value returns the most recent value found by a call to ScanKeyval. The
200
// returned slice may point to internal buffers and is only valid until the
201
// next call to ScanRecord.  It does no allocation when the value has no
202
// escape sequences.
203
func (dec *Decoder) Value() []byte {
204
	return dec.value
205
}
206
207
// Err returns the first non-EOF error that was encountered by the Scanner.
208
func (dec *Decoder) Err() error {
209
	return dec.err
210
}
211
212
func (dec *Decoder) syntaxError(msg string) {
213
	dec.err = &SyntaxError{
214
		Msg:  msg,
215
		Line: dec.lineNum,
216
		Pos:  dec.pos + 1,
217
	}
218
}
219
220
func (dec *Decoder) unexpectedByte(c byte) {
221
	dec.err = &SyntaxError{
222
		Msg:  fmt.Sprintf("unexpected %q", c),
223
		Line: dec.lineNum,
224
		Pos:  dec.pos + 1,
225
	}
226
}
227
228
// A SyntaxError represents a syntax error in the logfmt input stream.
229
type SyntaxError struct {
230
	Msg  string
231
	Line int
232
	Pos  int
233
}
234
235
func (e *SyntaxError) Error() string {
236
	return fmt.Sprintf("logfmt syntax error at pos %d on line %d: %s", e.Pos, e.Line, e.Msg)
237
}
238