From 940bed65f685762a243fa55a2e9192f06cae3c2b Mon Sep 17 00:00:00 2001 From: Sebastian Waschik Date: Sun, 8 Nov 2020 22:59:50 +0100 Subject: [PATCH 1/2] Run gofmt Run gofmt on error.go error_1_13.go error_test.go error_backward.go and error_1_13_test.go. --- error.go | 1 - error_1_13_test.go | 4 ++-- error_backward.go | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/error.go b/error.go index 13aa35a..340cb1a 100644 --- a/error.go +++ b/error.go @@ -139,7 +139,6 @@ func WrapPrefix(e interface{}, prefix string, skip int) *Error { } - // Errorf creates a new error with the given message. You can use it // as a drop-in replacement for fmt.Errorf() to provide descriptive // errors in return values. diff --git a/error_1_13_test.go b/error_1_13_test.go index 92d3326..5aa5819 100644 --- a/error_1_13_test.go +++ b/error_1_13_test.go @@ -59,10 +59,10 @@ type errorWithCustomIs struct { } func (ewci errorWithCustomIs) Error() string { - return "["+ewci.Key+"]: " + ewci.Err.Error() + return "[" + ewci.Key + "]: " + ewci.Err.Error() } func (ewci errorWithCustomIs) Is(target error) bool { matched, ok := target.(errorWithCustomIs) return ok && matched.Key == ewci.Key -} \ No newline at end of file +} diff --git a/error_backward.go b/error_backward.go index d7e09a8..faa4389 100644 --- a/error_backward.go +++ b/error_backward.go @@ -19,4 +19,4 @@ func Is(e error, original error) bool { } return false -} \ No newline at end of file +} From 725b1a223c65953b16d6efc0d5d709f58721724c Mon Sep 17 00:00:00 2001 From: Sebastian Waschik Date: Sun, 8 Nov 2020 23:04:28 +0100 Subject: [PATCH 2/2] Implement errors.As --- error.go | 5 +++++ error_1_13.go | 5 +++++ error_backward.go | 35 +++++++++++++++++++++++++++++++++++ error_test.go | 29 +++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+) diff --git a/error.go b/error.go index 340cb1a..ccbc2e4 100644 --- a/error.go +++ b/error.go @@ -202,3 +202,8 @@ func (err *Error) TypeName() string { } return reflect.TypeOf(err.Err).String() } + +// Return the wrapped error (implements api for As function). +func (err *Error) Unwrap() error { + return err.Err +} diff --git a/error_1_13.go b/error_1_13.go index a81e24e..0af2fc8 100644 --- a/error_1_13.go +++ b/error_1_13.go @@ -6,6 +6,11 @@ import ( baseErrors "errors" ) +// find error in any wrapped error +func As(err error, target interface{}) bool { + return baseErrors.As(err, target) +} + // Is detects whether the error is equal to a given error. Errors // are considered equal by this function if they are matched by errors.Is // or if their contained errors are matched through errors.Is diff --git a/error_backward.go b/error_backward.go index faa4389..80b0695 100644 --- a/error_backward.go +++ b/error_backward.go @@ -2,6 +2,41 @@ package errors +import ( + "reflect" +) + +type unwrapper interface { + Unwrap() error +} + +// As assigns error or any wrapped error to the value target points +// to. If there is no value of the target type of target As returns +// false. +func As(err error, target interface{}) bool { + targetType := reflect.TypeOf(target) + + for { + errType := reflect.TypeOf(err) + + if errType == nil { + return false + } + + if reflect.PtrTo(errType) == targetType { + reflect.ValueOf(target).Elem().Set(reflect.ValueOf(err)) + return true + } + + wrapped, ok := err.(unwrapper) + if ok { + err = wrapped.Unwrap() + } else { + return false + } + } +} + // Is detects whether the error is equal to a given error. Errors // are considered equal by this function if they are the same object, // or if they both contain the same error inside an errors.Error. diff --git a/error_test.go b/error_test.go index d7b11f9..5f740e5 100644 --- a/error_test.go +++ b/error_test.go @@ -30,6 +30,29 @@ func BenchmarkStackFormat(b *testing.B) { } } +func TestAs(t *testing.T) { + var errStrIn errorString = "TestForFun" + + var errStrOut errorString + if As(errStrIn, &errStrOut) { + if errStrOut != "TestForFun" { + t.Errorf("direct errStr value is not returned") + } + } else { + t.Errorf("direct errStr is not returned") + } + + errStrOut = "" + err := Wrap(errStrIn, 0) + if As(err, &errStrOut) { + if errStrOut != "TestForFun" { + t.Errorf("wrapped errStr value is not returned") + } + } else { + t.Errorf("wrapped errStr is not returned") + } +} + func TestStackFormat(t *testing.T) { defer func() { @@ -316,3 +339,9 @@ func callersToFrames(callers []uintptr) []runtime.Frame { } } } + +type errorString string + +func (e errorString) Error() string { + return string(e) +}