1package awserr 2 3import "fmt" 4 5// SprintError returns a string of the formatted error code. 6// 7// Both extra and origErr are optional. If they are included their lines 8// will be added, but if they are not included their lines will be ignored. 9func SprintError(code, message, extra string, origErr error) string { 10 msg := fmt.Sprintf("%s: %s", code, message) 11 if extra != "" { 12 msg = fmt.Sprintf("%s\n\t%s", msg, extra) 13 } 14 if origErr != nil { 15 msg = fmt.Sprintf("%s\ncaused by: %s", msg, origErr.Error()) 16 } 17 return msg 18} 19 20// A baseError wraps the code and message which defines an error. It also 21// can be used to wrap an original error object. 22// 23// Should be used as the root for errors satisfying the awserr.Error. Also 24// for any error which does not fit into a specific error wrapper type. 25type baseError struct { 26 // Classification of error 27 code string 28 29 // Detailed information about error 30 message string 31 32 // Optional original error this error is based off of. Allows building 33 // chained errors. 34 errs []error 35} 36 37// newBaseError returns an error object for the code, message, and errors. 38// 39// code is a short no whitespace phrase depicting the classification of 40// the error that is being created. 41// 42// message is the free flow string containing detailed information about the 43// error. 44// 45// origErrs is the error objects which will be nested under the new errors to 46// be returned. 47func newBaseError(code, message string, origErrs []error) *baseError { 48 b := &baseError{ 49 code: code, 50 message: message, 51 errs: origErrs, 52 } 53 54 return b 55} 56 57// Error returns the string representation of the error. 58// 59// See ErrorWithExtra for formatting. 60// 61// Satisfies the error interface. 62func (b baseError) Error() string { 63 size := len(b.errs) 64 if size > 0 { 65 return SprintError(b.code, b.message, "", errorList(b.errs)) 66 } 67 68 return SprintError(b.code, b.message, "", nil) 69} 70 71// String returns the string representation of the error. 72// Alias for Error to satisfy the stringer interface. 73func (b baseError) String() string { 74 return b.Error() 75} 76 77// Code returns the short phrase depicting the classification of the error. 78func (b baseError) Code() string { 79 return b.code 80} 81 82// Message returns the error details message. 83func (b baseError) Message() string { 84 return b.message 85} 86 87// OrigErr returns the original error if one was set. Nil is returned if no 88// error was set. This only returns the first element in the list. If the full 89// list is needed, use BatchedErrors. 90func (b baseError) OrigErr() error { 91 switch len(b.errs) { 92 case 0: 93 return nil 94 case 1: 95 return b.errs[0] 96 default: 97 if err, ok := b.errs[0].(Error); ok { 98 return NewBatchError(err.Code(), err.Message(), b.errs[1:]) 99 } 100 return NewBatchError("BatchedErrors", 101 "multiple errors occurred", b.errs) 102 } 103} 104 105// OrigErrs returns the original errors if one was set. An empty slice is 106// returned if no error was set. 107func (b baseError) OrigErrs() []error { 108 return b.errs 109} 110 111// So that the Error interface type can be included as an anonymous field 112// in the requestError struct and not conflict with the error.Error() method. 113type awsError Error 114 115// A requestError wraps a request or service error. 116// 117// Composed of baseError for code, message, and original error. 118type requestError struct { 119 awsError 120 statusCode int 121 requestID string 122} 123 124// newRequestError returns a wrapped error with additional information for 125// request status code, and service requestID. 126// 127// Should be used to wrap all request which involve service requests. Even if 128// the request failed without a service response, but had an HTTP status code 129// that may be meaningful. 130// 131// Also wraps original errors via the baseError. 132func newRequestError(err Error, statusCode int, requestID string) *requestError { 133 return &requestError{ 134 awsError: err, 135 statusCode: statusCode, 136 requestID: requestID, 137 } 138} 139 140// Error returns the string representation of the error. 141// Satisfies the error interface. 142func (r requestError) Error() string { 143 extra := fmt.Sprintf("status code: %d, request id: %s", 144 r.statusCode, r.requestID) 145 return SprintError(r.Code(), r.Message(), extra, r.OrigErr()) 146} 147 148// String returns the string representation of the error. 149// Alias for Error to satisfy the stringer interface. 150func (r requestError) String() string { 151 return r.Error() 152} 153 154// StatusCode returns the wrapped status code for the error 155func (r requestError) StatusCode() int { 156 return r.statusCode 157} 158 159// RequestID returns the wrapped requestID 160func (r requestError) RequestID() string { 161 return r.requestID 162} 163 164// OrigErrs returns the original errors if one was set. An empty slice is 165// returned if no error was set. 166func (r requestError) OrigErrs() []error { 167 if b, ok := r.awsError.(BatchedErrors); ok { 168 return b.OrigErrs() 169 } 170 return []error{r.OrigErr()} 171} 172 173// An error list that satisfies the golang interface 174type errorList []error 175 176// Error returns the string representation of the error. 177// 178// Satisfies the error interface. 179func (e errorList) Error() string { 180 msg := "" 181 // How do we want to handle the array size being zero 182 if size := len(e); size > 0 { 183 for i := 0; i < size; i++ { 184 msg += fmt.Sprintf("%s", e[i].Error()) 185 // We check the next index to see if it is within the slice. 186 // If it is, then we append a newline. We do this, because unit tests 187 // could be broken with the additional '\n' 188 if i+1 < size { 189 msg += "\n" 190 } 191 } 192 } 193 return msg 194} 195