mirror of
https://github.com/rclone/rclone
synced 2024-12-25 17:03:45 +01:00
fserrors: use errors.Walk for the wrapped error types
This commit is contained in:
parent
d0ff07bdb0
commit
d04b0b856a
@ -5,11 +5,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/ncw/rclone/lib/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Retrier is an optional interface for error as to whether the
|
// Retrier is an optional interface for error as to whether the
|
||||||
@ -64,17 +63,21 @@ func RetryError(err error) error {
|
|||||||
return wrappedRetryError{err}
|
return wrappedRetryError{err}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (err wrappedRetryError) Cause() error {
|
||||||
|
return err.error
|
||||||
|
}
|
||||||
|
|
||||||
// IsRetryError returns true if err conforms to the Retry interface
|
// IsRetryError returns true if err conforms to the Retry interface
|
||||||
// and calling the Retry method returns true.
|
// and calling the Retry method returns true.
|
||||||
func IsRetryError(err error) bool {
|
func IsRetryError(err error) (isRetry bool) {
|
||||||
if err == nil {
|
errors.Walk(err, func(err error) bool {
|
||||||
|
if r, ok := err.(Retrier); ok {
|
||||||
|
isRetry = r.Retry()
|
||||||
|
return true
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
})
|
||||||
_, err = Cause(err)
|
return
|
||||||
if r, ok := err.(Retrier); ok {
|
|
||||||
return r.Retry()
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fataler is an optional interface for error as to whether the
|
// Fataler is an optional interface for error as to whether the
|
||||||
@ -109,17 +112,21 @@ func FatalError(err error) error {
|
|||||||
return wrappedFatalError{err}
|
return wrappedFatalError{err}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (err wrappedFatalError) Cause() error {
|
||||||
|
return err.error
|
||||||
|
}
|
||||||
|
|
||||||
// IsFatalError returns true if err conforms to the Fatal interface
|
// IsFatalError returns true if err conforms to the Fatal interface
|
||||||
// and calling the Fatal method returns true.
|
// and calling the Fatal method returns true.
|
||||||
func IsFatalError(err error) bool {
|
func IsFatalError(err error) (isFatal bool) {
|
||||||
if err == nil {
|
errors.Walk(err, func(err error) bool {
|
||||||
|
if r, ok := err.(Fataler); ok {
|
||||||
|
isFatal = r.Fatal()
|
||||||
|
return true
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
})
|
||||||
_, err = Cause(err)
|
return
|
||||||
if r, ok := err.(Fataler); ok {
|
|
||||||
return r.Fatal()
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NoRetrier is an optional interface for error as to whether the
|
// NoRetrier is an optional interface for error as to whether the
|
||||||
@ -154,17 +161,21 @@ func NoRetryError(err error) error {
|
|||||||
return wrappedNoRetryError{err}
|
return wrappedNoRetryError{err}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (err wrappedNoRetryError) Cause() error {
|
||||||
|
return err.error
|
||||||
|
}
|
||||||
|
|
||||||
// IsNoRetryError returns true if err conforms to the NoRetry
|
// IsNoRetryError returns true if err conforms to the NoRetry
|
||||||
// interface and calling the NoRetry method returns true.
|
// interface and calling the NoRetry method returns true.
|
||||||
func IsNoRetryError(err error) bool {
|
func IsNoRetryError(err error) (isNoRetry bool) {
|
||||||
if err == nil {
|
errors.Walk(err, func(err error) bool {
|
||||||
|
if r, ok := err.(NoRetrier); ok {
|
||||||
|
isNoRetry = r.NoRetry()
|
||||||
|
return true
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
})
|
||||||
_, err = Cause(err)
|
return
|
||||||
if r, ok := err.(NoRetrier); ok {
|
|
||||||
return r.NoRetry()
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetryAfter is an optional interface for error as to whether the
|
// RetryAfter is an optional interface for error as to whether the
|
||||||
@ -203,15 +214,15 @@ var _ RetryAfter = ErrorRetryAfter{}
|
|||||||
|
|
||||||
// RetryAfterErrorTime returns the time that the RetryAfter error
|
// RetryAfterErrorTime returns the time that the RetryAfter error
|
||||||
// indicates or a Zero time.Time
|
// indicates or a Zero time.Time
|
||||||
func RetryAfterErrorTime(err error) time.Time {
|
func RetryAfterErrorTime(err error) (retryAfter time.Time) {
|
||||||
if err == nil {
|
errors.Walk(err, func(err error) bool {
|
||||||
return time.Time{}
|
if r, ok := err.(RetryAfter); ok {
|
||||||
}
|
retryAfter = r.RetryAfter()
|
||||||
_, err = Cause(err)
|
return true
|
||||||
if do, ok := err.(RetryAfter); ok {
|
}
|
||||||
return do.RetryAfter()
|
return false
|
||||||
}
|
})
|
||||||
return time.Time{}
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsRetryAfterError returns true if err is an ErrorRetryAfter
|
// IsRetryAfterError returns true if err is an ErrorRetryAfter
|
||||||
@ -223,8 +234,7 @@ func IsRetryAfterError(err error) bool {
|
|||||||
// library errors too. It returns true if any of the intermediate
|
// library errors too. It returns true if any of the intermediate
|
||||||
// errors had a Timeout() or Temporary() method which returned true.
|
// errors had a Timeout() or Temporary() method which returned true.
|
||||||
func Cause(cause error) (retriable bool, err error) {
|
func Cause(cause error) (retriable bool, err error) {
|
||||||
err = cause
|
errors.Walk(cause, func(c error) bool {
|
||||||
for prev := err; err != nil; prev = err {
|
|
||||||
// Check for net error Timeout()
|
// Check for net error Timeout()
|
||||||
if x, ok := err.(interface {
|
if x, ok := err.(interface {
|
||||||
Timeout() bool
|
Timeout() bool
|
||||||
@ -238,41 +248,10 @@ func Cause(cause error) (retriable bool, err error) {
|
|||||||
}); ok && x.Temporary() {
|
}); ok && x.Temporary() {
|
||||||
retriable = true
|
retriable = true
|
||||||
}
|
}
|
||||||
|
err = c
|
||||||
// Unwrap 1 level if possible
|
return false
|
||||||
err = errors.Cause(err)
|
})
|
||||||
if err == nil {
|
return
|
||||||
// errors.Cause can return nil which isn't
|
|
||||||
// desirable so pick the previous error in
|
|
||||||
// this case.
|
|
||||||
err = prev
|
|
||||||
}
|
|
||||||
if reflect.DeepEqual(err, prev) {
|
|
||||||
// Unpack any struct or *struct with a field
|
|
||||||
// of name Err which satisfies the error
|
|
||||||
// interface. This includes *url.Error,
|
|
||||||
// *net.OpError, *os.SyscallError and many
|
|
||||||
// others in the stdlib
|
|
||||||
errType := reflect.TypeOf(err)
|
|
||||||
errValue := reflect.ValueOf(err)
|
|
||||||
if errValue.IsValid() && errType.Kind() == reflect.Ptr {
|
|
||||||
errType = errType.Elem()
|
|
||||||
errValue = errValue.Elem()
|
|
||||||
}
|
|
||||||
if errValue.IsValid() && errType.Kind() == reflect.Struct {
|
|
||||||
if errField := errValue.FieldByName("Err"); errField.IsValid() {
|
|
||||||
errFieldValue := errField.Interface()
|
|
||||||
if newErr, ok := errFieldValue.(error); ok {
|
|
||||||
err = newErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if reflect.DeepEqual(err, prev) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return retriable, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// retriableErrorStrings is a list of phrases which when we find it
|
// retriableErrorStrings is a list of phrases which when we find it
|
||||||
@ -344,3 +323,13 @@ func ShouldRetryHTTP(resp *http.Response, retryErrorCodes []int) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type causer interface {
|
||||||
|
Cause() error
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ causer = wrappedRetryError{}
|
||||||
|
_ causer = wrappedFatalError{}
|
||||||
|
_ causer = wrappedNoRetryError{}
|
||||||
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user