-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmap.go
145 lines (122 loc) · 3.81 KB
/
map.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
package golden
import (
"fmt"
"math"
"regexp"
"time"
)
// replaceTransient replaces all the values in a map whose key is contained in
// the variadic list of transientFields. The replaced value has a stable value
// according to the data type.
func replaceTransient(
original map[string]any,
transientFields ...TransientField,
) map[string]any {
transientLookup := map[string]any{}
for _, field := range transientFields {
transientLookup[field.Key] = field.Replacement
}
replaced := map[string]any{}
for key, value := range original {
// Keep the original value.
replaced[key] = value
// Check if the field is meant to be replaced. If not, continue.
// We also check for wildcard replacements that are meant to replace all
// fields in a slice.
replacement, isTransient := transientLookup[key]
cleanedKey := replaceIndicesInKeys(key)
replacementCleaned, isTransientCleaned := transientLookup[cleanedKey]
if !isTransient && !isTransientCleaned {
// No replacement defined, continue and keep the original value.
continue
}
if isTransientCleaned {
replacement = replacementCleaned
}
// Replace the value with the replacement value.
if replacement != nil {
replaced[key] = replacement
continue
}
// No replacement defined, we fall back to default stable values here
// (based on type).
if stringValue, isString := value.(string); isString {
if _, err := time.Parse(time.RFC3339, stringValue); err == nil {
replaced[key] = StableTime
continue
}
if _, err := time.ParseDuration(stringValue); err == nil {
replaced[key] = StableDuration
continue
}
replaced[key] = StableText
continue
}
if _, isFloat := value.(float64); isFloat {
replaced[key] = StableFloat
continue
}
if _, IsInt := value.(int); IsInt {
replaced[key] = StableInt
continue
}
if _, isBool := value.(bool); isBool {
replaced[key] = StableBool
continue
}
}
return replaced
}
// roundFields rounds all the values in a map whose key is contained in the
// variadic list of roundingConfigs. The rounded value has a stable value
// according to the data type.
func roundFields(
original map[string]any,
roundedFields ...RoundingConfig,
) (map[string]any, error) {
roundingLookup := map[string]int{}
for _, field := range roundedFields {
roundingLookup[field.Key] = field.Precision
}
replaced := map[string]any{}
for key, value := range original {
// Keep the original value.
replaced[key] = value
// Check if the field is meant to be rounded. If not, continue.
// We also check for wildcard replacements that are meant to replace all
// fields in a slice.
cleanedKey := replaceIndicesInKeys(key)
replacement, isRounded := roundingLookup[key]
replacementCleaned, isRoundedCleaned := roundingLookup[cleanedKey]
if !isRounded && !isRoundedCleaned {
// No rounding defined, continue and keep the original value.
continue
}
if isRoundedCleaned {
replacement = replacementCleaned
}
// We don't deal with negative precision values.
if replacement < 0 {
continue
}
// Replace the value with the rounded value.
if _, isFloat := value.(float64); isFloat {
replaced[key] = round(value.(float64), replacement)
continue
}
// If the value was not a float, return an error.
return nil, fmt.Errorf("field %s is not a float", key)
}
return replaced, nil
}
// round rounds a float64 value to a given precision.
func round(value float64, precision int) float64 {
shift := math.Pow(10, float64(precision))
return math.Round(value*shift) / shift
}
var keyIndexMatcher = regexp.MustCompile(`\[\d+\]`)
// replaceIndicesInKeys replaces all the indices in a key with "[]" to make it
// easier to match them with configuration defined in jq-style.
func replaceIndicesInKeys(key string) string {
return keyIndexMatcher.ReplaceAllString(key, "[]")
}