package awserr import "fmt" // SprintError returns a string of the formatted error code. // // Both extra and origErr are optional. If they are included their lines // will be added, but if they are not included their lines will be ignored. func SprintError(code, message, extra string, origErr error) string { msg := fmt.Sprintf("%s: %s", code, message) if extra != "" { msg = fmt.Sprintf("%s\n\t%s", msg, extra) } if origErr != nil { msg = fmt.Sprintf("%s\ncaused by: %s", msg, origErr.Error()) } return msg } // A baseError wraps the code and message which defines an error. It also // can be used to wrap an original error object. // // Should be used as the root for errors satisfying the awserr.Error. Also // for any error which does not fit into a specific error wrapper type. type baseError struct { // Classification of error code string // Detailed information about error message string // Optional original error this error is based off of. Allows building // chained errors. errs []error } // newBaseError returns an error object for the code, message, and errors. // // code is a short no whitespace phrase depicting the classification of // the error that is being created. // // message is the free flow string containing detailed information about the // error. // // origErrs is the error objects which will be nested under the new errors to // be returned. func newBaseError(code, message string, origErrs []error) *baseError { b := &baseError{ code: code, message: message, errs: origErrs, } return b } // Error returns the string representation of the error. // // See ErrorWithExtra for formatting. // // Satisfies the error interface. func (b baseError) Error() string { size := len(b.errs) if size > 0 { return SprintError(b.code, b.message, "", errorList(b.errs)) } return SprintError(b.code, b.message, "", nil) } // String returns the string representation of the error. // Alias for Error to satisfy the stringer interface. func (b baseError) String() string { return b.Error() } // Code returns the short phrase depicting the classification of the error. func (b baseError) Code() string { return b.code } // Message returns the error details message. func (b baseError) Message() string { return b.message } // OrigErr returns the original error if one was set. Nil is returned if no // error was set. This only returns the first element in the list. If the full // list is needed, use BatchedErrors. func (b baseError) OrigErr() error { switch len(b.errs) { case 0: return nil case 1: return b.errs[0] default: if err, ok := b.errs[0].(Error); ok { return NewBatchError(err.Code(), err.Message(), b.errs[1:]) } return NewBatchError("BatchedErrors", "multiple errors occured", b.errs) } } // OrigErrs returns the original errors if one was set. An empty slice is // returned if no error was set. func (b baseError) OrigErrs() []error { return b.errs } // So that the Error interface type can be included as an anonymous field // in the requestError struct and not conflict with the error.Error() method. type awsError Error // A requestError wraps a request or service error. // // Composed of baseError for code, message, and original error. type requestError struct { awsError statusCode int requestID string } // newRequestError returns a wrapped error with additional information for // request status code, and service requestID. // // Should be used to wrap all request which involve service requests. Even if // the request failed without a service response, but had an HTTP status code // that may be meaningful. // // Also wraps original errors via the baseError. func newRequestError(err Error, statusCode int, requestID string) *requestError { return &requestError{ awsError: err, statusCode: statusCode, requestID: requestID, } } // Error returns the string representation of the error. // Satisfies the error interface. func (r requestError) Error() string { extra := fmt.Sprintf("status code: %d, request id: %s", r.statusCode, r.requestID) return SprintError(r.Code(), r.Message(), extra, r.OrigErr()) } // String returns the string representation of the error. // Alias for Error to satisfy the stringer interface. func (r requestError) String() string { return r.Error() } // StatusCode returns the wrapped status code for the error func (r requestError) StatusCode() int { return r.statusCode } // RequestID returns the wrapped requestID func (r requestError) RequestID() string { return r.requestID } // OrigErrs returns the original errors if one was set. An empty slice is // returned if no error was set. func (r requestError) OrigErrs() []error { if b, ok := r.awsError.(BatchedErrors); ok { return b.OrigErrs() } return []error{r.OrigErr()} } // An error list that satisfies the golang interface type errorList []error // Error returns the string representation of the error. // // Satisfies the error interface. func (e errorList) Error() string { msg := "" // How do we want to handle the array size being zero if size := len(e); size > 0 { for i := 0; i < size; i++ { msg += fmt.Sprintf("%s", e[i].Error()) // We check the next index to see if it is within the slice. // If it is, then we append a newline. We do this, because unit tests // could be broken with the additional '\n' if i+1 < size { msg += "\n" } } } return msg }