Skip to content

Commit

Permalink
errorsbp: Update for go 1.20
Browse files Browse the repository at this point in the history
Go 1.20 is going to add native support for batched/multi errors, and we
will be able to eventually deprecate errorsbp.Batch, but we can only do
so after we dropped support for go 1.19.

Before that, make errorsbp.Batch play nicer with go 1.20 by:

* Implement the new `Unwrap() []error` optional interface.
* Make errorsbp.BatchSize also support other errors implementing the new
  optional interface.
  • Loading branch information
fishy committed Jan 5, 2023
1 parent 480e9af commit 8508710
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 0 deletions.
21 changes: 21 additions & 0 deletions errorsbp/batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,23 @@ import (
var (
_ error = Batch{}
_ error = (*Batch)(nil)

_ batchUnwrapper = Batch{}
_ batchUnwrapper = (*Batch)(nil)
)

type batchUnwrapper interface {
Unwrap() []error
}

// Batch is an error that can contain multiple errors.
//
// The zero value of Batch is valid (with no errors) and ready to use.
//
// This type is not thread-safe.
// The same batch should not be operated on different goroutines concurrently.
//
// To be deprecated when we drop support for go 1.19.
type Batch struct {
errors []error
}
Expand Down Expand Up @@ -185,10 +194,19 @@ func (be Batch) GetErrors() []error {
return errors
}

// Unwrap implements the optional interface defined in go 1.20.
//
// It's an alias to GetErrors.
func (be Batch) Unwrap() []error {
return be.GetErrors()
}

// BatchSize returns the size of the batch for error err.
//
// If err is either errorsbp.Batch or *errorsbp.Batch,
// this function returns its Len().
// If err implements `Unwrap() []error` (optional interface defined in go 1.20),
// it returns the length of its Unwrap return.
// Otherwise, it returns 1 if err is non-nil, and 0 if err is nil.
//
// It's useful in tests,
Expand All @@ -202,6 +220,9 @@ func BatchSize(err error) int {
if errors.As(err, &be) {
return be.Len()
}
if unwrapper, ok := err.(batchUnwrapper); ok {
return len(unwrapper.Unwrap())
}
// single, non-batch error.
return 1
}
57 changes: 57 additions & 0 deletions errorsbp/batch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package errorsbp_test

import (
"errors"
"fmt"
"reflect"
"testing"

Expand Down Expand Up @@ -265,3 +266,59 @@ func TestAddPrefix(t *testing.T) {
}
}
}

func TestBatchSize(t *testing.T) {
for _, c := range []struct {
label string
err error
want int
}{
{
label: "nil",
err: nil,
want: 0,
},
{
label: "errors.New",
err: errors.New("foo"),
want: 1,
},
{
label: "fmt.Errorf-wrap-single",
err: fmt.Errorf("bar: %w", errors.New("foo")),
want: 1,
},
{
label: "batch-0",
err: new(errorsbp.Batch),
want: 0,
},
{
label: "batch-1",
want: 1,
err: func() error {
var batch errorsbp.Batch
batch.Add(errors.New("foo"))
return batch
}(),
},
{
label: "batch-2",
want: 2,
err: func() error {
var batch errorsbp.Batch
batch.Add(errors.New("foo"))
batch.Add(errors.New("bar"))
return batch
}(),
},
// TODO: Add cases from errors.Join and fmt.Errorf once we drop support for
// go 1.19.
} {
t.Run(c.label, func(t *testing.T) {
if got := errorsbp.BatchSize(c.err); got != c.want {
t.Errorf("errorsbp.BatchSize(%#v) got %v want %v", c.err, got, c.want)
}
})
}
}

0 comments on commit 8508710

Please sign in to comment.