-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathtag_modifier.go
136 lines (108 loc) · 3.03 KB
/
tag_modifier.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 config
import (
"fmt"
"reflect"
"unsafe"
"github.com/fatih/structtag"
)
// TagModifier is an interface that rewrites a set of tags for a struct
// field. This interface is used by the ApplyTagModifiers function.
type TagModifier interface {
// AlterFieldTag modifies the tags reference by setting or deleting
// values from the given tag wrapper. The given tag wrapper is the
// parsed version of the tag for the given field. Returns an error
// if there is an internal consistency problem.
AlterFieldTag(field reflect.StructField, tags *structtag.Tags) error
}
// ApplyTagModifiers returns a new struct with a dynamic type whose fields
// are equivalent to the given object but whose field tags are run through
// each tag modifier in sequence.
func ApplyTagModifiers(obj interface{}, modifiers ...TagModifier) (modified interface{}, err error) {
modified = obj
for _, modifier := range modifiers {
modified, err = apply(modified, modifier)
if err != nil {
return
}
}
return
}
func apply(p interface{}, modifier TagModifier) (interface{}, error) {
val := reflect.ValueOf(p)
newType, err := makeType(val.Type().Elem(), modifier)
if err != nil {
return nil, err
}
return reflect.NewAt(newType, unsafe.Pointer(val.Pointer())).Interface(), nil
}
func makeType(t reflect.Type, modifier TagModifier) (reflect.Type, error) {
switch t.Kind() {
case reflect.Struct:
return makeStructType(t, modifier)
case reflect.Ptr:
inner, err := makeType(t.Elem(), modifier)
if err != nil {
return nil, err
}
return reflect.PtrTo(inner), nil
case reflect.Array:
inner, err := makeType(t.Elem(), modifier)
if err != nil {
return nil, err
}
return reflect.ArrayOf(t.Len(), inner), nil
case reflect.Slice:
inner, err := makeType(t.Elem(), modifier)
if err != nil {
return nil, err
}
return reflect.SliceOf(inner), nil
case reflect.Map:
key, err := makeType(t.Key(), modifier)
if err != nil {
return nil, err
}
val, err := makeType(t.Elem(), modifier)
if err != nil {
return nil, err
}
return reflect.MapOf(key, val), nil
case
reflect.Chan,
reflect.Func,
reflect.UnsafePointer,
reflect.Interface:
return nil, fmt.Errorf("unsupported type %s", t.Kind().String())
default:
return t, nil
}
}
func makeStructType(structType reflect.Type, modifier TagModifier) (reflect.Type, error) {
fields := []reflect.StructField{}
for i := 0; i < structType.NumField(); i++ {
field := structType.Field(i)
if !isExported(field.Name) {
continue
}
fieldType, err := makeType(field.Type, modifier)
if err != nil {
return nil, err
}
field.Type = fieldType
if tags, ok := getTags(field); ok {
if err := modifier.AlterFieldTag(field, tags); err != nil {
return nil, err
}
field.Tag = reflect.StructTag(tags.String())
}
fields = append(fields, field)
}
return reflect.StructOf(fields), nil
}
func getTags(field reflect.StructField) (*structtag.Tags, bool) {
tags, err := structtag.Parse(string(field.Tag))
if err == nil {
return tags, true
}
return nil, false
}