-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathresolvers.go
140 lines (125 loc) · 3.84 KB
/
resolvers.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
package spectagular
import (
"reflect"
"time"
)
// StructTagOptionUnmarshaler is an interface used to convert a string value extracted
// from a field's struct tag options and convert it to its expected value. It should also
// return any errors involved with processing if any.
type StructTagOptionUnmarshaler interface {
UnmarshalTagOption(field reflect.StructField, value string) (reflect.Value, error)
}
// nameResolver is used to parse tags that use the first value as a "name"
// and default to the field name (i.e. json, yaml, etc.)
type nameResolver struct {
resolver StructTagOptionUnmarshaler
}
func (n *nameResolver) UnmarshalTagOption(field reflect.StructField, value string) (reflect.Value, error) {
if value == EmptyTag {
return n.resolver.UnmarshalTagOption(field, field.Name)
}
return n.resolver.UnmarshalTagOption(field, value)
}
// boolResolver is used to parse tags of boolean values. if the key is present it is set to true
type boolResolver struct {
key string
}
func (b *boolResolver) UnmarshalTagOption(field reflect.StructField, value string) (reflect.Value, error) {
if value == b.key {
return reflect.ValueOf(true), nil
}
return convertToValue(value, reflect.Bool)
}
// pointerResolver resolves a value and returns a pointer to it
type pointerResolver struct {
resolver StructTagOptionUnmarshaler
underlyingType reflect.Type
}
func (p *pointerResolver) UnmarshalTagOption(field reflect.StructField, valueStr string) (reflect.Value, error) {
v, err := p.resolver.UnmarshalTagOption(field, valueStr)
if err != nil {
return reflect.ValueOf(nil), err
}
value := reflect.New(p.underlyingType.Elem())
value.Elem().Set(v.Convert(p.underlyingType.Elem()))
return value, err
}
// arrayResolver is used to parse anything as an array
type sliceResolver struct {
resolver StructTagOptionUnmarshaler
underlyingType reflect.Type
}
func (s *sliceResolver) UnmarshalTagOption(field reflect.StructField, tag string) (reflect.Value, error) {
valueStr := ""
value := reflect.MakeSlice(reflect.SliceOf(s.underlyingType), 0, 0)
if len(tag) > 0 {
if tag[0] == ',' {
tag = "," + tag
}
if tag[len(tag)-1] == ',' {
tag += ","
}
}
var err error
for tag != EmptyTag {
if tag[0] == ',' {
tag = tag[1:]
}
tag, valueStr, err = getNextTagValue(tag)
if err != nil {
return reflect.ValueOf(nil), err
}
val, err := s.resolver.UnmarshalTagOption(field, valueStr)
if err != nil {
return reflect.ValueOf(nil), err
}
value = reflect.Append(value, val)
}
return value, nil
}
// durationResolver is used to parse a duration string
type durationResolver struct{}
func (d *durationResolver) UnmarshalTagOption(field reflect.StructField, value string) (reflect.Value, error) {
dur, err := time.ParseDuration(value)
return reflect.ValueOf(dur), err
}
// defaultResolver is used to parse any other values
type defaultResolver struct {
kind reflect.Kind
}
func (d *defaultResolver) UnmarshalTagOption(field reflect.StructField, value string) (reflect.Value, error) {
return convertToValue(value, d.kind)
}
func getResolver(fType reflect.Type, name string) StructTagOptionUnmarshaler {
if name == NameTag {
return &nameResolver{
resolver: getResolver(fType, ""),
}
}
if fType.Implements(reflect.TypeOf((*StructTagOptionUnmarshaler)(nil)).Elem()) {
return reflect.New(fType).Interface().(StructTagOptionUnmarshaler)
}
if fType == reflect.TypeOf(*new(time.Duration)) {
return &durationResolver{}
}
if fType.Kind() == reflect.Slice {
return &sliceResolver{
resolver: getResolver(fType.Elem(), name),
underlyingType: fType.Elem(),
}
}
if fType.Kind() == reflect.Pointer {
return &pointerResolver{
resolver: getResolver(fType.Elem(), name),
underlyingType: fType,
}
}
if fType.Kind() == reflect.Bool {
return &boolResolver{
key: name,
}
}
return &defaultResolver{
kind: fType.Kind(),
}
}