-
Notifications
You must be signed in to change notification settings - Fork 0
/
veil.go
181 lines (139 loc) · 3.5 KB
/
veil.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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package veil
import (
"fmt"
"reflect"
)
// Veil represents a veil instance.
type Veil struct {
rules []Rule
}
// NewVeil creates a new veil instance.
func NewVeil(rules []Rule) (Veil, error) {
err := validate(rules)
if err != nil {
return Veil{}, err
}
return Veil{
rules: rules,
}, nil
}
// Rules returns the set of rules attached to the veil instance.
func (v *Veil) Rules() []Rule {
return v.rules
}
// Process returns a processed set of inputs against the rule set.
func (v *Veil) Process(inputs ...interface{}) ([]interface{}, error) {
var outputs []interface{}
for _, input := range inputs {
p, err := v.process(input)
if err != nil {
return nil, err
}
outputs = append(outputs, p)
}
return outputs, nil
}
// process processes the given type.
func (v *Veil) process(input interface{}) (interface{}, error) {
// check for a stringer implementation
s, ok := input.(fmt.Stringer)
if ok {
return s.String(), nil
}
switch input.(type) {
case string,
int, int8, int16, int32, int64,
uint, uint8, uint16, uint32, uint64,
float32, float64:
return v.processString(fmt.Sprintf("%v", input))
default:
return v.processComposite(input)
}
}
// processComposite processes composite types such as structs, maps and slices.
func (v *Veil) processComposite(input interface{}) (interface{}, error) {
switch reflect.TypeOf(input).Kind() {
case reflect.Struct:
return v.processStruct(input)
case reflect.Map:
return v.processMap(input)
case reflect.Array,
reflect.Slice:
return v.processSlice(input)
default:
return v.process(fmt.Sprintf("%+v", input))
}
}
// ProcessString processes the given string against the rule set.
// TODO: need to work on the pattern overlapping issue
func (v *Veil) processString(input string) (string, error) {
for _, rule := range v.rules {
input = rule.patternRx.ReplaceAllStringFunc(input, rule.action)
}
return input, nil
}
// processStruct process the given struct.
//
// Structs will be converted to maps for processing convenience.
// Precedence is given to veil tags of struct fields if there are any.
func (v *Veil) processStruct(input interface{}) (interface{}, error) {
s := make(map[string]interface{})
typ := reflect.TypeOf(input)
val := reflect.ValueOf(input)
for i := 0; i < val.NumField(); i++ {
fTyp := typ.Field(i)
fVal := val.Field(i)
// process tags
switch fTyp.Tag.Get("veil") {
case tagHide:
continue
case tagObscure:
s[fTyp.Name] = ActionObscureFunc("")
continue
case tagMask:
s[fTyp.Name] = ActionMaskFunc(fmt.Sprintf("%v", fVal))
continue
default:
var f interface{}
if fVal.CanInterface() {
f = fVal.Interface()
} else {
f = fmt.Sprintf("%v", fVal)
}
out, err := v.process(f)
if err != nil {
return nil, err
}
s[fTyp.Name] = out
}
}
return s, nil
}
// processMap process the given map.
func (v *Veil) processMap(input interface{}) (interface{}, error) {
m := make(map[interface{}]interface{})
iter := reflect.ValueOf(input).MapRange()
for iter.Next() {
key := iter.Key()
val := iter.Value()
out, err := v.process(val.Interface())
if err != nil {
return nil, err
}
m[key.Interface()] = out
}
return m, nil
}
// processSlice process the given slice.
func (v *Veil) processSlice(input interface{}) (interface{}, error) {
var s []interface{}
val := reflect.ValueOf(input)
for i := 0; i < val.Len(); i++ {
out, err := v.process(val.Index(i).Interface())
if err != nil {
return nil, err
}
s = append(s, out)
}
return s, nil
}