mirror of
https://github.com/rclone/rclone
synced 2024-12-23 14:23:44 +01:00
lib/errors: add support for unwrapping go1.20 multi errors
This commit is contained in:
parent
ca9182d6ae
commit
844e8fb8bd
@ -17,8 +17,10 @@ type WalkFunc func(error) bool
|
|||||||
// The next error in the chain is determined by the following rules:
|
// The next error in the chain is determined by the following rules:
|
||||||
//
|
//
|
||||||
// the return value of this method is used.
|
// the return value of this method is used.
|
||||||
// - If the current error has a `Unwrap() error` method (golang.org/x/xerrors),
|
// - If the current error has a `Unwrap() error` method
|
||||||
// the return value of this method is used.
|
// the return value of this method is used.
|
||||||
|
// - If the current error has a `Unwrap() []error` method
|
||||||
|
// the return values of this method is used.
|
||||||
// - Common errors in the Go runtime that contain an Err field will use this value.
|
// - Common errors in the Go runtime that contain an Err field will use this value.
|
||||||
func Walk(err error, f WalkFunc) {
|
func Walk(err error, f WalkFunc) {
|
||||||
for prev := err; err != nil; prev = err {
|
for prev := err; err != nil; prev = err {
|
||||||
@ -27,6 +29,11 @@ func Walk(err error, f WalkFunc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch e := err.(type) {
|
switch e := err.(type) {
|
||||||
|
case multiWrapper:
|
||||||
|
for _, err = range e.Unwrap() {
|
||||||
|
Walk(err, f)
|
||||||
|
}
|
||||||
|
return
|
||||||
case causer:
|
case causer:
|
||||||
err = e.Cause()
|
err = e.Cause()
|
||||||
case wrapper:
|
case wrapper:
|
||||||
@ -62,3 +69,6 @@ type causer interface {
|
|||||||
type wrapper interface {
|
type wrapper interface {
|
||||||
Unwrap() error
|
Unwrap() error
|
||||||
}
|
}
|
||||||
|
type multiWrapper interface {
|
||||||
|
Unwrap() []error
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package errors_test
|
package errors
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@ -6,86 +6,244 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
liberrors "github.com/rclone/rclone/lib/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWalk(t *testing.T) {
|
func TestWalk(t *testing.T) {
|
||||||
origin := errors.New("origin")
|
var (
|
||||||
|
e1 = errors.New("e1")
|
||||||
|
e2 = errors.New("e2")
|
||||||
|
e3 = errors.New("e3")
|
||||||
|
)
|
||||||
|
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
err error
|
err error
|
||||||
calls int
|
want []error
|
||||||
last error
|
|
||||||
}{
|
}{
|
||||||
{causerError{nil}, 1, causerError{nil}},
|
{
|
||||||
{wrapperError{nil}, 1, wrapperError{nil}},
|
causerError{nil}, []error{
|
||||||
{reflectError{nil}, 1, reflectError{nil}},
|
causerError{nil},
|
||||||
{causerError{origin}, 2, origin},
|
},
|
||||||
{wrapperError{origin}, 2, origin},
|
}, {
|
||||||
{reflectError{origin}, 2, origin},
|
wrapperError{nil}, []error{
|
||||||
{causerError{reflectError{origin}}, 3, origin},
|
wrapperError{nil},
|
||||||
{wrapperError{causerError{origin}}, 3, origin},
|
},
|
||||||
{reflectError{wrapperError{origin}}, 3, origin},
|
}, {
|
||||||
{causerError{reflectError{causerError{origin}}}, 4, origin},
|
reflectError{nil}, []error{
|
||||||
{wrapperError{causerError{wrapperError{origin}}}, 4, origin},
|
reflectError{nil},
|
||||||
{reflectError{wrapperError{reflectError{origin}}}, 4, origin},
|
},
|
||||||
|
}, {
|
||||||
{stopError{nil}, 1, stopError{nil}},
|
causerError{e1}, []error{
|
||||||
{stopError{causerError{nil}}, 1, stopError{causerError{nil}}},
|
causerError{e1}, e1,
|
||||||
{stopError{wrapperError{nil}}, 1, stopError{wrapperError{nil}}},
|
},
|
||||||
{stopError{reflectError{nil}}, 1, stopError{reflectError{nil}}},
|
}, {
|
||||||
{causerError{stopError{origin}}, 2, stopError{origin}},
|
wrapperError{e1}, []error{
|
||||||
{wrapperError{stopError{origin}}, 2, stopError{origin}},
|
wrapperError{e1}, e1,
|
||||||
{reflectError{stopError{origin}}, 2, stopError{origin}},
|
},
|
||||||
{causerError{reflectError{stopError{nil}}}, 3, stopError{nil}},
|
}, {
|
||||||
{wrapperError{causerError{stopError{nil}}}, 3, stopError{nil}},
|
reflectError{e1}, []error{
|
||||||
{reflectError{wrapperError{stopError{nil}}}, 3, stopError{nil}},
|
reflectError{e1}, e1,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
causerError{reflectError{e1}}, []error{
|
||||||
|
causerError{reflectError{e1}},
|
||||||
|
reflectError{e1},
|
||||||
|
e1,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
wrapperError{causerError{e1}}, []error{
|
||||||
|
wrapperError{causerError{e1}},
|
||||||
|
causerError{e1},
|
||||||
|
e1,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
reflectError{wrapperError{e1}}, []error{
|
||||||
|
reflectError{wrapperError{e1}},
|
||||||
|
wrapperError{e1},
|
||||||
|
e1,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
causerError{reflectError{causerError{e1}}}, []error{
|
||||||
|
causerError{reflectError{causerError{e1}}},
|
||||||
|
reflectError{causerError{e1}},
|
||||||
|
causerError{e1},
|
||||||
|
e1,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
wrapperError{causerError{wrapperError{e1}}}, []error{
|
||||||
|
wrapperError{causerError{wrapperError{e1}}},
|
||||||
|
causerError{wrapperError{e1}},
|
||||||
|
wrapperError{e1},
|
||||||
|
e1,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
reflectError{wrapperError{reflectError{e1}}}, []error{
|
||||||
|
reflectError{wrapperError{reflectError{e1}}},
|
||||||
|
wrapperError{reflectError{e1}},
|
||||||
|
reflectError{e1},
|
||||||
|
e1,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
stopError{nil}, []error{
|
||||||
|
stopError{nil},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
stopError{causerError{nil}}, []error{
|
||||||
|
stopError{causerError{nil}},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
stopError{wrapperError{nil}}, []error{
|
||||||
|
stopError{wrapperError{nil}},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
stopError{reflectError{nil}}, []error{
|
||||||
|
stopError{reflectError{nil}},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
causerError{stopError{e1}}, []error{
|
||||||
|
causerError{stopError{e1}},
|
||||||
|
stopError{e1},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
wrapperError{stopError{e1}}, []error{
|
||||||
|
wrapperError{stopError{e1}},
|
||||||
|
stopError{e1},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
reflectError{stopError{e1}}, []error{
|
||||||
|
reflectError{stopError{e1}},
|
||||||
|
stopError{e1},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
causerError{reflectError{stopError{nil}}}, []error{
|
||||||
|
causerError{reflectError{stopError{nil}}},
|
||||||
|
reflectError{stopError{nil}},
|
||||||
|
stopError{nil},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
wrapperError{causerError{stopError{nil}}}, []error{
|
||||||
|
wrapperError{causerError{stopError{nil}}},
|
||||||
|
causerError{stopError{nil}},
|
||||||
|
stopError{nil},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
reflectError{wrapperError{stopError{nil}}}, []error{
|
||||||
|
reflectError{wrapperError{stopError{nil}}},
|
||||||
|
wrapperError{stopError{nil}},
|
||||||
|
stopError{nil},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
multiWrapperError{[]error{e1}}, []error{
|
||||||
|
multiWrapperError{[]error{e1}},
|
||||||
|
e1,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
multiWrapperError{[]error{}}, []error{
|
||||||
|
multiWrapperError{[]error{}},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
multiWrapperError{[]error{e1, e2, e3}}, []error{
|
||||||
|
multiWrapperError{[]error{e1, e2, e3}},
|
||||||
|
e1,
|
||||||
|
e2,
|
||||||
|
e3,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
multiWrapperError{[]error{reflectError{e1}, wrapperError{e2}, stopError{e3}}}, []error{
|
||||||
|
multiWrapperError{[]error{reflectError{e1}, wrapperError{e2}, stopError{e3}}},
|
||||||
|
reflectError{e1},
|
||||||
|
e1,
|
||||||
|
wrapperError{e2},
|
||||||
|
e2,
|
||||||
|
stopError{e3},
|
||||||
|
},
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
var last error
|
var got []error
|
||||||
calls := 0
|
Walk(test.err, func(err error) bool {
|
||||||
liberrors.Walk(test.err, func(err error) bool {
|
got = append(got, err)
|
||||||
calls++
|
|
||||||
last = err
|
|
||||||
_, stop := err.(stopError)
|
_, stop := err.(stopError)
|
||||||
return stop
|
return stop
|
||||||
})
|
})
|
||||||
assert.Equal(t, test.calls, calls)
|
assert.Equal(t, test.want, got, test.err)
|
||||||
assert.Equal(t, test.last, last)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type causerError struct {
|
type causerError struct {
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
type wrapperError struct {
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
type reflectError struct {
|
|
||||||
Err error
|
|
||||||
}
|
|
||||||
type stopError struct {
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e causerError) Error() string {
|
func (e causerError) Error() string {
|
||||||
return fmt.Sprintf("causerError(%s)", e.err)
|
return fmt.Sprintf("causerError(%s)", e.err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e causerError) Cause() error {
|
func (e causerError) Cause() error {
|
||||||
return e.err
|
return e.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ error = causerError{nil}
|
||||||
|
_ causer = causerError{nil}
|
||||||
|
)
|
||||||
|
|
||||||
|
type wrapperError struct {
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
func (e wrapperError) Unwrap() error {
|
func (e wrapperError) Unwrap() error {
|
||||||
return e.err
|
return e.err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e wrapperError) Error() string {
|
func (e wrapperError) Error() string {
|
||||||
return fmt.Sprintf("wrapperError(%s)", e.err)
|
return fmt.Sprintf("wrapperError(%s)", e.err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ error = wrapperError{nil}
|
||||||
|
_ wrapper = wrapperError{nil}
|
||||||
|
)
|
||||||
|
|
||||||
|
type multiWrapperError struct {
|
||||||
|
errs []error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e multiWrapperError) Unwrap() []error {
|
||||||
|
return e.errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e multiWrapperError) Error() string {
|
||||||
|
return fmt.Sprintf("multiWrapperError(%s)", e.errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ error = multiWrapperError{nil}
|
||||||
|
_ multiWrapper = multiWrapperError{nil}
|
||||||
|
)
|
||||||
|
|
||||||
|
type reflectError struct {
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
func (e reflectError) Error() string {
|
func (e reflectError) Error() string {
|
||||||
return fmt.Sprintf("reflectError(%s)", e.Err)
|
return fmt.Sprintf("reflectError(%s)", e.Err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ error = reflectError{nil}
|
||||||
|
)
|
||||||
|
|
||||||
|
type stopError struct {
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
func (e stopError) Error() string {
|
func (e stopError) Error() string {
|
||||||
return fmt.Sprintf("stopError(%s)", e.err)
|
return fmt.Sprintf("stopError(%s)", e.err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e stopError) Cause() error {
|
func (e stopError) Cause() error {
|
||||||
return e.err
|
return e.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ error = stopError{nil}
|
||||||
|
_ causer = stopError{nil}
|
||||||
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user