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