-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathoptions_helper.go
149 lines (126 loc) · 4.34 KB
/
options_helper.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
package trcache
import (
"go.uber.org/multierr"
)
// parse options
// ParseOptions parses and applies a list of options, in order of appearance, and returns an error if any of
// the parameters was not accepted.
// If options of [OptionChecker] type exist in the list, the error is reported to them instead of being returned to
// the caller. See [OptionChecker] for details.
func ParseOptions[OA ~[]IOption[TO], TO any](obj any, options ...OA) ParseOptionsResult {
checkers := parseOptionsCheckers(options...)
var err error
for _, optinstance := range options {
for _, opt := range optinstance {
if !opt.ApplyCacheOpt(obj) {
err = multierr.Append(err, NewOptionNotSupportedError(opt))
} else {
for _, chk := range checkers {
chk.CheckCacheOpt(opt)
}
}
}
}
var retErr error
if len(checkers) == 0 {
retErr = err
}
return ParseOptionsResult{
err: retErr,
selfErr: err,
}
}
// ParseOptionsResult is the result of [ParseOptions]
type ParseOptionsResult struct {
err, selfErr error
}
// Err returns the options parsing error, if any.
func (r ParseOptionsResult) Err() error {
return r.err
}
// SelfErr returns the options parsing error, if any, even if it was only sent to an [OptionChecker].
func (r ParseOptionsResult) SelfErr() error {
return r.selfErr
}
// ConcatOptions concatenates multiple option lists.
func ConcatOptions[S ~[]O, O Option](options ...S) S {
var ret S
for _, opt := range options {
ret = append(ret, opt...)
}
return ret
}
// ParseOptionsChecker parses the options using the passed [OptionChecker] to report usage.
func ParseOptionsChecker[O IOption[TO], TO any](checker OptionChecker[O, TO], obj any) ParseOptionsResult {
return ParseOptions(obj, ConcatOptionsChecker[O, TO](checker, checker.CheckCacheOptList()))
}
// ConcatOptionsChecker concatenates multiple option lists and prepends an [OptionChecker] to it.
func ConcatOptionsChecker[O IOption[TO], TO any](checker OptionChecker[O, TO], options ...[]IOption[TO]) []IOption[TO] {
return append([]IOption[TO]{checker}, ConcatOptions(options...)...)
}
// ForwardOptionsChecker returns the list of options from checker prepended by the checker itself.
func ForwardOptionsChecker[O IOption[TO], TO any](checker OptionChecker[O, TO]) []IOption[TO] {
return append([]IOption[TO]{checker}, checker.CheckCacheOptList()...)
}
// OptionChecker is a special option type that is used to postpone checking the usage of options,
// to account for when the same options are used in multiple calls and we want to postpone the checking
// of valid options only at the end.
type OptionChecker[O IOption[TO], TO any] interface {
IOption[TO]
CheckCacheOpt(opt Option)
CheckCacheError() error
CheckCacheOptList() []IOption[TO]
}
// NewOptionChecker creates a new [OptionChecker] concatenating the list of options to check into it.
func NewOptionChecker[OA ~[]IOption[TO], TO any](options ...OA) OptionChecker[IOption[TO], TO] {
return &optionCheckerImpl[IOption[TO], TO]{
check: ConcatOptions(options...),
}
}
// optionCheckerImpl is an implementation of [OptionChecker].
type optionCheckerImpl[O IOption[TO], TO any] struct {
IIsOption[TO]
check []IOption[TO]
optns map[uint64]Option
}
func (o *optionCheckerImpl[O, TO]) ApplyCacheOpt(a any) bool {
return true
}
func (o *optionCheckerImpl[O, TO]) CacheOptName() string {
return "checker"
}
func (o *optionCheckerImpl[O, TO]) CacheOptHash() uint64 {
return 1
}
func (o *optionCheckerImpl[O, TO]) CheckCacheOpt(opt Option) {
if o.optns == nil {
o.optns = map[uint64]Option{}
}
if _, ok := o.optns[opt.CacheOptHash()]; !ok {
o.optns[opt.CacheOptHash()] = opt
}
}
func (o *optionCheckerImpl[O, TO]) CheckCacheError() error {
var err error
for _, opt := range o.check {
if _, ok := o.optns[opt.CacheOptHash()]; !ok {
err = multierr.Append(err, NewOptionNotSupportedError(opt))
}
}
return err
}
func (o *optionCheckerImpl[O, TO]) CheckCacheOptList() []IOption[TO] {
return o.check
}
// parseOptionsCheckers returns [OptionChecker] implementations from a list of options.
func parseOptionsCheckers[OA ~[]IOption[TO], TO any](options ...OA) []OptionChecker[IOption[TO], TO] {
var checkers []OptionChecker[IOption[TO], TO]
for _, optinstance := range options {
for _, opt := range optinstance {
if oc, ok := any(opt).(OptionChecker[IOption[TO], TO]); ok {
checkers = append(checkers, oc)
}
}
}
return checkers
}