-
Notifications
You must be signed in to change notification settings - Fork 0
/
may_handler.go
191 lines (158 loc) 路 4.44 KB
/
may_handler.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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
package fo
import (
"errors"
"fmt"
"reflect"
"sync"
"go.uber.org/multierr"
)
var (
// errNotOK is the error returned by MayInvokers when the invoked.
errNotOK = errors.New("not ok")
)
// MayHandler is a function that handles the error from May and May* functions.
type MayHandler func(err error, messageArgs ...any)
type Logger interface {
Error(v ...any)
}
// WithLoggerHandler returns a MayHandler that logs the error with the
// given logger.
func WithLoggerHandler(l Logger) MayHandler {
return func(err error, messageArgs ...any) {
l.Error(formatErrorWithMessageArgs(err, messageArgs...))
}
}
// WithLogFuncHandler returns a MayHandler that logs the error with the
// given logFunc which accepts variadic arguments.
func WithLogFuncHandler(logFunc func(...any)) MayHandler {
return func(err error, messageArgs ...any) {
logFunc(formatErrorWithMessageArgs(err, messageArgs...))
}
}
// mayHandlers is a collection of MayHandler.
type mayHandlers struct {
handlers []MayHandler
errs error
mutex sync.Mutex
}
// newMayHandlers creates a new mayHandlers.
func newMayHandlers() *mayHandlers {
return &mayHandlers{
handlers: make([]MayHandler, 0),
}
}
// Use registers the handlers.
func (h *mayHandlers) Use(handler ...MayHandler) {
h.mutex.Lock()
defer h.mutex.Unlock()
h.handlers = append(h.handlers, handler...)
}
// CollectAsError collects error from the invoked result from MayInvoker
// for post error handling.
//
// The error can be extracted with
//
// multierr.Errors().
func (h *mayHandlers) CollectAsError() error {
return h.errs
}
// CollectAsErrors collects errors from the invoked result from
// MayInvoker for post error handling.
//
// The errors can be combined with
//
// multierr.Combine().
func (h *mayHandlers) CollectAsErrors() []error {
if h.errs == nil {
return make([]error, 0)
}
return multierr.Errors(h.errs)
}
// HandleErrors executes the handler with the collected error from
// MayInvoker.
func (h *mayHandlers) HandleErrors(handler func(errs []error)) {
if h.errs == nil {
return
}
handler(multierr.Errors(h.errs))
}
// HandleErrorsWithReturn executes the handler with the collected error from
// MayInvoker, and returns the error that handled by the handler.
func (h *mayHandlers) HandleErrorsWithReturn(handler func(errs []error) error) error {
if h.errs == nil {
return nil
}
return handler(multierr.Errors(h.errs))
}
// messageFromMsgAndArgs constructs the message from the given msgAndArgs.
// If the first argument is a string, it will be used as the message.
// If the first argument is not a string, it will be formatted with
// fmt.Sprintf("%+v", ...) along with the rest of the arguments.
func messageFromMsgAndArgs(msgAndArgs ...any) string {
if len(msgAndArgs) == 1 {
if msgAsStr, ok := msgAndArgs[0].(string); ok {
return msgAsStr
}
return fmt.Sprintf("%+v", msgAndArgs[0])
}
if len(msgAndArgs) > 1 {
msgAsStr, ok := msgAndArgs[0].(string)
if ok {
return fmt.Sprintf(msgAsStr, msgAndArgs[1:]...)
}
return fmt.Sprintf("%+v", msgAndArgs)
}
return ""
}
// formatErrorWithMessageArgs formats the error with the given messageArgs.
// If the error is not nil, it will be wrapped with the message.
func formatErrorWithMessageArgs(err error, messageArgs ...any) error {
if err == nil {
return nil
}
message := messageFromMsgAndArgs(messageArgs...)
if errors.Is(err, errNotOK) && message != "" {
return errors.New(message)
}
if message != "" {
return fmt.Errorf("%s: %w", message, err)
}
return err
}
// formatError formats the error.
// It supports two types of error:
// 1. bool: if the bool is false, it will be converted to errNotOK.
// 2. error: if the error is not nil, it will be returned as is.
// Otherwise, it will panic due to invalid error type.
func formatError(err any) error {
if err == nil {
return nil
}
switch e := err.(type) {
case bool:
if !e {
return errNotOK
}
return nil
case error:
return e
default:
panic("may: invalid err type '" + fmt.Sprintf("%v", reflect.TypeOf(err)) + "', should either be a bool or an error")
}
}
// handleError handles the error with the registered handlers.
func (h *mayHandlers) handleError(anyErr any, messageArgs ...any) {
h.mutex.Lock()
defer h.mutex.Unlock()
err := formatError(anyErr)
if err == nil {
return
}
for _, handler := range h.handlers {
if handler != nil {
handler(err, messageArgs...)
}
}
err = formatErrorWithMessageArgs(err, messageArgs...)
h.errs = multierr.Append(h.errs, err)
}