-
Notifications
You must be signed in to change notification settings - Fork 0
/
fieldparser.go
162 lines (146 loc) · 3.22 KB
/
fieldparser.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package sqllogging
import (
"github.com/sirupsen/logrus"
"strconv"
"strings"
"unicode"
"unicode/utf8"
//"unicode"
)
type scanner struct {
input string
pos int // current position of the scanner
}
func (s *scanner) skipWhitespace() {
for i, r := range s.input[s.pos:] {
if !unicode.IsSpace(r) {
s.pos += i
return
}
}
// eof
s.pos = len(s.input)
}
const eof rune = -1
func (s *scanner) peek(offset int) rune {
if s.pos+offset >= len(s.input) {
return eof
} else {
r, _ := utf8.DecodeRuneInString(s.input[s.pos+offset:])
return r
}
}
func parseStringValue(s *scanner) (value string, found bool) {
// We are positioned after the [.
// Read until ], while ]] is treated as an escape and produces ]
// If no trailing single ] is found then found=false is returned
skipnext := false
for i, r := range s.input[s.pos:] {
if skipnext {
skipnext = false
continue
}
if r == ']' {
if s.peek(i+1) == ']' {
skipnext = true
} else {
value = strings.ReplaceAll(s.input[s.pos:s.pos+i], "]]", "]")
s.pos += i + 1
found = true
return
}
}
}
return "", false
}
func parseIntValue(s *scanner) (value int, found bool) {
// problem here is finding the end of the number, otherwise we could just use Atoi
newpos := len(s.input) // eof case
loop:
for i, r := range s.input[s.pos:] {
switch {
case i == 0 && (r == '+' || r == '-'):
case r >= '0' && r <= '9':
case unicode.IsSpace(r):
newpos = s.pos + i
break loop
default:
// encountered something not part of numeric and not whitespace => not found
return 0, false
}
}
value, err := strconv.Atoi(s.input[s.pos:newpos])
if err != nil {
// don't see how this could happen, but fall back safely..
return 0, false
}
s.pos = newpos
found = true
return
}
type keyValue struct {
key string
value interface{}
}
// parses a key; it is only a key if followed by `=[` or `=\d`,
// otherwise return empty string. The position will be after `=[`
// for stringValue and after `=` for intValue
func parseKeyValue(s *scanner) (kv keyValue, found bool) {
var key strings.Builder
var value interface{}
var valueFound bool
oldPos := s.pos
loop:
for i, r := range s.input[s.pos:] {
if unicode.IsLetter(r) {
key.WriteRune(r)
} else if i > 0 && r == '=' {
s.pos += i + 1
nextRune := s.peek(0)
if nextRune == '[' { // string
s.pos++
value, valueFound = parseStringValue(s)
break loop
} else if unicode.IsSpace(nextRune) || nextRune == eof { // nil
value = nil
valueFound = true
break loop
} else { // try for integer
value, valueFound = parseIntValue(s)
break loop
}
} else {
valueFound = false
break loop
}
}
if !valueFound {
s.pos = oldPos
found = false
return
} else {
kv.key = key.String()
kv.value = value
found = true
return
}
}
func parseFields(input string) (fields logrus.Fields, msg string) {
// Top level loop, looking for "key=[value]", skipping but not requiring
// whitespace in-between
s := scanner{input: input}
for {
s.skipWhitespace()
kv, found := parseKeyValue(&s)
if found {
if fields == nil {
fields = make(logrus.Fields)
}
value := kv.value
fields[kv.key] = value
} else {
msg = s.input[s.pos:]
return
}
}
}