Skip to content

Commit

Permalink
Rename Append to AppendInto + add new Append function
Browse files Browse the repository at this point in the history
  • Loading branch information
roeldev committed Feb 1, 2024
1 parent ef6e961 commit b27cec8
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 33 deletions.
5 changes: 2 additions & 3 deletions examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,8 @@ func ExampleAppend() {
doSomething := func() (err error) {
defer AppendFunc(&err, closeSomething)

_, unmarshalErr := unmarshal()
if unmarshalErr != nil {
Append(&err, unmarshalErr)
_, err = unmarshal()
if err != nil {
return err
}
return nil
Expand Down
67 changes: 49 additions & 18 deletions multi.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,54 +55,78 @@ func Join(errs ...error) error {
return newMultiErr(errs, 2)
}

// Append creates a multi error from two non-nil errors. If left is already a
// multi error, the other error is appended to it. If either of the errors is
// nil, the other error is returned.
func Append(left, right error) error {
if left == nil {
return right
}
if right == nil {
return left
}

//goland:noinspection GoTypeAssertionOnErrors
if m, ok := left.(*multiErr); ok {
m.append(right)
return m
}
return newMultiErr([]error{left, right}, 1)
}

const (
panicAppendNilPtr = "errors.Append: dest must not be a nil pointer"
panicAppendIntoNilPtr = "errors.AppendInto: dest must not be a nil pointer"
panicAppendFuncNilPtr = "errors.AppendFunc: dest must not be a nil pointer"
panicAppendFuncNilFn = "errors.AppendFunc: fn must not be nil"
)

// Append appends multiple non-nil errors to a single multi error dest.
// AppendInto appends multiple non-nil errors to a single multi error dest.
// When the value of dest is nil and errs only contains a single error, its
// value is set to the value of dest.
//
// Important: when using Append with defer, the pointer to the dest error
// Important: when using AppendInto with defer, the pointer to the dest error
// must be a named return variable. For additional details see
// https://golang.org/ref/spec#Defer_statements.
func Append(dest *error, errs ...error) {
func AppendInto(dest *error, errs ...error) (errored bool) {
if dest == nil {
panic(panicAppendNilPtr)
panic(panicAppendIntoNilPtr)
}

var multi *multiErr
for _, err := range errs {
if err == nil {
continue
}

switch d := (*dest).(type) {
case nil:
*dest = err

case *multiErr:
if internal.TraceStack {
skipStackTrace(err, d.stack.Len())
}
d.errs = append(d.errs, err)
if multi != nil {
multi.append(err)
continue
}

default:
*dest = newMultiErr([]error{*dest, err}, 1)
//goland:noinspection GoTypeAssertionOnErrors
if *dest == nil {
*dest = err
} else if m, ok := (*dest).(*multiErr); ok {
multi = m
multi.append(err)
} else {
multi = newMultiErr([]error{*dest, err}, 1)
*dest = multi
}
}
return multi != nil
}

// AppendFunc appends the non-nil error result of fn to dest using Append.
// AppendFunc appends the non-nil error result of fn to dest using
// AppendInto.
func AppendFunc(dest *error, fn func() error) {
if dest == nil {
panic(panicAppendFuncNilPtr)
}
if fn == nil {
panic(panicAppendFuncNilFn)
}
Append(dest, fn())
AppendInto(dest, fn())
}

type multiErr struct {
Expand All @@ -125,6 +149,13 @@ func newMultiErr(errs []error, skipFrames uint) *multiErr {
return m
}

func (m *multiErr) append(err error) {
if internal.TraceStack {
skipStackTrace(err, m.stack.Len())
}
m.errs = append(m.errs, err)
}

func (m *multiErr) StackTrace() *StackTrace { return m.stack }

// Unwrap returns the errors within the multi error.
Expand Down
22 changes: 12 additions & 10 deletions multi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,22 +125,22 @@ func TestJoin(t *testing.T) {
})
}

func TestAppend(t *testing.T) {
func TestAppendInto(t *testing.T) {
t.Run("panic on nil dest ptr", func(t *testing.T) {
assert.PanicsWithValue(t, panicAppendNilPtr, func() {
Append(nil, New("bar"))
assert.PanicsWithValue(t, panicAppendIntoNilPtr, func() {
AppendInto(nil, New("bar"))
})
})
t.Run("with nil", func(t *testing.T) {
err := New("err")
want := err.Error()
Append(&err, nil)
AppendInto(&err, nil)
assert.Equal(t, want, err.Error())
})
t.Run("with error", func(t *testing.T) {
var have error
want := stderrors.New("foobar")
Append(&have, want)
AppendInto(&have, want)

assert.Same(t, want, have)
})
Expand All @@ -152,9 +152,11 @@ func TestAppend(t *testing.T) {
fmt.Errorf("another %s", "error"),
}

Append(&have, errs[0]) // set value to *have
Append(&have, errs[1]) // create multi error from errors 0 and 1
Append(&have, errs[2]) // append error 2 to multi error
AppendInto(&have,
errs[0], // set value to *have
errs[1], // create multi error from errors 0 and 1
errs[2], // append error 2 to multi error
)

assert.IsType(t, new(multiErr), have)

Expand All @@ -166,8 +168,8 @@ func TestAppend(t *testing.T) {
assert.Equal(t, len(multi.stack.frames), 1)

_, file, line, _ := runtime.Caller(0)
// line must point to the last Append call a couple of lines above
assert.Contains(t, multi.StackTrace().String(), fmt.Sprintf("%s:%d", file, line-12))
// line must point to the last AppendInto call a couple of lines above
assert.Contains(t, multi.StackTrace().String(), fmt.Sprintf("%s:%d", file, line-15))
}
})
}
Expand Down
4 changes: 2 additions & 2 deletions panic.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ func Must(args ...interface{}) {
}

// CatchPanic recovers from a panic and wraps it in an error. It then calls
// Append with the provided dest *error and wrapped panic.
// AppendInto with the provided dest *error and wrapped panic.
// Use CatchPanic directly with defer. It is not possible to use CatchPanic
// inside a deferred function, like:
//
// defer func(){ CatchPanic(&err }()
func CatchPanic(dest *error) {
if r := recover(); r != nil {
Append(dest, newCommonErr(&panicError{v: r}, false, 1))
AppendInto(dest, newCommonErr(&panicError{v: r}, false, 1))
if st := GetStackTrace(*dest); st != nil {
st.Skip = 1
}
Expand Down

0 comments on commit b27cec8

Please sign in to comment.