1
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:
Fabian Möller 2019-02-09 20:39:14 +01:00 committed by Nick Craig-Wood
parent d0ff07bdb0
commit d04b0b856a

View File

@ -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{}
)