Skip to content

Commit

Permalink
improved message outputting of a nested error
Browse files Browse the repository at this point in the history
Signed-off-by: Hedzr Yeh <hedzrz@gmail.com>
  • Loading branch information
hedzr committed Feb 26, 2023
1 parent 9598535 commit 88a4b74
Show file tree
Hide file tree
Showing 6 changed files with 270 additions and 83 deletions.
12 changes: 6 additions & 6 deletions asisunwrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ func As(err error, target interface{}) bool {
if typ.Kind() != reflect.Ptr || val.IsNil() {
panic("errors: target must be a non-nil pointer")
}
e := typ.Elem()
k := e.Kind()
if k != reflect.Interface && k != reflect.Slice && !e.Implements(errorType) {
// panic("errors: *target must be interface or implement error")
return false
}
// e := typ.Elem()
// k := e.Kind()
// if k != reflect.Interface && k != reflect.Slice && !e.Implements(errorType) {
// // panic("errors: *target must be interface or implement error")
// return false
// }
targetType := typ.Elem()
for err != nil {
if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) {
Expand Down
137 changes: 113 additions & 24 deletions causes.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package errors

import (
"bufio"
"bytes"
"fmt"
"io"
"strings"
)

type causes2 struct {
Expand Down Expand Up @@ -38,23 +40,22 @@ func (w *causes2) End() {}
//
// The codes:
//
// func some(){
// // as a inner errors container
// child := func() (err error) {
// errContainer := errors.New("")
// defer errContainer.Defer(&err)
// func some(){
// // as a inner errors container
// child := func() (err error) {
// errContainer := errors.New("")
// defer errContainer.Defer(&err)
//
// for _, r := range []error{io.EOF, io.ErrClosedPipe, errors.Internal} {
// errContainer.Attach(r)
// }
// for _, r := range []error{io.EOF, io.ErrClosedPipe, errors.Internal} {
// errContainer.Attach(r)
// }
//
// return
// }
//
// err := child()
// t.Logf("failed: %+v", err)
// }
// return
// }
//
// err := child()
// t.Logf("failed: %+v", err)
// }
func (w *causes2) Defer(err *error) {
*err = w
}
Expand Down Expand Up @@ -110,6 +111,49 @@ func (w *causes2) Clone() *causes2 {
}

func (w *causes2) Error() string {
return w.makeErrorString(false)
// var buf bytes.Buffer
// if w.msg != "" {
// if len(w.liveArgs) > 0 {
// msg := fmt.Sprintf(w.msg, w.liveArgs...)
// buf.WriteString(msg)
// } else {
// buf.WriteString(w.msg)
// }
// }
//
// var needclose, needsep bool
// if w.Code != OK {
// if buf.Len() > 0 {
// buf.WriteRune(' ')
// }
// buf.WriteString("[")
// buf.WriteString(w.Code.String())
// needclose = true
// needsep = true
// }
// if len(w.Causers) > 0 {
// if buf.Len() > 0 {
// buf.WriteRune(' ')
// }
// buf.WriteString("[")
// needclose = true
// }
//
// for i, c := range w.Causers {
// if i > 0 || needsep {
// buf.WriteString(" | ")
// }
// buf.WriteString(c.Error())
// }
// if needclose {
// buf.WriteString("]")
// }
// // buf.WriteString(w.Stack)
// return buf.String()
}

func (w *causes2) makeErrorString(line bool) string {
var buf bytes.Buffer
if w.msg != "" {
if len(w.liveArgs) > 0 {
Expand All @@ -120,6 +164,26 @@ func (w *causes2) Error() string {
}
}

if line {
buf.WriteRune('\n')
if w.Code != OK {
buf.WriteString(w.Code.String())
}

for _, c := range w.Causers {
buf.WriteString(" - ")
var xc *causes2
if As(c, &xc) {
buf.WriteString(leftPad(xc.makeErrorString(line), " ", false))
} else {
buf.WriteString(leftPad(c.Error(), " ", false))
}
}

// buf.WriteRune('\n')
return buf.String()
}

var needclose, needsep bool
if w.Code != OK {
if buf.Len() > 0 {
Expand Down Expand Up @@ -151,19 +215,38 @@ func (w *causes2) Error() string {
return buf.String()
}

func leftPad(s, padStr string, firstLine bool) string {
if padStr == "" {
return s
}

var ln int
var sb strings.Builder
scanner := bufio.NewScanner(bufio.NewReader(strings.NewReader(s)))
for scanner.Scan() {
if ln != 0 || firstLine {
sb.WriteString(padStr)
}
sb.WriteString(scanner.Text())
sb.WriteRune('\n')
ln++
}
return sb.String()
}

// Format formats the stack of Frames according to the fmt.Formatter interface.
//
// %s lists source files for each Frame in the stack
// %v lists the source file and line number for each Frame in the stack
// %s lists source files for each Frame in the stack
// %v lists the source file and line number for each Frame in the stack
//
// Format accepts flags that alter the printing of some verbs, as follows:
//
// %+v Prints filename, function, and line number for each Frame in the stack.
// %+v Prints filename, function, and line number for each Frame in the stack.
func (w *causes2) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('+') {
_, _ = fmt.Fprintf(s, "%+v", w.Error())
_, _ = fmt.Fprintf(s, "%+v", w.makeErrorString(true))
return
}
fallthrough
Expand All @@ -174,6 +257,9 @@ func (w *causes2) Format(s fmt.State, verb rune) {
}
}

// String for stringer interface
func (w *causes2) String() string { return w.Error() }

func (w *causes2) Cause() error {
if len(w.Causers) == 0 {
return nil
Expand All @@ -187,12 +273,11 @@ func (w *causes2) Cause() error {
// errors.Unwrap for instead. The errors.Unwrap could extract all
// of them one by one:
//
// var err = errors.New("hello").WithErrors(io.EOF, io.ShortBuffers)
// var e error = err
// for e != nil {
// e = errors.Unwrap(err)
// }
//
// var err = errors.New("hello").WithErrors(io.EOF, io.ShortBuffers)
// var e error = err
// for e != nil {
// e = errors.Unwrap(err)
// }
func (w *causes2) Causes() []error {
if len(w.Causers) == 0 {
return nil
Expand Down Expand Up @@ -261,6 +346,10 @@ func (w *causes2) As(target interface{}) bool {
*c = w.Causers
return true
}
if c, ok := target.(**causes2); ok {
*c = w
return true
}
return AsSlice(w.Causers, target)
}

Expand Down
96 changes: 96 additions & 0 deletions causes_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,107 @@
package errors

import (
"errors"
"fmt"
"io"
"testing"
)

type DivisionError struct {
IntA int
IntB int
Msg string
}

func (e *DivisionError) Error() string {
return e.Msg
}

func Divide(a, b int) (int, error) {
if b == 0 {
return 0, &DivisionError{
Msg: fmt.Sprintf("cannot divide '%d' by zero", a),
IntA: a, IntB: b,
}
}
return a / b, nil
}

func dummy(t *testing.T) error {
a, b := 10, 0
result, err := Divide(a, b)
if err != nil {
var divErr *DivisionError
switch {
case errors.As(err, &divErr):
fmt.Printf("%d / %d is not mathematically valid: %s\n",
divErr.IntA, divErr.IntB, divErr.Error())
default:
fmt.Printf("unexpected division error: %s\n", err)
t.Fail()
}
return err
}

fmt.Printf("%d / %d = %d\n", a, b, result)
return err
}

func dummyV3(t *testing.T) error {
a, b := 10, 0
result, err := Divide(a, b)
if err != nil {
var divErr *DivisionError
switch {
case As(err, &divErr):
fmt.Printf("%d / %d is not mathematically valid: %s\n",
divErr.IntA, divErr.IntB, divErr.Error())
default:
fmt.Printf("unexpected division error: %s\n", err)
t.Fail()
}
return err
}

fmt.Printf("%d / %d = %d\n", a, b, result)
return err
}

func TestCauses2_errors(t *testing.T) {
err := io.EOF

if !errors.Is(err, io.EOF) {
t.Fail()
}

err = dummy(t)
err = fmt.Errorf("wrapped: %w", err)
t.Logf("divide: %v", err)
t.Logf("Unwrap: %v", errors.Unwrap(err))
}

func TestCauses2_errorsV3(t *testing.T) {
err := io.EOF

if !Is(err, io.EOF) {
t.Fail()
}

err = dummyV3(t)
err = fmt.Errorf("wrapped: %w", err)
t.Logf("divide: %v", err)
t.Logf("Unwrap: %v", Unwrap(err))

// As() on our error wrappers
err = New("xx")
var e1 *causes2
if As(err, &e1) {
t.Logf("e1: %v", e1)
} else {
t.Fail()
}
}

func TestCauses2_WithCode(t *testing.T) {
err := &causes2{
Code: Internal,
Expand Down
5 changes: 2 additions & 3 deletions coded.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,8 @@ func (c Code) Register(codeName string) (errno Code) {
//
// The best way is:
//
// var ErrAck = errors.RegisterCode(3, "cannot ack") // ErrAck will be -1003
// var ErrAck = errors.RegisterCode(-1003, "cannot ack) // equivelant with last line
//
// var ErrAck = errors.RegisterCode(3, "cannot ack") // ErrAck will be -1003
// var ErrAck = errors.RegisterCode(-1003, "cannot ack) // equivelant with last line
func RegisterCode(codePositive int, codeName string) (errno Code) {
errno = AlreadyExists
applier := func(c Code) {
Expand Down
6 changes: 3 additions & 3 deletions stack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ func TestWithStackInfo(t *testing.T) {
WithTaggedData(TaggedData{"1": 1}).
End()
err.WithCause(io.ErrNoProgress).End()
t.Logf("failed: %+v", err)
t.Logf("failed: %v", err.Cause())
t.Logf("failed: %v", err.Causes())
t.Logf(" err is: %+v", err)
t.Logf(" err.Cause() is: %v", err.Cause())
t.Logf(" err.Causes() are: %v", err.Causes())

fmt.Printf("%+v", err)
fmt.Printf("%#v", err)
Expand Down
Loading

0 comments on commit 88a4b74

Please sign in to comment.