/
transform.go
135 lines (120 loc) · 3.45 KB
/
transform.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
// Copyright (c) 2016 Palantir Technologies. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package transform
import (
"reflect"
)
// Rules is a slice where the elements are unary functions like func(*big.Float)json.Number.
type Rules []interface{}
func (rules Rules) Apply(in interface{}) interface{} {
result := rules.apply(reflect.TypeOf(in), reflect.ValueOf(in))
if result.IsValid() {
return result.Interface()
}
return nil
}
func (rules Rules) apply(target reflect.Type, in reflect.Value) reflect.Value {
if !in.IsValid() {
return in
}
// find applicable rule
for _, rule := range rules {
if in.Type().AssignableTo(reflect.TypeOf(rule).In(0)) {
return applyRule(rule, target, in)
}
}
switch in.Kind() {
case reflect.Array:
return rules.applyArray(in)
case reflect.Interface:
return rules.applyInterface(target, in)
case reflect.Map:
return rules.applyMap(in)
case reflect.Ptr:
return rules.applyPointer(in)
case reflect.Slice:
return rules.applySlice(in)
case reflect.Struct:
return rules.applyStruct(in)
default:
return in
}
}
func (rules Rules) applyArray(in reflect.Value) reflect.Value {
result := reflect.New(reflect.ArrayOf(in.Len(), in.Type().Elem())).Elem()
for i := 0; i < in.Len(); i++ {
newV := rules.apply(in.Type().Elem(), in.Index(i))
result.Index(i).Set(newV)
}
return result
}
func (rules Rules) applyInterface(target reflect.Type, in reflect.Value) reflect.Value {
if in.IsNil() {
return in
}
return rules.apply(target, in.Elem())
}
func (rules Rules) applyMap(in reflect.Value) reflect.Value {
if in.IsNil() {
return in
}
result := reflect.MakeMap(in.Type())
for _, k := range in.MapKeys() {
newK := rules.apply(in.Type().Key(), k)
newV := rules.apply(in.Type().Elem(), in.MapIndex(k))
result.SetMapIndex(newK, newV)
}
return result
}
func (rules Rules) applyPointer(in reflect.Value) reflect.Value {
if in.IsNil() {
return in
}
ptr := reflect.New(in.Type()).Elem() // create a pointer of the right type
ptr.Set(reflect.New(in.Type().Elem())) // create a value for it to point to
ptr.Elem().Set(rules.apply(in.Type().Elem(), in.Elem()))
return ptr
}
func (rules Rules) applySlice(in reflect.Value) reflect.Value {
if in.IsNil() {
return in
}
result := reflect.MakeSlice(in.Type(), in.Len(), in.Cap())
for i := 0; i < in.Len(); i++ {
newV := rules.apply(in.Type().Elem(), in.Index(i))
result.Index(i).Set(newV)
}
return result
}
func (rules Rules) applyStruct(in reflect.Value) reflect.Value {
result := reflect.New(in.Type()).Elem()
for i := 0; i < in.NumField(); i++ {
structField := in.Type().Field(i)
if structField.PkgPath != "" && !structField.Anonymous {
return in // unexported field, cannot safely copy struct
}
newV := rules.apply(structField.Type, in.Field(i))
result.Field(i).Set(newV)
}
return result
}
func applyRule(rule interface{}, target reflect.Type, in reflect.Value) reflect.Value {
result := reflect.ValueOf(rule).Call([]reflect.Value{in})[0]
if !result.IsValid() && !canBeNil(target) {
return in
}
if result.IsValid() && !result.Type().AssignableTo(target) {
return in
}
return result
}
// canBeNil reports whether an untyped nil can be assigned to the type. See reflect.Zero.
func canBeNil(typ reflect.Type) bool {
switch typ.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
return true
default:
return false
}
}