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 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.
|
||||
// - 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.
|
||||
func Walk(err error, f WalkFunc) {
|
||||
for prev := err; err != nil; prev = err {
|
||||
@ -27,6 +29,11 @@ func Walk(err error, f WalkFunc) {
|
||||
}
|
||||
|
||||
switch e := err.(type) {
|
||||
case multiWrapper:
|
||||
for _, err = range e.Unwrap() {
|
||||
Walk(err, f)
|
||||
}
|
||||
return
|
||||
case causer:
|
||||
err = e.Cause()
|
||||
case wrapper:
|
||||
@ -62,3 +69,6 @@ type causer interface {
|
||||
type wrapper interface {
|
||||
Unwrap() error
|
||||
}
|
||||
type multiWrapper interface {
|
||||
Unwrap() []error
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package errors_test
|
||||
package errors
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@ -6,86 +6,244 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
liberrors "github.com/rclone/rclone/lib/errors"
|
||||
)
|
||||
|
||||
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 {
|
||||
err error
|
||||
calls int
|
||||
last error
|
||||
want []error
|
||||
}{
|
||||
{causerError{nil}, 1, causerError{nil}},
|
||||
{wrapperError{nil}, 1, wrapperError{nil}},
|
||||
{reflectError{nil}, 1, reflectError{nil}},
|
||||
{causerError{origin}, 2, origin},
|
||||
{wrapperError{origin}, 2, origin},
|
||||
{reflectError{origin}, 2, origin},
|
||||
{causerError{reflectError{origin}}, 3, origin},
|
||||
{wrapperError{causerError{origin}}, 3, origin},
|
||||
{reflectError{wrapperError{origin}}, 3, origin},
|
||||
{causerError{reflectError{causerError{origin}}}, 4, origin},
|
||||
{wrapperError{causerError{wrapperError{origin}}}, 4, origin},
|
||||
{reflectError{wrapperError{reflectError{origin}}}, 4, origin},
|
||||
|
||||
{stopError{nil}, 1, stopError{nil}},
|
||||
{stopError{causerError{nil}}, 1, stopError{causerError{nil}}},
|
||||
{stopError{wrapperError{nil}}, 1, stopError{wrapperError{nil}}},
|
||||
{stopError{reflectError{nil}}, 1, stopError{reflectError{nil}}},
|
||||
{causerError{stopError{origin}}, 2, stopError{origin}},
|
||||
{wrapperError{stopError{origin}}, 2, stopError{origin}},
|
||||
{reflectError{stopError{origin}}, 2, stopError{origin}},
|
||||
{causerError{reflectError{stopError{nil}}}, 3, stopError{nil}},
|
||||
{wrapperError{causerError{stopError{nil}}}, 3, stopError{nil}},
|
||||
{reflectError{wrapperError{stopError{nil}}}, 3, stopError{nil}},
|
||||
{
|
||||
causerError{nil}, []error{
|
||||
causerError{nil},
|
||||
},
|
||||
}, {
|
||||
wrapperError{nil}, []error{
|
||||
wrapperError{nil},
|
||||
},
|
||||
}, {
|
||||
reflectError{nil}, []error{
|
||||
reflectError{nil},
|
||||
},
|
||||
}, {
|
||||
causerError{e1}, []error{
|
||||
causerError{e1}, e1,
|
||||
},
|
||||
}, {
|
||||
wrapperError{e1}, []error{
|
||||
wrapperError{e1}, e1,
|
||||
},
|
||||
}, {
|
||||
reflectError{e1}, []error{
|
||||
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
|
||||
calls := 0
|
||||
liberrors.Walk(test.err, func(err error) bool {
|
||||
calls++
|
||||
last = err
|
||||
var got []error
|
||||
Walk(test.err, func(err error) bool {
|
||||
got = append(got, err)
|
||||
_, stop := err.(stopError)
|
||||
return stop
|
||||
})
|
||||
assert.Equal(t, test.calls, calls)
|
||||
assert.Equal(t, test.last, last)
|
||||
assert.Equal(t, test.want, got, test.err)
|
||||
}
|
||||
}
|
||||
|
||||
type causerError struct {
|
||||
err error
|
||||
}
|
||||
type wrapperError struct {
|
||||
err error
|
||||
}
|
||||
type reflectError struct {
|
||||
Err error
|
||||
}
|
||||
type stopError struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (e causerError) Error() string {
|
||||
return fmt.Sprintf("causerError(%s)", e.err)
|
||||
}
|
||||
|
||||
func (e causerError) Cause() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
var (
|
||||
_ error = causerError{nil}
|
||||
_ causer = causerError{nil}
|
||||
)
|
||||
|
||||
type wrapperError struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (e wrapperError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
func (e wrapperError) Error() string {
|
||||
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 {
|
||||
return fmt.Sprintf("reflectError(%s)", e.Err)
|
||||
}
|
||||
|
||||
var (
|
||||
_ error = reflectError{nil}
|
||||
)
|
||||
|
||||
type stopError struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (e stopError) Error() string {
|
||||
return fmt.Sprintf("stopError(%s)", e.err)
|
||||
}
|
||||
|
||||
func (e stopError) Cause() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
var (
|
||||
_ error = stopError{nil}
|
||||
_ causer = stopError{nil}
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user