]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package awserr |
2 | ||
863486a6 AG |
3 | import ( |
4 | "encoding/hex" | |
5 | "fmt" | |
6 | ) | |
bae9f6d2 JC |
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. | |
12 | func 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. | |
28 | type 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. | |
50 | func 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. | |
65 | func (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. | |
76 | func (b baseError) String() string { | |
77 | return b.Error() | |
78 | } | |
79 | ||
80 | // Code returns the short phrase depicting the classification of the error. | |
81 | func (b baseError) Code() string { | |
82 | return b.code | |
83 | } | |
84 | ||
85 | // Message returns the error details message. | |
86 | func (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. | |
93 | func (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. | |
110 | func (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. | |
116 | type awsError Error | |
117 | ||
118 | // A requestError wraps a request or service error. | |
119 | // | |
120 | // Composed of baseError for code, message, and original error. | |
121 | type requestError struct { | |
122 | awsError | |
123 | statusCode int | |
124 | requestID string | |
863486a6 | 125 | bytes []byte |
bae9f6d2 JC |
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. | |
136 | func 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. | |
146 | func (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. | |
154 | func (r requestError) String() string { | |
155 | return r.Error() | |
156 | } | |
157 | ||
158 | // StatusCode returns the wrapped status code for the error | |
159 | func (r requestError) StatusCode() int { | |
160 | return r.statusCode | |
161 | } | |
162 | ||
163 | // RequestID returns the wrapped requestID | |
164 | func (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. | |
170 | func (r requestError) OrigErrs() []error { | |
171 | if b, ok := r.awsError.(BatchedErrors); ok { | |
172 | return b.OrigErrs() | |
173 | } | |
174 | return []error{r.OrigErr()} | |
175 | } | |
176 | ||
863486a6 AG |
177 | type unmarshalError struct { |
178 | awsError | |
179 | bytes []byte | |
180 | } | |
181 | ||
182 | // Error returns the string representation of the error. | |
183 | // Satisfies the error interface. | |
184 | func (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. | |
191 | func (e unmarshalError) String() string { | |
192 | return e.Error() | |
193 | } | |
194 | ||
195 | // Bytes returns the bytes that failed to unmarshal. | |
196 | func (e unmarshalError) Bytes() []byte { | |
197 | return e.bytes | |
198 | } | |
199 | ||
bae9f6d2 JC |
200 | // An error list that satisfies the golang interface |
201 | type errorList []error | |
202 | ||
203 | // Error returns the string representation of the error. | |
204 | // | |
205 | // Satisfies the error interface. | |
206 | func (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++ { | |
863486a6 | 211 | msg += e[i].Error() |
bae9f6d2 JC |
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 | } |