From 648bc1bab4c6478ab50decaf48b52ec2f7d3b295 Mon Sep 17 00:00:00 2001 From: disksing Date: Thu, 29 Apr 2021 06:51:28 +0800 Subject: [PATCH 1/3] make Error compatible with functions in standard package Signed-off-by: disksing --- errors_test.go | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++ normalize.go | 20 +++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/errors_test.go b/errors_test.go index 7f5e225f..3d69324c 100644 --- a/errors_test.go +++ b/errors_test.go @@ -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") + } +} diff --git a/normalize.go b/normalize.go index 40a6ec12..5778f6c1 100644 --- a/normalize.go +++ b/normalize.go @@ -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.ID() == err.ID() +} + func (e *Error) Cause() error { root := Unwrap(e.cause) if root == nil { From f194df254a9d9003a9f4c2767e868f6df367f53f Mon Sep 17 00:00:00 2001 From: disksing Date: Thu, 29 Apr 2021 06:51:28 +0800 Subject: [PATCH 2/3] add nil check Signed-off-by: disksing --- normalize.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/normalize.go b/normalize.go index 5778f6c1..e6f26440 100644 --- a/normalize.go +++ b/normalize.go @@ -272,7 +272,7 @@ func (e *Error) Is(other error) bool { if !ok { return false } - return (e == nil && err == nil) || e.ID() == err.ID() + return (e == nil && err == nil) || (e != nil && err != nil && e.ID() == err.ID()) } func (e *Error) Cause() error { From 1c15609772cde5d15b55417e397e7358533d853c Mon Sep 17 00:00:00 2001 From: disksing Date: Thu, 29 Apr 2021 06:56:36 +0800 Subject: [PATCH 3/3] make pkg/errors types compatible with error chain Signed-off-by: disksing --- errors.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/errors.go b/errors.go index 2e1d3f62..612f77d2 100644 --- a/errors.go +++ b/errors.go @@ -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': @@ -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) {