Skip to content

Commit d8067ab

Browse files
authored
Support multierr.Every (#78)
This PR introduces a new exported function in the multierr package called `Every`, which checks every error within a given error against a given target using `errors.Is`, and only returns true if all checks return true. ```go err := multierr.Combine(ErrIgnorable, errors.New("great sadness")) fmt.Println(errors.Is(err, ErrIgnorable)) // output = "true" fmt.Println(multierr.Every(err, ErrIgnorable)) // output = "false" err := multierr.Combine(ErrIgnorable, ErrIgnorable) fmt.Println(errors.Is(err, ErrIgnorable)) // output = "true" fmt.Println(multierr.Every(err, ErrIgnorable)) // output = "true" ``` This also works when the error passed in as the first argument is not a `multiErr`, including when it is a go1.20+ error that implements `Unwrap() []error`. This solves #66.
1 parent d42b7a1 commit d8067ab

File tree

3 files changed

+96
-0
lines changed

3 files changed

+96
-0
lines changed

error.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ package multierr // import "go.uber.org/multierr"
142142

143143
import (
144144
"bytes"
145+
"errors"
145146
"fmt"
146147
"io"
147148
"strings"
@@ -234,6 +235,17 @@ func (merr *multiError) Error() string {
234235
return result
235236
}
236237

238+
// Every compares every error in the given err against the given target error
239+
// using [errors.Is], and returns true only if every comparison returned true.
240+
func Every(err error, target error) bool {
241+
for _, e := range extractErrors(err) {
242+
if !errors.Is(e, target) {
243+
return false
244+
}
245+
}
246+
return true
247+
}
248+
237249
func (merr *multiError) Format(f fmt.State, c rune) {
238250
if c == 'v' && f.Flag('+') {
239251
merr.writeMultiline(f)

error_post_go120_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,26 @@ func TestErrorsOnErrorsJoin(t *testing.T) {
4040
assert.Equal(t, err1, errs[0])
4141
assert.Equal(t, err2, errs[1])
4242
}
43+
44+
func TestEveryWithErrorsJoin(t *testing.T) {
45+
myError1 := errors.New("woeful misfortune")
46+
myError2 := errors.New("worrisome travesty")
47+
48+
t.Run("all match", func(t *testing.T) {
49+
err := errors.Join(myError1, myError1, myError1)
50+
51+
assert.True(t, errors.Is(err, myError1))
52+
assert.True(t, Every(err, myError1))
53+
assert.False(t, errors.Is(err, myError2))
54+
assert.False(t, Every(err, myError2))
55+
})
56+
57+
t.Run("one matches", func(t *testing.T) {
58+
err := errors.Join(myError1, myError2)
59+
60+
assert.True(t, errors.Is(err, myError1))
61+
assert.False(t, Every(err, myError1))
62+
assert.True(t, errors.Is(err, myError2))
63+
assert.False(t, Every(err, myError2))
64+
})
65+
}

error_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,67 @@ func newMultiErr(errors ...error) error {
5959
return &multiError{errors: errors}
6060
}
6161

62+
func TestEvery(t *testing.T) {
63+
myError1 := errors.New("woeful misfortune")
64+
myError2 := errors.New("worrisome travesty")
65+
66+
for _, tt := range []struct {
67+
desc string
68+
giveErr error
69+
giveTarget error
70+
wantIs bool
71+
wantEvery bool
72+
}{
73+
{
74+
desc: "all match",
75+
giveErr: newMultiErr(myError1, myError1, myError1),
76+
giveTarget: myError1,
77+
wantIs: true,
78+
wantEvery: true,
79+
},
80+
{
81+
desc: "one matches",
82+
giveErr: newMultiErr(myError1, myError2),
83+
giveTarget: myError1,
84+
wantIs: true,
85+
wantEvery: false,
86+
},
87+
{
88+
desc: "not multiErrs and non equal",
89+
giveErr: myError1,
90+
giveTarget: myError2,
91+
wantIs: false,
92+
wantEvery: false,
93+
},
94+
{
95+
desc: "not multiErrs but equal",
96+
giveErr: myError1,
97+
giveTarget: myError1,
98+
wantIs: true,
99+
wantEvery: true,
100+
},
101+
{
102+
desc: "not multiErr w multiErr target",
103+
giveErr: myError1,
104+
giveTarget: newMultiErr(myError1, myError1),
105+
wantIs: false,
106+
wantEvery: false,
107+
},
108+
{
109+
desc: "multiErr w multiErr target",
110+
giveErr: newMultiErr(myError1, myError1),
111+
giveTarget: newMultiErr(myError1, myError1),
112+
wantIs: false,
113+
wantEvery: false,
114+
},
115+
} {
116+
t.Run(tt.desc, func(t *testing.T) {
117+
assert.Equal(t, tt.wantIs, errors.Is(tt.giveErr, tt.giveTarget))
118+
assert.Equal(t, tt.wantEvery, Every(tt.giveErr, tt.giveTarget))
119+
})
120+
}
121+
}
122+
62123
func TestCombine(t *testing.T) {
63124
tests := []struct {
64125
// Input

0 commit comments

Comments
 (0)