From d04b0b856a91f07200b4a01200b4ed6b63eb5d63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20M=C3=B6ller?= Date: Sat, 9 Feb 2019 20:39:14 +0100 Subject: [PATCH] fserrors: use errors.Walk for the wrapped error types --- fs/fserrors/error.go | 133 ++++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 72 deletions(-) diff --git a/fs/fserrors/error.go b/fs/fserrors/error.go index e5eb89d49..ee7e93832 100644 --- a/fs/fserrors/error.go +++ b/fs/fserrors/error.go @@ -5,11 +5,10 @@ import ( "fmt" "io" "net/http" - "reflect" "strings" "time" - "github.com/pkg/errors" + "github.com/ncw/rclone/lib/errors" ) // Retrier is an optional interface for error as to whether the @@ -64,17 +63,21 @@ func RetryError(err error) error { return wrappedRetryError{err} } +func (err wrappedRetryError) Cause() error { + return err.error +} + // IsRetryError returns true if err conforms to the Retry interface // and calling the Retry method returns true. -func IsRetryError(err error) bool { - if err == nil { +func IsRetryError(err error) (isRetry bool) { + errors.Walk(err, func(err error) bool { + if r, ok := err.(Retrier); ok { + isRetry = r.Retry() + return true + } return false - } - _, err = Cause(err) - if r, ok := err.(Retrier); ok { - return r.Retry() - } - return false + }) + return } // Fataler is an optional interface for error as to whether the @@ -109,17 +112,21 @@ func FatalError(err error) error { return wrappedFatalError{err} } +func (err wrappedFatalError) Cause() error { + return err.error +} + // IsFatalError returns true if err conforms to the Fatal interface // and calling the Fatal method returns true. -func IsFatalError(err error) bool { - if err == nil { +func IsFatalError(err error) (isFatal bool) { + errors.Walk(err, func(err error) bool { + if r, ok := err.(Fataler); ok { + isFatal = r.Fatal() + return true + } return false - } - _, err = Cause(err) - if r, ok := err.(Fataler); ok { - return r.Fatal() - } - return false + }) + return } // NoRetrier is an optional interface for error as to whether the @@ -154,17 +161,21 @@ func NoRetryError(err error) error { return wrappedNoRetryError{err} } +func (err wrappedNoRetryError) Cause() error { + return err.error +} + // IsNoRetryError returns true if err conforms to the NoRetry // interface and calling the NoRetry method returns true. -func IsNoRetryError(err error) bool { - if err == nil { +func IsNoRetryError(err error) (isNoRetry bool) { + errors.Walk(err, func(err error) bool { + if r, ok := err.(NoRetrier); ok { + isNoRetry = r.NoRetry() + return true + } return false - } - _, err = Cause(err) - if r, ok := err.(NoRetrier); ok { - return r.NoRetry() - } - return false + }) + return } // 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 // indicates or a Zero time.Time -func RetryAfterErrorTime(err error) time.Time { - if err == nil { - return time.Time{} - } - _, err = Cause(err) - if do, ok := err.(RetryAfter); ok { - return do.RetryAfter() - } - return time.Time{} +func RetryAfterErrorTime(err error) (retryAfter time.Time) { + errors.Walk(err, func(err error) bool { + if r, ok := err.(RetryAfter); ok { + retryAfter = r.RetryAfter() + return true + } + return false + }) + return } // 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 // errors had a Timeout() or Temporary() method which returned true. func Cause(cause error) (retriable bool, err error) { - err = cause - for prev := err; err != nil; prev = err { + errors.Walk(cause, func(c error) bool { // Check for net error Timeout() if x, ok := err.(interface { Timeout() bool @@ -238,41 +248,10 @@ func Cause(cause error) (retriable bool, err error) { }); ok && x.Temporary() { retriable = true } - - // Unwrap 1 level if possible - err = errors.Cause(err) - if err == nil { - // 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 + err = c + return false + }) + return } // 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 } + +type causer interface { + Cause() error +} + +var ( + _ causer = wrappedRetryError{} + _ causer = wrappedFatalError{} + _ causer = wrappedNoRetryError{} +)