Skip to content

Commit

Permalink
implement&test for auto-annotation for multi-handler API
Browse files Browse the repository at this point in the history
  • Loading branch information
lainio committed Feb 20, 2024
1 parent 12db281 commit 67cff84
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 37 deletions.
96 changes: 69 additions & 27 deletions internal/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ type Info struct {
CallerName string

werr error

needErrorAnnotation bool
}

const (
Expand Down Expand Up @@ -101,7 +103,15 @@ func (i *Info) checkErrorTracer() {
func (i *Info) callErrorHandler() {
i.checkErrorTracer()
if i.ErrorFn != nil {
*i.Err = i.ErrorFn(i.Any.(error))
// we want to auto-annotate error first and exec ErrorFn then
i.werr = i.workError()
if i.needErrorAnnotation && i.werr != nil {
i.buildFmtErr()
*i.Err = i.ErrorFn(*i.Err)
} else {
*i.Err = i.ErrorFn(i.Any.(error))
}

i.werr = *i.Err // remember change both our errors!
} else {
i.defaultErrorHandler()
Expand Down Expand Up @@ -269,23 +279,7 @@ func PreProcess(errPtr *error, info *Info, a []any) error {
if len(a) > 0 {
subProcess(info, a)
} else {
fnName := "Handle" // default
if info.CallerName != "" {
fnName = info.CallerName
}
funcName, _, _, ok := debug.FuncName(debug.StackInfo{
PackageName: "",
FuncName: fnName,
Level: lvl,
})
if ok {
setFmter := fmtstore.Formatter()
if setFmter != nil {
info.Format = setFmter.Format(funcName)
} else {
info.Format = str.Decamel(funcName)
}
}
buildFormatStr(info, lvl)
}
defCatchCallMode := info.PanicFn == nil && info.CallerName == "Catch"
if defCatchCallMode {
Expand All @@ -309,6 +303,32 @@ func PreProcess(errPtr *error, info *Info, a []any) error {
return err
}

func buildFormatStr(info *Info, lvl int) {
if fs, ok := doBuildFormatStr(info, lvl); ok {
info.Format = fs
}
}

func doBuildFormatStr(info *Info, lvl int) (fs string, ok bool) {
fnName := "Handle"
if info.CallerName != "" {
fnName = info.CallerName
}
funcName, _, _, ok := debug.FuncName(debug.StackInfo{
PackageName: "",
FuncName: fnName,
Level: lvl,
})
if ok {
setFmter := fmtstore.Formatter()
if setFmter != nil {
return setFmter.Format(funcName), true
}
return str.Decamel(funcName), true
}
return
}

func subProcess(info *Info, a []any) {
// not that switch cannot be 0: see call side
switch len(a) {
Expand All @@ -326,13 +346,30 @@ programming error: subProcess: case 0:
} else if _, ok := a[1].(ErrorFn); ok {
// check second ^ and then change the rest by combining them to
// one that we set to proper places: ErrorFn and NilFn
hfn := Pipeline(ToErrorFns(a))
errorFns, dis := ToErrorFns(a)
autoOn := !dis
hfn := Pipeline(errorFns)
info.ErrorFn = hfn
info.NilFn = hfn

if fs, ok := doBuildFormatStr(info, -1); autoOn && ok {
//println("fmt:", fs)
info.Format = fs
info.needErrorAnnotation = true
}
}
}
}

func isAutoAnnotationFn(errorFns []ErrorFn) bool {
for _, f := range errorFns {
if f == nil {
return false
}
}
return true
}

func processArg(info *Info, i int, a []any) {
switch first := a[i].(type) {
case string:
Expand Down Expand Up @@ -404,19 +441,24 @@ func Pipeline(f []ErrorFn) ErrorFn {
}
}

func ToErrorFns(handlerFns []any) (hs []ErrorFn) {
func ToErrorFns(handlerFns []any) (hs []ErrorFn, dis bool) {
count := len(handlerFns)
hs = make([]ErrorFn, 0, count)
for _, a := range handlerFns {
if fn, ok := a.(ErrorFn); ok {
hs = append(hs, fn)
} else {
msg := `---
autoAnnotationDisabling := a == nil
if !autoAnnotationDisabling {
if fn, ok := a.(ErrorFn); ok {
hs = append(hs, fn)
} else {
msg := `---
assertion violation: your handlers should be 'func(error) error' type
---`
fmt.Fprintln(os.Stderr, color.Red()+msg+color.Reset())
return nil
fmt.Fprintln(os.Stderr, color.Red()+msg+color.Reset())
return nil, true
}
} else {
dis = autoAnnotationDisabling
}
}
return hs
return hs, dis
}
27 changes: 17 additions & 10 deletions internal/handler/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,25 @@ func TestHandlers(t *testing.T) {
name string
args args
want error
dis bool
}{
{"one", args{f: []any{err2.Noop}}, err2.ErrNotFound},
{"two", args{f: []any{err2.Noop, err2.Noop}}, err2.ErrNotFound},
{"three", args{f: []any{err2.Noop, err2.Noop, err2.Noop}}, err2.ErrNotFound},
{"reset", args{f: []any{err2.Noop, err2.Noop, err2.Reset}}, nil},
{"reset first", args{f: []any{err2.Reset, err2.Noop, err2.Noop}}, nil},
{"reset second", args{f: []any{err2.Noop, err2.Reset, err2.Noop}}, nil},
{"one", args{f: []any{err2.Noop}}, err2.ErrNotFound, false},
{"one disabled NOT real case", args{f: []any{nil}}, err2.ErrNotFound, true},
{"two", args{f: []any{err2.Noop, err2.Noop}}, err2.ErrNotFound, false},
{"three", args{f: []any{err2.Noop, err2.Noop, err2.Noop}}, err2.ErrNotFound, false},
{"three last disabled", args{f: []any{err2.Noop, err2.Noop, nil}}, err2.ErrNotFound, true},
{"three 2nd disabled", args{f: []any{err2.Noop, nil, err2.Noop}}, err2.ErrNotFound, true},
{"three all disabled", args{f: []any{nil, nil, nil}}, err2.ErrNotFound, true},
{"reset", args{f: []any{err2.Noop, err2.Noop, err2.Reset}}, nil, false},
{"reset and disabled", args{f: []any{nil, err2.Noop, err2.Reset}}, nil, true},
{"reset first", args{f: []any{err2.Reset, err2.Noop, err2.Noop}}, nil, false},
{"reset second", args{f: []any{err2.Noop, err2.Reset, err2.Noop}}, nil, false},
{"set new first", args{f: []any{
func(error) error { return err2.ErrAlreadyExist }, err2.Noop}}, err2.ErrAlreadyExist},
func(error) error { return err2.ErrAlreadyExist }, err2.Noop}}, err2.ErrAlreadyExist, false},
{"set new second", args{f: []any{err2.Noop,
func(error) error { return err2.ErrAlreadyExist }, err2.Noop}}, err2.ErrAlreadyExist},
func(error) error { return err2.ErrAlreadyExist }, err2.Noop}}, err2.ErrAlreadyExist, false},
{"set new first and reset", args{f: []any{
func(error) error { return err2.ErrAlreadyExist }, err2.Reset}}, nil},
func(error) error { return err2.ErrAlreadyExist }, err2.Reset}}, nil, false},
}
for _, tt := range tests {
tt := tt
Expand All @@ -38,8 +44,9 @@ func TestHandlers(t *testing.T) {
anys := tt.args.f

test.Require(t, anys != nil, "cannot be nil")
fns := handler.ToErrorFns(anys)
fns, dis := handler.ToErrorFns(anys)
test.Require(t, fns != nil, "cannot be nil")
test.Require(t, dis == tt.dis, "disabled wanted")

errHandler := handler.Pipeline(fns)
err := errHandler(err2.ErrNotFound)
Expand Down

0 comments on commit 67cff84

Please sign in to comment.