Skip to content

Commit 19776b6

Browse files
authored
Merge feature/1.13_errors_unwrap
2 parents c749e95 + 379717d commit 19776b6

File tree

5 files changed

+92
-13
lines changed

5 files changed

+92
-13
lines changed

error.go

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -106,17 +106,9 @@ func (e *Error) HasTrait(key Trait) bool {
106106
// IsOfType is a proper type check for an errorx-based errors.
107107
// It takes the transparency and error types hierarchy into account,
108108
// so that type check against any supertype of the original cause passes.
109+
// Go 1.13 and above: it also tolerates non-errorx errors in chain if those errors support errors unwrap.
109110
func (e *Error) IsOfType(t *Type) bool {
110-
cause := e
111-
for cause != nil {
112-
if !cause.transparent {
113-
return cause.errorType.IsOfType(t)
114-
}
115-
116-
cause = Cast(cause.Cause())
117-
}
118-
119-
return false
111+
return e.isOfType(t)
120112
}
121113

122114
// Type returns the exact type of this error.

error_112.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// +build !go1.13
2+
3+
package errorx
4+
5+
func isOfType(err error, t *Type) bool {
6+
e := Cast(err)
7+
return e != nil && e.IsOfType(t)
8+
}
9+
10+
func (e *Error) isOfType(t *Type) bool {
11+
cause := e
12+
for cause != nil {
13+
if !cause.transparent {
14+
return cause.errorType.IsOfType(t)
15+
}
16+
17+
cause = Cast(cause.Cause())
18+
}
19+
20+
return false
21+
}
22+
23+

error_113.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// +build go1.13
2+
3+
package errorx
4+
5+
import "errors"
6+
7+
func isOfType(err error, t *Type) bool {
8+
e := burrowForTyped(err)
9+
return e != nil && e.IsOfType(t)
10+
}
11+
12+
func (e *Error) isOfType(t *Type) bool {
13+
cause := e
14+
for cause != nil {
15+
if !cause.transparent {
16+
return cause.errorType.IsOfType(t)
17+
}
18+
19+
cause = burrowForTyped(cause.Cause())
20+
}
21+
22+
return false
23+
}
24+
25+
// burrowForTyped returns either the first *Error in unwrap chain or nil
26+
func burrowForTyped(err error) *Error {
27+
raw := err
28+
for raw != nil {
29+
typed := Cast(raw)
30+
if typed != nil {
31+
return typed
32+
}
33+
34+
raw = errors.Unwrap(raw)
35+
}
36+
37+
return nil
38+
}

error_113_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package errorx
44

55
import (
66
"errors"
7+
"fmt"
78
"io"
89
"testing"
910

@@ -103,3 +104,28 @@ func TestErrorIs(t *testing.T) {
103104
require.True(t, errors.Is(err, io.EOF))
104105
})
105106
}
107+
108+
func TestErrorsAndErrorx(t *testing.T) {
109+
t.Run("DecoratedForeign", func(t *testing.T) {
110+
err := fmt.Errorf("error test: %w", testType.NewWithNoMessage())
111+
require.True(t, errors.Is(err, testType.NewWithNoMessage()))
112+
require.True(t, IsOfType(err, testType))
113+
})
114+
115+
t.Run("LayeredDecorate", func(t *testing.T) {
116+
err := Decorate(fmt.Errorf("error test: %w", testType.NewWithNoMessage()), "test")
117+
require.True(t, errors.Is(err, testType.NewWithNoMessage()))
118+
require.True(t, IsOfType(err, testType))
119+
})
120+
121+
t.Run("LayeredDecorateAgain", func(t *testing.T) {
122+
err := fmt.Errorf("error test: %w", Decorate(io.EOF, "test"))
123+
require.True(t, errors.Is(err, io.EOF))
124+
})
125+
126+
t.Run("Wrap", func(t *testing.T) {
127+
err := fmt.Errorf("error test: %w", testType.Wrap(io.EOF, "test"))
128+
require.False(t, errors.Is(err, io.EOF))
129+
require.True(t, errors.Is(err, testType.NewWithNoMessage()))
130+
})
131+
}

type.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,10 @@ func (t *Type) HasTrait(key Trait) bool {
9797

9898
// IsOfType is a type check for errors.
9999
// Returns true either if both are of exactly the same type, or if the same is true for one of current type's ancestors.
100-
// For an error that does not have an errorx type, returns false.
100+
// Go 1.12 and below: for an error that does not have an errorx type, returns false.
101+
// Go 1.13 and above: for an error that does not have an errorx type, returns false unless it wraps another error of errorx type.
101102
func IsOfType(err error, t *Type) bool {
102-
e := Cast(err)
103-
return e != nil && e.IsOfType(t)
103+
return isOfType(err, t)
104104
}
105105

106106
// Supertype returns a parent type, if present.

0 commit comments

Comments
 (0)