Skip to content

Commit

Permalink
add UserWrap(f,s) functions
Browse files Browse the repository at this point in the history
These maintain the type UserCode
  • Loading branch information
Greg Weber authored and gregwebs committed Sep 30, 2024
1 parent 3e4de2d commit 20de2fc
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 0 deletions.
57 changes: 57 additions & 0 deletions error_code_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,15 @@ func TestErrorWrapperNil(t *testing.T) {
if errcode.Wraps[errcode.ErrorCode](nil, "wrapped") != nil {
t.Errorf("not nil")
}
if errcode.UserWrap[errcode.UserCode](nil, "wrapped") != nil {
t.Errorf("not nil")
}
if errcode.UserWrapf[errcode.UserCode](nil, "wrapped") != nil {
t.Errorf("not nil")
}
if errcode.UserWraps[errcode.UserCode](nil, "wrapped") != nil {
t.Errorf("not nil")
}
}

func TestErrorWrapperFunctions(t *testing.T) {
Expand Down Expand Up @@ -240,6 +249,54 @@ func TestErrorWrapperFunctions(t *testing.T) {
}
}

func TestUserWrapperFunctions(t *testing.T) {
underlying := errors.New("underlying")
ec := errcode.NewBadRequestErr(underlying)
coded := errcode.WithUserMsg("user", ec)
AssertCode(t, coded, errcode.InvalidInputCode.CodeStr())
{
wrap := errcode.UserWrap(coded, "wrapped")
AssertCode(t, wrap, errcode.InvalidInputCode.CodeStr())
if errMsg := wrap.Error(); errMsg != "wrapped: user: underlying" {
t.Errorf("Wrap unexpected: %s", errMsg)
}
if errors.Unwrap(wrap).Error() != coded.Error() {
t.Errorf("bad unwrap %+v", errors.Unwrap(wrap))
}
if wrap.Unwrapped() != coded {
t.Errorf("bad unwrapped")
}
}

{
wrapf := errcode.UserWrapf(coded, "wrapped %s", "arg")
AssertCode(t, wrapf, errcode.InvalidInputCode.CodeStr())
if errMsg := wrapf.Error(); errMsg != "wrapped arg: user: underlying" {
t.Errorf("Wrap unexpected: %s", errMsg)
}
if errors.Unwrap(wrapf).Error() != coded.Error() {
t.Errorf("bad unwrap %+v", errors.Unwrap(wrapf))
}
if wrapf.Unwrapped() != coded {
t.Errorf("bad unwrapped")
}
}

{
wraps := errcode.UserWraps(coded, "wrapped", "arg", 1)
AssertCode(t, wraps, errcode.InvalidInputCode.CodeStr())
if errMsg := wraps.Error(); errMsg != "wrapped arg=1: user: underlying" {
t.Errorf("Wrap unexpected: %s", errMsg)
}
if errors.Unwrap(wraps).Error() != coded.Error() {
t.Errorf("bad unwrap %+v", errors.Unwrap(wraps))
}
if wraps.Unwrapped() != coded {
t.Errorf("bad unwrapped")
}
}
}

var internalChildCodeStr errcode.CodeStr = "internal.child.granchild"
var internalChild = errcode.InternalCode.Child("internal.child").SetHTTP(503).Child(internalChildCodeStr)

Expand Down
76 changes: 76 additions & 0 deletions user.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

package errcode

import "github.com/gregwebs/errors"

// HasUserMsg retrieves a user message.
// The goal is to be able to show an error message that is tailored for end users and to hide extended error messages from the user.
//
Expand Down Expand Up @@ -116,3 +118,77 @@ func WithUserMsg(msg string, err ErrorCode) UserCode {
}
return UserMsgErrCode{Msg: msg, Err: err}
}

type UserCodeWrap[Wrap UserCode] interface {
UserCode
Unwrapper[Wrap]
}

// wrappedUserCode is a convenience to maintain the UserCode type when wrapping errors
type wrappedUserCode[Wrapped UserCode] struct {
Err error
UserCode Wrapped
}

// Code fulfills the ErrorCode interface
func (wrapped wrappedUserCode[Wrapped]) Code() Code {
return wrapped.UserCode.Code()
}

// GetUserMsg fulfills the UserCode interface
func (wrapped wrappedUserCode[Wrapped]) GetUserMsg() string {
return wrapped.UserCode.GetUserMsg()
}

// Error fulfills the Error interface
func (wrapped wrappedUserCode[Wrapped]) Error() string {
return wrapped.Err.Error()
}

// Allow unwrapping
func (wrapped wrappedUserCode[Wrapped]) Unwrap() error {
return wrapped.UserCode
}

func (wrapped wrappedUserCode[Wrapped]) Unwrapped() Wrapped {
return wrapped.UserCode
}

// UserWrap is a convenience that calls errors.Wrap but still returns the UserCode interface
// If a nil UserCode is given it will be returned as nil
func UserWrap[EC UserCode](errCode EC, msg string) UserCodeWrap[EC] {
err := errors.Wrap(errCode, msg)
if err == nil {
return nil
}
return wrappedUserCode[EC]{
Err: err,
UserCode: errCode,
}
}

// UserWrapf is a convenience that calls errors.Wrapf but still returns the UserCode interface
// If a nil UserCode is given it will be returned as nil
func UserWrapf[EC UserCode](errCode EC, msg string, args ...interface{}) UserCodeWrap[EC] {
err := errors.Wrapf(errCode, msg, args...)
if err == nil {
return nil
}
return wrappedUserCode[EC]{
Err: err,
UserCode: errCode,
}
}

// UserWraps is a convenience that calls errors.Wraps but still returns the UserCode interface
// If a nil UserCode is given it will be returned as nil
func UserWraps[EC UserCode](errCode EC, msg string, args ...interface{}) UserCodeWrap[EC] {
err := errors.Wraps(errCode, msg, args...)
if err == nil {
return nil
}
return wrappedUserCode[EC]{
Err: err,
UserCode: errCode,
}
}

0 comments on commit 20de2fc

Please sign in to comment.