/
prop_key_resolver.go
152 lines (129 loc) · 4.69 KB
/
prop_key_resolver.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
141
142
143
144
145
146
147
148
149
150
151
152
package format
import (
"errors"
"fmt"
"reflect"
"sort"
"strings"
t "github.com/containrrr/shoutrrr/pkg/types"
)
// PropKeyResolver implements the ConfigQueryResolver interface for services that uses key tags for query props
type PropKeyResolver struct {
confValue reflect.Value
keyFields map[string]FieldInfo
keys []string
}
// NewPropKeyResolver creates a new PropKeyResolver and initializes it using the provided config
func NewPropKeyResolver(config t.ServiceConfig) PropKeyResolver {
configNode := GetConfigFormat(config)
items := configNode.Items
keyFields := make(map[string]FieldInfo, len(items))
keys := make([]string, 0, len(items))
for _, item := range items {
field := *item.Field()
for _, key := range field.Keys {
key = strings.ToLower(key)
if key != "" {
keys = append(keys, key)
keyFields[key] = field
}
}
}
sort.Strings(keys)
confValue := reflect.ValueOf(config)
if confValue.Kind() == reflect.Ptr {
confValue = confValue.Elem()
}
return PropKeyResolver{
keyFields: keyFields,
confValue: confValue,
keys: keys,
}
}
// QueryFields returns a list of tagged keys
func (pkr *PropKeyResolver) QueryFields() []string {
return pkr.keys
}
// Get returns the value of a config property tagged with the corresponding key
func (pkr *PropKeyResolver) Get(key string) (string, error) {
if field, found := pkr.keyFields[strings.ToLower(key)]; found {
return GetConfigFieldString(pkr.confValue, field)
}
return "", fmt.Errorf("%v is not a valid config key", key)
}
// Set sets the value of it's bound struct's property, tagged with the corresponding key
func (pkr *PropKeyResolver) Set(key string, value string) error {
return pkr.set(pkr.confValue, key, value)
}
// set sets the value of a target struct tagged with the corresponding key
func (pkr *PropKeyResolver) set(target reflect.Value, key string, value string) error {
if field, found := pkr.keyFields[strings.ToLower(key)]; found {
valid, err := SetConfigField(target, field, value)
if !valid && err == nil {
return errors.New("invalid value for type")
}
return err
}
return fmt.Errorf("%v is not a valid config key %v", key, pkr.keys)
}
// UpdateConfigFromParams mutates the provided config, updating the values from it's corresponding params
// If the provided config is nil, the internal config will be updated instead.
// The error returned is the first error that occurred, subsequent errors are just discarded.
func (pkr *PropKeyResolver) UpdateConfigFromParams(config t.ServiceConfig, params *t.Params) (firstError error) {
confValue := pkr.configValueOrInternal(config)
if params != nil {
for key, val := range *params {
if err := pkr.set(confValue, key, val); err != nil && firstError == nil {
firstError = err
}
}
}
return
}
// SetDefaultProps mutates the provided config, setting the tagged fields with their default values
// If the provided config is nil, the internal config will be updated instead.
// The error returned is the first error that occurred, subsequent errors are just discarded.
func (pkr *PropKeyResolver) SetDefaultProps(config t.ServiceConfig) (firstError error) {
confValue := pkr.configValueOrInternal(config)
for key, info := range pkr.keyFields {
if err := pkr.set(confValue, key, info.DefaultValue); err != nil && firstError == nil {
firstError = err
}
}
return
}
// Bind is called to create a new instance of the PropKeyResolver, with he internal config reference
// set to the provided config. This should only be used for configs of the same type.
func (pkr *PropKeyResolver) Bind(config t.ServiceConfig) PropKeyResolver {
bound := *pkr
bound.confValue = configValue(config)
return bound
}
// GetConfigQueryResolver returns the config itself if it implements ConfigQueryResolver
// otherwise it creates and returns a PropKeyResolver that implements it
func GetConfigQueryResolver(config t.ServiceConfig) t.ConfigQueryResolver {
var resolver t.ConfigQueryResolver
var ok bool
if resolver, ok = config.(t.ConfigQueryResolver); !ok {
pkr := NewPropKeyResolver(config)
resolver = &pkr
}
return resolver
}
// KeyIsPrimary returns whether the key is the primary (and not an alias)
func (pkr *PropKeyResolver) KeyIsPrimary(key string) bool {
return pkr.keyFields[key].Keys[0] == key
}
func (pkr *PropKeyResolver) configValueOrInternal(config t.ServiceConfig) reflect.Value {
if config != nil {
return configValue(config)
}
return pkr.confValue
}
func configValue(config t.ServiceConfig) reflect.Value {
return reflect.Indirect(reflect.ValueOf(config))
}
// IsDefault returns whether the specified key value is the default value
func (pkr *PropKeyResolver) IsDefault(key string, value string) bool {
return pkr.keyFields[key].DefaultValue == value
}