forked from elastic/beats
-
Notifications
You must be signed in to change notification settings - Fork 7
/
mapstr.go
136 lines (119 loc) · 3.11 KB
/
mapstr.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
package common
import (
"encoding/json"
"fmt"
"time"
)
// Commonly used map of things, used in JSON creation and the like.
type MapStr map[string]interface{}
// Eventer defines a type its ability to fill a MapStr.
type Eventer interface {
// Add fields to MapStr.
Event(event MapStr) error
}
// MapStrUnion creates a new MapStr containing the union of the
// key-value pairs of the two maps. If the same key is present in
// both, the key-value pairs from dict2 overwrite the ones from dict1.
func MapStrUnion(dict1 MapStr, dict2 MapStr) MapStr {
dict := MapStr{}
for k, v := range dict1 {
dict[k] = v
}
for k, v := range dict2 {
dict[k] = v
}
return dict
}
// Update copies all the key-value pairs from the
// d map overwriting any existing keys.
func (m MapStr) Update(d MapStr) {
for k, v := range d {
m[k] = v
}
}
// Checks if a timestamp field exists and if it doesn't it adds
// one by using the injected now() function as a time source.
func (m MapStr) EnsureTimestampField(now func() time.Time) error {
ts, exists := m["@timestamp"]
if !exists {
m["@timestamp"] = Time(now())
return nil
}
_, is_common_time := ts.(Time)
if is_common_time {
// already perfect
return nil
}
tstime, is_time := ts.(time.Time)
if is_time {
m["@timestamp"] = Time(tstime)
return nil
}
tsstr, is_string := ts.(string)
if is_string {
var err error
m["@timestamp"], err = ParseTime(tsstr)
return err
}
return fmt.Errorf("Don't know how to convert %v to a Time value", ts)
}
func (m MapStr) EnsureCountField() error {
_, exists := m["count"]
if !exists {
m["count"] = 1
}
return nil
}
// Prints the dict as a json
func (m MapStr) String() string {
bytes, err := json.Marshal(m)
if err != nil {
return fmt.Sprintf("Not valid json: %v", err)
}
return string(bytes)
}
// UnmarshalYAML helps out with the YAML unmarshalling when the target
// variable is a MapStr. The default behavior is to unmarshal nested
// maps to map[interface{}]interface{} values, and such values can't
// be marshalled as JSON.
//
// The keys of map[interface{}]interface{} maps will be converted to
// strings with a %v format string, as will any scalar values that
// aren't already strings (i.e. numbers and boolean values).
//
// Since we want to modify the receiver it needs to be a pointer.
func (ms *MapStr) UnmarshalYAML(unmarshal func(interface{}) error) error {
var result map[interface{}]interface{}
err := unmarshal(&result)
if err != nil {
panic(err)
}
*ms = cleanUpInterfaceMap(result)
return nil
}
func cleanUpInterfaceArray(in []interface{}) []interface{} {
result := make([]interface{}, len(in))
for i, v := range in {
result[i] = cleanUpMapValue(v)
}
return result
}
func cleanUpInterfaceMap(in map[interface{}]interface{}) MapStr {
result := make(MapStr)
for k, v := range in {
result[fmt.Sprintf("%v", k)] = cleanUpMapValue(v)
}
return result
}
func cleanUpMapValue(v interface{}) interface{} {
switch v := v.(type) {
case []interface{}:
return cleanUpInterfaceArray(v)
case map[interface{}]interface{}:
return cleanUpInterfaceMap(v)
case string:
return v
default:
return fmt.Sprintf("%v", v)
}
}