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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 3 additions & 35 deletions functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -370,42 +370,10 @@ func As(err error, target interface{}) bool {
return stderrors.As(err, target)
}

// AsType is a convenience method for checking and getting an error from within
// a chain that is of type T. If no error is found of type T in the chain the
// zero value of T is returned with false. If an error in the chain implementes
// As(any) bool then it's As method will be called if it's type is not of type T.

// AsType finds the first error in err's chain that is assignable to type T, and
// if a match is found, returns that error value and true. Otherwise, it returns
// T's zero value and false.
//
// AsType is equivalent to errors.As, but uses a type parameter and returns
// the target, to avoid having to define a variable before the call. For
// example, callers can replace this:
//
// var pathError *fs.PathError
// if errors.As(err, &pathError) {
// fmt.Println("Failed at path:", pathError.Path)
// }
//
// With:
//
// if pathError, ok := errors.AsType[*fs.PathError](err); ok {
// fmt.Println("Failed at path:", pathError.Path)
// }
// AsType is a proxy for the AsType function in Go's standard `errors` library
// (pkg.go.dev/errors). It finds the first error in err's tree that matches T.
func AsType[T error](err error) (T, bool) {
for err != nil {
if e, is := err.(T); is {
return e, true
}
var res T
if x, ok := err.(interface{ As(any) bool }); ok && x.As(&res) {
return res, true
}
err = stderrors.Unwrap(err)
}
var zero T
return zero, false
return stderrors.AsType[T](err)
}

// SetLocation takes a given error and records where in the stack SetLocation
Expand Down
115 changes: 16 additions & 99 deletions functions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,22 @@ func (*functionSuite) TestAs(c *gc.C) {
}
}

func (*functionSuite) TestAsType(c *gc.C) {
baseError := &basicError{"I'm an error"}
testErrors := []error{
errors.Trace(baseError),
errors.Annotate(baseError, "annotation"),
errors.Wrap(baseError, errors.New("wrapper")),
errors.Mask(baseError),
Comment thread
kian99 marked this conversation as resolved.
}

for _, err := range testErrors {
bError, val := errors.AsType[*basicError](err)
c.Check(val, gc.Equals, true)
c.Check(bError.Reason, gc.Equals, "I'm an error")
}
}

func (*functionSuite) TestIs(c *gc.C) {
baseError := &basicError{"I'm an error"}
testErrors := []error{
Expand Down Expand Up @@ -403,105 +419,6 @@ func (*functionSuite) TestQuietWrappedErrorStillSatisfied(c *gc.C) {
c.Assert(errors.Is(err, simpleTestError), gc.Equals, true)
}

type ComplexErrorMessage interface {
error
ComplexMessage() string
}

type complexError struct {
Message string
}

func (c *complexError) Error() string {
return c.Message
}

func (c *complexError) ComplexMessage() string {
return c.Message
}

type complexErrorOther struct {
Message string
}

func (c *complexErrorOther) As(e any) bool {
if ce, ok := e.(**complexError); ok {
*ce = &complexError{
Message: c.Message,
}
return true
}
return false
}

func (c *complexErrorOther) Error() string {
return c.Message
}

func (c *complexErrorOther) ComplexMessage() string {
return c.Message
}

func (*functionSuite) TestHasType(c *gc.C) {
complexErr := &complexError{Message: "complex error message"}
wrapped1 := fmt.Errorf("wrapping1: %w", complexErr)
wrapped2 := fmt.Errorf("wrapping2: %w", wrapped1)

c.Assert(errors.HasType[*complexError](complexErr), gc.Equals, true)
c.Assert(errors.HasType[*complexError](wrapped1), gc.Equals, true)
c.Assert(errors.HasType[*complexError](wrapped2), gc.Equals, true)
c.Assert(errors.HasType[ComplexErrorMessage](wrapped2), gc.Equals, true)
c.Assert(errors.HasType[*complexErrorOther](wrapped2), gc.Equals, false)
c.Assert(errors.HasType[*complexErrorOther](nil), gc.Equals, false)

complexErrOther := &complexErrorOther{Message: "another complex error"}

c.Assert(errors.HasType[*complexError](complexErrOther), gc.Equals, true)

wrapped2 = fmt.Errorf("wrapping1: %w", complexErrOther)
c.Assert(errors.HasType[*complexError](wrapped2), gc.Equals, true)
}

func (*functionSuite) TestAsType(c *gc.C) {
complexErr := &complexError{Message: "complex error message"}
wrapped1 := fmt.Errorf("wrapping1: %w", complexErr)
wrapped2 := fmt.Errorf("wrapping2: %w", wrapped1)

ce, ok := errors.AsType[*complexError](complexErr)
c.Assert(ok, gc.Equals, true)
c.Assert(ce.Message, gc.Equals, complexErr.Message)

ce, ok = errors.AsType[*complexError](wrapped1)
c.Assert(ok, gc.Equals, true)
c.Assert(ce.Message, gc.Equals, complexErr.Message)

ce, ok = errors.AsType[*complexError](wrapped2)
c.Assert(ok, gc.Equals, true)
c.Assert(ce.Message, gc.Equals, complexErr.Message)

cem, ok := errors.AsType[ComplexErrorMessage](wrapped2)
c.Assert(ok, gc.Equals, true)
c.Assert(cem.ComplexMessage(), gc.Equals, complexErr.Message)

ceo, ok := errors.AsType[*complexErrorOther](wrapped2)
c.Assert(ok, gc.Equals, false)
c.Assert(ceo, gc.Equals, (*complexErrorOther)(nil))

ceo, ok = errors.AsType[*complexErrorOther](nil)
c.Assert(ok, gc.Equals, false)
c.Assert(ceo, gc.Equals, (*complexErrorOther)(nil))

complexErrOther := &complexErrorOther{Message: "another complex error"}
ce, ok = errors.AsType[*complexError](complexErrOther)
c.Assert(ok, gc.Equals, true)
c.Assert(ce.Message, gc.Equals, complexErrOther.Message)

wrapped2 = fmt.Errorf("wrapping1: %w", complexErrOther)
ce, ok = errors.AsType[*complexError](wrapped2)
c.Assert(ok, gc.Equals, true)
c.Assert(ce.Message, gc.Equals, complexErrOther.Message)
}

func ExampleHide() {
myConstError := errors.ConstError("I don't want to be fmt printed")
err := fmt.Errorf("don't show this error%w", errors.Hide(myConstError))
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/juju/errors

go 1.18
go 1.26

require gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c

Expand Down
Loading