Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 2 additions & 10 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,17 +106,9 @@ func (e *Error) HasTrait(key Trait) bool {
// IsOfType is a proper type check for an errorx-based errors.
// It takes the transparency and error types hierarchy into account,
// so that type check against any supertype of the original cause passes.
// Go 1.13 and above: it also tolerates non-errorx errors in chain if those errors support errors unwrap.
func (e *Error) IsOfType(t *Type) bool {
cause := e
for cause != nil {
if !cause.transparent {
return cause.errorType.IsOfType(t)
}

cause = Cast(cause.Cause())
}

return false
return e.isOfType(t)
}

// Type returns the exact type of this error.
Expand Down
23 changes: 23 additions & 0 deletions error_112.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// +build !go1.13

package errorx

func isOfType(err error, t *Type) bool {
e := Cast(err)
return e != nil && e.IsOfType(t)
}

func (e *Error) isOfType(t *Type) bool {
cause := e
for cause != nil {
if !cause.transparent {
return cause.errorType.IsOfType(t)
}

cause = Cast(cause.Cause())
}

return false
}


38 changes: 38 additions & 0 deletions error_113.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// +build go1.13

package errorx

import "errors"

func isOfType(err error, t *Type) bool {
e := burrowForTyped(err)
return e != nil && e.IsOfType(t)
}

func (e *Error) isOfType(t *Type) bool {
cause := e
for cause != nil {
if !cause.transparent {
return cause.errorType.IsOfType(t)
}

cause = burrowForTyped(cause.Cause())
}

return false
}

// burrowForTyped returns either the first *Error in unwrap chain or nil
func burrowForTyped(err error) *Error {
raw := err
for raw != nil {
typed := Cast(raw)
if typed != nil {
return typed
}

raw = errors.Unwrap(raw)
}

return nil
}
26 changes: 26 additions & 0 deletions error_113_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package errorx

import (
"errors"
"fmt"
"io"
"testing"

Expand Down Expand Up @@ -103,3 +104,28 @@ func TestErrorIs(t *testing.T) {
require.True(t, errors.Is(err, io.EOF))
})
}

func TestErrorsAndErrorx(t *testing.T) {
t.Run("DecoratedForeign", func(t *testing.T) {
err := fmt.Errorf("error test: %w", testType.NewWithNoMessage())
require.True(t, errors.Is(err, testType.NewWithNoMessage()))
require.True(t, IsOfType(err, testType))
})

t.Run("LayeredDecorate", func(t *testing.T) {
err := Decorate(fmt.Errorf("error test: %w", testType.NewWithNoMessage()), "test")
require.True(t, errors.Is(err, testType.NewWithNoMessage()))
require.True(t, IsOfType(err, testType))
})

t.Run("LayeredDecorateAgain", func(t *testing.T) {
err := fmt.Errorf("error test: %w", Decorate(io.EOF, "test"))
require.True(t, errors.Is(err, io.EOF))
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should cover opaque wrap here?


t.Run("Wrap", func(t *testing.T) {
err := fmt.Errorf("error test: %w", testType.Wrap(io.EOF, "test"))
require.False(t, errors.Is(err, io.EOF))
require.True(t, errors.Is(err, testType.NewWithNoMessage()))
})
}
6 changes: 3 additions & 3 deletions type.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@ func (t *Type) HasTrait(key Trait) bool {

// IsOfType is a type check for errors.
// Returns true either if both are of exactly the same type, or if the same is true for one of current type's ancestors.
// For an error that does not have an errorx type, returns false.
// Go 1.12 and below: for an error that does not have an errorx type, returns false.
// 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.
func IsOfType(err error, t *Type) bool {
e := Cast(err)
return e != nil && e.IsOfType(t)
return isOfType(err, t)
}

// Supertype returns a parent type, if present.
Expand Down