Skip to content
This repository was archived by the owner on Dec 1, 2021. It is now read-only.

Commit 49f8f61

Browse files
jayschwaaperezg
authored andcommitted
Support Go 1.13 error chains in Cause (#215)
1 parent 004deef commit 49f8f61

File tree

4 files changed

+85
-27
lines changed

4 files changed

+85
-27
lines changed

cause.go

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// +build !go1.13
2+
3+
package errors
4+
5+
// Cause recursively unwraps an error chain and returns the underlying cause of
6+
// the error, if possible. An error value has a cause if it implements the
7+
// following interface:
8+
//
9+
// type causer interface {
10+
// Cause() error
11+
// }
12+
//
13+
// If the error does not implement Cause, the original error will
14+
// be returned. If the error is nil, nil will be returned without further
15+
// investigation.
16+
func Cause(err error) error {
17+
type causer interface {
18+
Cause() error
19+
}
20+
21+
for err != nil {
22+
cause, ok := err.(causer)
23+
if !ok {
24+
break
25+
}
26+
err = cause.Cause()
27+
}
28+
return err
29+
}

errors.go

-26
Original file line numberDiff line numberDiff line change
@@ -260,29 +260,3 @@ func (w *withMessage) Format(s fmt.State, verb rune) {
260260
io.WriteString(s, w.Error())
261261
}
262262
}
263-
264-
// Cause returns the underlying cause of the error, if possible.
265-
// An error value has a cause if it implements the following
266-
// interface:
267-
//
268-
// type causer interface {
269-
// Cause() error
270-
// }
271-
//
272-
// If the error does not implement Cause, the original error will
273-
// be returned. If the error is nil, nil will be returned without further
274-
// investigation.
275-
func Cause(err error) error {
276-
type causer interface {
277-
Cause() error
278-
}
279-
280-
for err != nil {
281-
cause, ok := err.(causer)
282-
if !ok {
283-
break
284-
}
285-
err = cause.Cause()
286-
}
287-
return err
288-
}

go113.go

+33
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,36 @@ func As(err error, target interface{}) bool { return stderrors.As(err, target) }
3636
func Unwrap(err error) error {
3737
return stderrors.Unwrap(err)
3838
}
39+
40+
// Cause recursively unwraps an error chain and returns the underlying cause of
41+
// the error, if possible. There are two ways that an error value may provide a
42+
// cause. First, the error may implement the following interface:
43+
//
44+
// type causer interface {
45+
// Cause() error
46+
// }
47+
//
48+
// Second, the error may return a non-nil value when passed as an argument to
49+
// the Unwrap function. This makes Cause forwards-compatible with Go 1.13 error
50+
// chains.
51+
//
52+
// If an error value satisfies both methods of unwrapping, Cause will use the
53+
// causer interface.
54+
//
55+
// If the error is nil, nil will be returned without further investigation.
56+
func Cause(err error) error {
57+
type causer interface {
58+
Cause() error
59+
}
60+
61+
for err != nil {
62+
if cause, ok := err.(causer); ok {
63+
err = cause.Cause()
64+
} else if unwrapped := Unwrap(err); unwrapped != nil {
65+
err = unwrapped
66+
} else {
67+
break
68+
}
69+
}
70+
return err
71+
}

go113_test.go

+23-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,29 @@ import (
99
"testing"
1010
)
1111

12-
func TestErrorChainCompat(t *testing.T) {
12+
func TestCauseErrorChainCompat(t *testing.T) {
13+
err := stderrors.New("the cause!")
14+
15+
// Wrap error using the standard library
16+
wrapped := fmt.Errorf("wrapped with stdlib: %w", err)
17+
if Cause(wrapped) != err {
18+
t.Errorf("Cause does not support Go 1.13 error chains")
19+
}
20+
21+
// Wrap in another layer using pkg/errors
22+
wrapped = WithMessage(wrapped, "wrapped with pkg/errors")
23+
if Cause(wrapped) != err {
24+
t.Errorf("Cause does not support Go 1.13 error chains")
25+
}
26+
27+
// Wrap in another layer using the standard library
28+
wrapped = fmt.Errorf("wrapped with stdlib: %w", wrapped)
29+
if Cause(wrapped) != err {
30+
t.Errorf("Cause does not support Go 1.13 error chains")
31+
}
32+
}
33+
34+
func TestWrapErrorChainCompat(t *testing.T) {
1335
err := stderrors.New("error that gets wrapped")
1436
wrapped := Wrap(err, "wrapped up")
1537
if !stderrors.Is(wrapped, err) {

0 commit comments

Comments
 (0)