Repository navigation
Expand file tree
/
Copy patherrors.go
More file actions
231 lines (202 loc) · 6.98 KB
/
Copy patherrors.go
File metadata and controls
231 lines (202 loc) · 6.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
package typesafe
import (
"context"
"errors"
"fmt"
"math"
"net/http"
"strconv"
"strings"
"time"
)
// Error is the base type for SDK failures that do not carry an HTTP response,
// such as invalid configuration and request validation errors.
type Error struct {
// Message describes the failure.
Message string
// Err is an optional underlying cause.
Err error
}
// Error implements the error interface.
func (e *Error) Error() string {
if e.Err == nil {
return e.Message
}
if e.Message == "" {
return e.Err.Error()
}
return e.Message + ": " + e.Err.Error()
}
// Unwrap returns the underlying cause so errors.Is and errors.As can inspect it.
func (e *Error) Unwrap() error { return e.Err }
const maxErrorBodyLength = 200
// APIError reports a non-2xx HTTP response from the API.
type APIError struct {
// StatusCode is the HTTP response status code.
StatusCode int
// Headers holds the full response headers.
Headers http.Header
// Body is the parsed response body: a decoded JSON value, a raw string, or nil
// for an empty body.
Body any
// RawBody is the response body exactly as received.
RawBody []byte
// RequestID is the x-typesafe-request-id response header, empty when absent.
RequestID string
// Message describes the failure: the server's message when one was provided,
// otherwise the status code description.
Message string
// Endpoint is the request method and URL that produced the error.
Endpoint string
}
// Error implements the error interface.
func (e *APIError) Error() string {
message := fmt.Sprintf("%d %s", e.StatusCode, e.Message)
if e.Endpoint != "" {
message = e.Endpoint + ": " + message
}
if e.RequestID != "" {
message += " (request_id=" + e.RequestID + ")"
}
return message
}
// IsBadRequest reports whether the response status is 400.
func (e *APIError) IsBadRequest() bool { return e.StatusCode == http.StatusBadRequest }
// IsAuthentication reports whether the response status is 401.
func (e *APIError) IsAuthentication() bool { return e.StatusCode == http.StatusUnauthorized }
// IsPermissionDenied reports whether the response status is 403.
func (e *APIError) IsPermissionDenied() bool { return e.StatusCode == http.StatusForbidden }
// IsNotFound reports whether the response status is 404.
func (e *APIError) IsNotFound() bool { return e.StatusCode == http.StatusNotFound }
// IsUnprocessableEntity reports whether the response status is 422.
func (e *APIError) IsUnprocessableEntity() bool {
return e.StatusCode == http.StatusUnprocessableEntity
}
// IsRateLimit reports whether the response status is 429.
func (e *APIError) IsRateLimit() bool { return e.StatusCode == http.StatusTooManyRequests }
// IsServerError reports whether the response status is 5xx.
func (e *APIError) IsServerError() bool { return e.StatusCode >= 500 }
// RetryAfter returns the wait requested by the server through the retry-after-ms or
// Retry-After response header. It returns 0 when no valid delay is present.
func (e *APIError) RetryAfter() time.Duration { return ParseRetryAfter(e.Headers) }
// APIConnectionError reports a request that failed before receiving an HTTP
// response, such as a DNS, TLS, or connection failure.
type APIConnectionError struct {
// Endpoint is the request method and URL that failed.
Endpoint string
// Err is the underlying transport error.
Err error
}
// Error implements the error interface.
func (e *APIConnectionError) Error() string {
message := "connection error"
if e.Endpoint != "" {
message = e.Endpoint + ": " + message
}
if e.Err != nil {
message += ": " + e.Err.Error()
}
return message
}
// Unwrap returns the underlying transport error.
func (e *APIConnectionError) Unwrap() error { return e.Err }
// APITimeoutError reports a request that exceeded its configured timeout.
// It unwraps to context.DeadlineExceeded.
type APITimeoutError struct {
// Endpoint is the request method and URL that timed out.
Endpoint string
// Timeout is the timeout that was in effect for the attempt.
Timeout time.Duration
// Err is the underlying error.
Err error
}
// Error implements the error interface.
func (e *APITimeoutError) Error() string {
message := fmt.Sprintf("request timed out after %s", e.Timeout)
if e.Endpoint != "" {
message = e.Endpoint + ": " + message
}
return message
}
// Unwrap returns the underlying error, which is context.DeadlineExceeded.
func (e *APITimeoutError) Unwrap() error { return e.Err }
// UserAbortError reports that the caller cancelled the request through its context.
// It unwraps to context.Canceled.
type UserAbortError struct {
// Endpoint is the request method and URL that was cancelled.
Endpoint string
// Err is the context error that caused the abort.
Err error
}
// Error implements the error interface.
func (e *UserAbortError) Error() string {
message := "request was aborted"
if e.Endpoint != "" {
message = e.Endpoint + ": " + message
}
if e.Err != nil && !errors.Is(e.Err, context.Canceled) {
message += ": " + e.Err.Error()
}
return message
}
// Unwrap returns the context error that caused the abort.
func (e *UserAbortError) Unwrap() error { return e.Err }
// APIResponseError reports a 2xx response whose body could not be decoded into
// the expected structure, for example a missing required field.
type APIResponseError struct {
// StatusCode is the HTTP response status code.
StatusCode int
// Endpoint is the request method and URL.
Endpoint string
// RawBody is the response body exactly as received.
RawBody []byte
// Err describes the decoding failure.
Err error
}
// Error implements the error interface.
func (e *APIResponseError) Error() string {
message := "invalid response data"
if e.Endpoint != "" {
message = e.Endpoint + ": " + message
}
if e.Err != nil {
message += ": " + e.Err.Error()
}
return message
}
// Unwrap returns the decoding failure.
func (e *APIResponseError) Unwrap() error { return e.Err }
// ParseRetryAfter returns the wait requested by the retry-after-ms or Retry-After
// response header. retry-after-ms takes precedence; Retry-After accepts either a
// number of seconds or an HTTP date. It returns 0 when no valid delay is present.
func ParseRetryAfter(header http.Header) time.Duration {
if header == nil {
return 0
}
if raw := strings.TrimSpace(header.Get("retry-after-ms")); raw != "" {
// A negative or unparseable value falls through to Retry-After.
if ms, err := strconv.ParseFloat(raw, 64); err == nil && isFinite(ms) && ms >= 0 {
return time.Duration(ms * float64(time.Millisecond))
}
}
raw := strings.TrimSpace(header.Get("Retry-After"))
if raw == "" {
return 0
}
if seconds, err := strconv.ParseFloat(raw, 64); err == nil {
if seconds >= 0 && isFinite(seconds) {
return time.Duration(seconds * float64(time.Second))
}
return 0
}
if when, err := http.ParseTime(raw); err == nil {
if delay := time.Until(when); delay > 0 {
return delay
}
}
return 0
}
// isFinite reports whether a float is neither infinite nor NaN.
func isFinite(value float64) bool {
return !math.IsInf(value, 0) && !math.IsNaN(value)
}