-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlayers.go
114 lines (95 loc) · 2.93 KB
/
layers.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
package cake
import (
"fmt"
"reflect"
"strings"
)
func getLayerValue(layer any) (reflect.Value, bool) {
if layer == nil {
return reflect.Value{}, false
}
val := reflect.ValueOf(layer)
if val.IsZero() {
return val, false
} else if val.IsNil() {
return val, false
} else if val.Kind() != reflect.Ptr {
return val, false
}
return val, true
}
// If returns the layer if cond is true, otherwise it returns a zero value of the layer's type.
// This is useful for skipping entire layers based on a condition.
func If[T interface{}](cond bool, layer T) T {
if cond {
return layer
} else {
return *new(T)
}
}
// IfCallback returns the result of the layer function if cond is true, otherwise it returns a
// zero value of the layer's type. This is useful for skipping entire layers based on a condition
// when the layer is expensive to construct.
func IfCallback[T interface{}](cond bool, layer func() T) T {
if cond {
return layer()
} else {
return *new(T)
}
}
// Layered takes base layer T and a list of additional layers and constructs a single T value
// that is a wrapper around the base layer. The layers are applied in order, with the last layer
// being the outermost layer. This is useful for wrapping a base layer with additional functionality
// without having to modify the base layer.
func Layered[T interface{}](base T, layers ...T) (T, error) {
if len(layers) == 0 {
return base, nil
}
var entryLayer = -1
// get the name of T, which is the interface that all layers implement
var interfaceName = strings.Split(fmt.Sprintf("%T", new(T)), ".")[1]
// iterate through all provided layers.
for i := 0; i < len(layers); i++ {
// layers should be a pointer to a struct that implements T
curLayerValue, ok := getLayerValue(layers[i])
if !ok {
continue
}
if entryLayer == -1 {
entryLayer = i
}
curLayerValue = curLayerValue.Elem()
// get a reference to the value of the embedded field that
// implements the interface that T represents
targetField := curLayerValue.FieldByName(interfaceName)
if !targetField.IsValid() || !targetField.CanSet() {
return *new(T), fmt.Errorf("field %s in layer '%T' cannot be set", targetField.String(), layers[i])
}
// if this is the last provided layer, set the embedded field to the base layer
if i == len(layers)-1 {
targetField.Set(reflect.ValueOf(base))
break
}
// now seek the next valid layer
var didSet bool
for j := i + 1; j < len(layers); j++ {
nextLayerValue, ok := getLayerValue(layers[j])
if !ok {
continue
}
didSet = true
targetField.Set(nextLayerValue)
// i will be incremented just after this
// so set it to the previous layer
i = j - 1
break
}
// if after iterating through all the layers we did not set the value of the embedded field,
// set it to the base layer's value and break the loop.
if !didSet {
targetField.Set(reflect.ValueOf(base))
break
}
}
return layers[entryLayer], nil
}