Skip to content

Commit

Permalink
Merge pull request #57 from tlm/wrapping-features
Browse files Browse the repository at this point in the history
Independent SetLocation and WithType for errors.
  • Loading branch information
tlm authored Mar 31, 2022
2 parents 6664a20 + 5ed85fd commit b38fca4
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 0 deletions.
18 changes: 18 additions & 0 deletions errortypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,24 @@ func makeWrappedConstError(err error, format string, args ...interface{}) error
return fmt.Errorf(strings.Join([]string{format, "%w"}, separator), append(args, err)...)
}

// WithType is responsible for annotating an already existing error so that it
// also satisfies that of a ConstError. The resultant error returned should
// satisfy Is(err, errType). If err is nil then a nil error will also be returned.
//
// Now with Go's Is, As and Unwrap support it no longer makes sense to Wrap()
// 2 errors as both of those errors could be chains of errors in their own right.
// WithType aims to solve some of the usefulness of Wrap with the ability to
// make a pre-existing error also satisfy a ConstError type.
func WithType(err error, errType ConstError) error {
if err == nil {
return nil
}
return &errWithType{
error: err,
errType: errType,
}
}

// Timeoutf returns an error which satisfies Is(err, Timeout) and the Locationer
// interface.
func Timeoutf(format string, args ...interface{}) error {
Expand Down
16 changes: 16 additions & 0 deletions errortypes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,19 @@ func (*errorTypeSuite) TestThatYouAlwaysGetError(c *gc.C) {
c.Assert(err.Error(), gc.Equals, "")
}
}

func (*errorTypeSuite) TestWithTypeNil(c *gc.C) {
myErr := errors.ConstError("do you feel lucky?")
c.Assert(errors.WithType(nil, myErr), gc.IsNil)
}

func (*errorTypeSuite) TestWithType(c *gc.C) {
myErr := errors.ConstError("do you feel lucky?")
myErr2 := errors.ConstError("i don't feel lucky")
err := errors.New("yes")

err = errors.WithType(err, myErr)
c.Assert(errors.Is(err, myErr), gc.Equals, true)
c.Assert(err.Error(), gc.Equals, "yes")
c.Assert(errors.Is(err, myErr2), gc.Equals, false)
}
12 changes: 12 additions & 0 deletions functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,3 +355,15 @@ func Is(err, target error) bool {
func As(err error, target interface{}) bool {
return stderrors.As(err, target)
}

// SetLocation takes a given error and records where in the stack SetLocation
// was called from and returns the wrapped error with the location information
// set. The returned error implements the Locationer interface. If err is nil
// then a nil error is returned.
func SetLocation(err error, callDepth int) error {
if err == nil {
return nil
}

return newLocationError(err, callDepth)
}
14 changes: 14 additions & 0 deletions functions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,3 +373,17 @@ func (*functionSuite) TestIs(c *gc.C) {
c.Check(val, gc.Equals, true)
}
}

func (*functionSuite) TestSetLocationWithNilError(c *gc.C) {
c.Assert(errors.SetLocation(nil, 1), gc.IsNil)
}

func (*functionSuite) TestSetLocation(c *gc.C) {
err := errors.New("test")
err = errors.SetLocation(err, 1)
stack := fmt.Sprintf("%s: test", errorLocationValue(c))
_, implements := err.(errors.Locationer)
c.Assert(implements, gc.Equals, true)

c.Check(errors.ErrorStack(err), gc.Equals, stack)
}

0 comments on commit b38fca4

Please sign in to comment.