-
Notifications
You must be signed in to change notification settings - Fork 0
/
faces.go
135 lines (115 loc) · 2.88 KB
/
faces.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
// Package faces filters a structure using field tags
package faces
import (
"reflect"
"strings"
)
const revealTag = "faces"
var (
revealableTypes = []reflect.Kind{
reflect.Bool,
reflect.Int,
reflect.Int8,
reflect.Int16,
reflect.Int32,
reflect.Int64,
reflect.Uint,
reflect.Uint8,
reflect.Uint16,
reflect.Uint32,
reflect.Uint64,
reflect.Float32,
reflect.Float64,
reflect.Complex64,
reflect.Complex128,
reflect.String,
}
)
// Reveal reveals faces of the input based on tags. The input can be:
//
// - A pointer to a structure. In this case, the function will be applied
// directly to the structure.
//
// - An array or a slice. In this case, the function will be applied to each
// element.
//
// - A map. In this case, the function will be applied to each value (of a
// key/value pair).
//
// Examples of struct field tags and their meanings:
//
// // This field will be revealed with the public face of the structure.
// Field string `faces:"public"`
//
// // This field will be revealed with the private and the public faces of the structure.
// Field string `faces:"private,public"`
//
// // This field will be revealed with any faces of the structure.
// Field string
func Reveal(input interface{}, tags ...string) {
if tags == nil {
return
}
v := reflect.ValueOf(input)
revealValue(v, tags...)
}
func revealValue(v reflect.Value, tags ...string) {
// Get the underlying elements if the value is a pointer
for v.Kind() == reflect.Ptr {
v = v.Elem()
}
// If v is an array or a slice, reveal each element
if v.Kind() == reflect.Array || v.Kind() == reflect.Slice {
for i := 0; i < v.Len(); i++ {
revealValue(v.Index(i), tags...)
}
}
// If v is a map, reveal each value
if v.Kind() == reflect.Map {
for _, vElement := range v.MapKeys() {
revealValue(v.MapIndex(vElement), tags...)
}
}
// Finally, make sure that the value is a structure
if v.Kind() != reflect.Struct {
return
}
// Loop over the fields of the structure
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
if !revealable(field) {
revealValue(field, tags...)
}
structField := v.Type().Field(i)
// Unexported fields are not accessible
if structField.PkgPath != "" {
continue
}
fieldTags := structField.Tag.Get(revealTag)
// Keep the original value if the field has no tags and reset the field if
// it does not match any of the given tags
if field.CanSet() &&
fieldTags != "" &&
!matchTags(strings.Split(fieldTags, ","), tags) {
field.Set(reflect.New(structField.Type).Elem())
}
}
}
func revealable(v reflect.Value) bool {
for _, revealableType := range revealableTypes {
if revealableType == v.Kind() {
return true
}
}
return false
}
func matchTags(fieldTags []string, tags []string) bool {
for _, fieldTag := range fieldTags {
for _, tag := range tags {
if fieldTag == tag {
return true
}
}
}
return false
}