Skip to content

Commit

Permalink
make Error compatible with functions in standard package (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
disksing authored May 13, 2021
1 parent da1aaba commit 40f9a19
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 2 deletions.
10 changes: 8 additions & 2 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ type withStack struct {

func (w *withStack) Cause() error { return w.error }

// Unwrap provides compatibility for Go 1.13 error chains.
func (w *withStack) Unwrap() error { return w.error }

func (w *withStack) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
Expand Down Expand Up @@ -260,8 +263,11 @@ type withMessage struct {
causeHasStack bool
}

func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
func (w *withMessage) Cause() error { return w.cause }
func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
func (w *withMessage) Cause() error { return w.cause }

// Unwrap provides compatibility for Go 1.13 error chains.
func (w *withMessage) Unwrap() error { return w.cause }
func (w *withMessage) HasStack() bool { return w.causeHasStack }

func (w *withMessage) Format(s fmt.State, verb rune) {
Expand Down
68 changes: 68 additions & 0 deletions errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,3 +369,71 @@ func TestWalkDeep(t *testing.T) {
t.Errorf("found not exists")
}
}

type fooError int

func (fooError) Error() string {
return "foo"
}

func TestWorkWithStdErrors(t *testing.T) {
e1 := fooError(100)
e2 := Normalize("e2", RFCCodeText("e2"))
e3 := Normalize("e3", RFCCodeText("e3"))
e21 := e2.Wrap(e1)
e31 := e3.Wrap(e1)
e32 := e3.Wrap(e2)
e321 := e3.Wrap(e21)

unwrapTbl := []struct {
x *Error // x.Unwrap() == y
y error
}{{e2, nil}, {e3, nil}, {e21, e1}, {e31, e1}, {e32, e2}, {e321, e21}}
for _, c := range unwrapTbl {
if c.x.Unwrap() != c.y {
t.Errorf("`%s`.Unwrap() != `%s`", c.x, c.y)
}
}

isTbl := []struct {
x, y error // errors.Is(x, y) == b
b bool
}{
{e1, e1, true}, {e2, e1, false}, {e3, e1, false}, {e21, e1, true}, {e321, e1, true},
{e1, e2, false}, {e2, e2, true}, {e3, e2, false}, {e21, e2, true}, {e31, e2, false}, {e321, e2, true},
{e2, e21, true}, {e21, e21, true}, {e31, e21, false}, {e321, e21, true},
{e321, e321, true}, {e3, e321, true}, {e21, e321, false},
}
for _, c := range isTbl {
if c.b && !errors.Is(c.x, c.y) {
t.Errorf("`%s` is not `%s`", c.x, c.y)
}
if !c.b && errors.Is(c.x, c.y) {
t.Errorf("`%s` is `%s`", c.x, c.y)
}
}

var e1x fooError
if ok := errors.As(e21, &e1x); !ok {
t.Error("e21 cannot convert to e1")
}
if int(e1x) != 100 {
t.Error("e1x is not 100")
}

var e2x *Error
if ok := errors.As(e21, &e2x); !ok {
t.Error("e21 cannot convert to e2")
}
if e2x.ID() != "e2" {
t.Error("err is not e2")
}

e3x := e3.Wrap(e1)
if ok := errors.As(e21, &e3x); !ok {
t.Error("e21 cannot convert to e3")
}
if e3x.ID() != "e2" {
t.Error("err is not e2")
}
}
20 changes: 20 additions & 0 deletions normalize.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,26 @@ func (e *Error) Wrap(err error) *Error {
return nil
}

// Unwrap returns cause of the error.
// It allows Error to work with errors.Is() and errors.As() from the Go
// standard package.
func (e *Error) Unwrap() error {
if e == nil {
return nil
}
return e.cause
}

// Is checks if e has the same error ID with other.
// It allows Error to work with errors.Is() from the Go standard package.
func (e *Error) Is(other error) bool {
err, ok := other.(*Error)
if !ok {
return false
}
return (e == nil && err == nil) || (e != nil && err != nil && e.ID() == err.ID())
}

func (e *Error) Cause() error {
root := Unwrap(e.cause)
if root == nil {
Expand Down

0 comments on commit 40f9a19

Please sign in to comment.