Skip to content

Commit 67cff84

Browse files
committed
implement&test for auto-annotation for multi-handler API
1 parent 12db281 commit 67cff84

File tree

2 files changed

+86
-37
lines changed

2 files changed

+86
-37
lines changed

internal/handler/handler.go

Lines changed: 69 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ type Info struct {
5555
CallerName string
5656

5757
werr error
58+
59+
needErrorAnnotation bool
5860
}
5961

6062
const (
@@ -101,7 +103,15 @@ func (i *Info) checkErrorTracer() {
101103
func (i *Info) callErrorHandler() {
102104
i.checkErrorTracer()
103105
if i.ErrorFn != nil {
104-
*i.Err = i.ErrorFn(i.Any.(error))
106+
// we want to auto-annotate error first and exec ErrorFn then
107+
i.werr = i.workError()
108+
if i.needErrorAnnotation && i.werr != nil {
109+
i.buildFmtErr()
110+
*i.Err = i.ErrorFn(*i.Err)
111+
} else {
112+
*i.Err = i.ErrorFn(i.Any.(error))
113+
}
114+
105115
i.werr = *i.Err // remember change both our errors!
106116
} else {
107117
i.defaultErrorHandler()
@@ -269,23 +279,7 @@ func PreProcess(errPtr *error, info *Info, a []any) error {
269279
if len(a) > 0 {
270280
subProcess(info, a)
271281
} else {
272-
fnName := "Handle" // default
273-
if info.CallerName != "" {
274-
fnName = info.CallerName
275-
}
276-
funcName, _, _, ok := debug.FuncName(debug.StackInfo{
277-
PackageName: "",
278-
FuncName: fnName,
279-
Level: lvl,
280-
})
281-
if ok {
282-
setFmter := fmtstore.Formatter()
283-
if setFmter != nil {
284-
info.Format = setFmter.Format(funcName)
285-
} else {
286-
info.Format = str.Decamel(funcName)
287-
}
288-
}
282+
buildFormatStr(info, lvl)
289283
}
290284
defCatchCallMode := info.PanicFn == nil && info.CallerName == "Catch"
291285
if defCatchCallMode {
@@ -309,6 +303,32 @@ func PreProcess(errPtr *error, info *Info, a []any) error {
309303
return err
310304
}
311305

306+
func buildFormatStr(info *Info, lvl int) {
307+
if fs, ok := doBuildFormatStr(info, lvl); ok {
308+
info.Format = fs
309+
}
310+
}
311+
312+
func doBuildFormatStr(info *Info, lvl int) (fs string, ok bool) {
313+
fnName := "Handle"
314+
if info.CallerName != "" {
315+
fnName = info.CallerName
316+
}
317+
funcName, _, _, ok := debug.FuncName(debug.StackInfo{
318+
PackageName: "",
319+
FuncName: fnName,
320+
Level: lvl,
321+
})
322+
if ok {
323+
setFmter := fmtstore.Formatter()
324+
if setFmter != nil {
325+
return setFmter.Format(funcName), true
326+
}
327+
return str.Decamel(funcName), true
328+
}
329+
return
330+
}
331+
312332
func subProcess(info *Info, a []any) {
313333
// not that switch cannot be 0: see call side
314334
switch len(a) {
@@ -326,13 +346,30 @@ programming error: subProcess: case 0:
326346
} else if _, ok := a[1].(ErrorFn); ok {
327347
// check second ^ and then change the rest by combining them to
328348
// one that we set to proper places: ErrorFn and NilFn
329-
hfn := Pipeline(ToErrorFns(a))
349+
errorFns, dis := ToErrorFns(a)
350+
autoOn := !dis
351+
hfn := Pipeline(errorFns)
330352
info.ErrorFn = hfn
331353
info.NilFn = hfn
354+
355+
if fs, ok := doBuildFormatStr(info, -1); autoOn && ok {
356+
//println("fmt:", fs)
357+
info.Format = fs
358+
info.needErrorAnnotation = true
359+
}
332360
}
333361
}
334362
}
335363

364+
func isAutoAnnotationFn(errorFns []ErrorFn) bool {
365+
for _, f := range errorFns {
366+
if f == nil {
367+
return false
368+
}
369+
}
370+
return true
371+
}
372+
336373
func processArg(info *Info, i int, a []any) {
337374
switch first := a[i].(type) {
338375
case string:
@@ -404,19 +441,24 @@ func Pipeline(f []ErrorFn) ErrorFn {
404441
}
405442
}
406443

407-
func ToErrorFns(handlerFns []any) (hs []ErrorFn) {
444+
func ToErrorFns(handlerFns []any) (hs []ErrorFn, dis bool) {
408445
count := len(handlerFns)
409446
hs = make([]ErrorFn, 0, count)
410447
for _, a := range handlerFns {
411-
if fn, ok := a.(ErrorFn); ok {
412-
hs = append(hs, fn)
413-
} else {
414-
msg := `---
448+
autoAnnotationDisabling := a == nil
449+
if !autoAnnotationDisabling {
450+
if fn, ok := a.(ErrorFn); ok {
451+
hs = append(hs, fn)
452+
} else {
453+
msg := `---
415454
assertion violation: your handlers should be 'func(error) error' type
416455
---`
417-
fmt.Fprintln(os.Stderr, color.Red()+msg+color.Reset())
418-
return nil
456+
fmt.Fprintln(os.Stderr, color.Red()+msg+color.Reset())
457+
return nil, true
458+
}
459+
} else {
460+
dis = autoAnnotationDisabling
419461
}
420462
}
421-
return hs
463+
return hs, dis
422464
}

internal/handler/handlers_test.go

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,25 @@ func TestHandlers(t *testing.T) {
1717
name string
1818
args args
1919
want error
20+
dis bool
2021
}{
21-
{"one", args{f: []any{err2.Noop}}, err2.ErrNotFound},
22-
{"two", args{f: []any{err2.Noop, err2.Noop}}, err2.ErrNotFound},
23-
{"three", args{f: []any{err2.Noop, err2.Noop, err2.Noop}}, err2.ErrNotFound},
24-
{"reset", args{f: []any{err2.Noop, err2.Noop, err2.Reset}}, nil},
25-
{"reset first", args{f: []any{err2.Reset, err2.Noop, err2.Noop}}, nil},
26-
{"reset second", args{f: []any{err2.Noop, err2.Reset, err2.Noop}}, nil},
22+
{"one", args{f: []any{err2.Noop}}, err2.ErrNotFound, false},
23+
{"one disabled NOT real case", args{f: []any{nil}}, err2.ErrNotFound, true},
24+
{"two", args{f: []any{err2.Noop, err2.Noop}}, err2.ErrNotFound, false},
25+
{"three", args{f: []any{err2.Noop, err2.Noop, err2.Noop}}, err2.ErrNotFound, false},
26+
{"three last disabled", args{f: []any{err2.Noop, err2.Noop, nil}}, err2.ErrNotFound, true},
27+
{"three 2nd disabled", args{f: []any{err2.Noop, nil, err2.Noop}}, err2.ErrNotFound, true},
28+
{"three all disabled", args{f: []any{nil, nil, nil}}, err2.ErrNotFound, true},
29+
{"reset", args{f: []any{err2.Noop, err2.Noop, err2.Reset}}, nil, false},
30+
{"reset and disabled", args{f: []any{nil, err2.Noop, err2.Reset}}, nil, true},
31+
{"reset first", args{f: []any{err2.Reset, err2.Noop, err2.Noop}}, nil, false},
32+
{"reset second", args{f: []any{err2.Noop, err2.Reset, err2.Noop}}, nil, false},
2733
{"set new first", args{f: []any{
28-
func(error) error { return err2.ErrAlreadyExist }, err2.Noop}}, err2.ErrAlreadyExist},
34+
func(error) error { return err2.ErrAlreadyExist }, err2.Noop}}, err2.ErrAlreadyExist, false},
2935
{"set new second", args{f: []any{err2.Noop,
30-
func(error) error { return err2.ErrAlreadyExist }, err2.Noop}}, err2.ErrAlreadyExist},
36+
func(error) error { return err2.ErrAlreadyExist }, err2.Noop}}, err2.ErrAlreadyExist, false},
3137
{"set new first and reset", args{f: []any{
32-
func(error) error { return err2.ErrAlreadyExist }, err2.Reset}}, nil},
38+
func(error) error { return err2.ErrAlreadyExist }, err2.Reset}}, nil, false},
3339
}
3440
for _, tt := range tests {
3541
tt := tt
@@ -38,8 +44,9 @@ func TestHandlers(t *testing.T) {
3844
anys := tt.args.f
3945

4046
test.Require(t, anys != nil, "cannot be nil")
41-
fns := handler.ToErrorFns(anys)
47+
fns, dis := handler.ToErrorFns(anys)
4248
test.Require(t, fns != nil, "cannot be nil")
49+
test.Require(t, dis == tt.dis, "disabled wanted")
4350

4451
errHandler := handler.Pipeline(fns)
4552
err := errHandler(err2.ErrNotFound)

0 commit comments

Comments
 (0)