-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patherror.go
More file actions
143 lines (127 loc) · 4.27 KB
/
Copy patherror.go
File metadata and controls
143 lines (127 loc) · 4.27 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
package mapbox
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
)
// Code is a Mapbox API response code. It appears in two places:
// - HTTP 200 Map Matching response bodies (e.g. [CodeOK], [CodeNoMatch])
// - HTTP 4xx error response bodies (e.g. [CodeInvalidInput], [CodeTooManyCoordinates])
//
// Use [CodeOf] to extract the code from any error.
type Code string
const (
// CodeUnknown is the zero value, returned by [CodeOf] when the error has no code.
CodeUnknown Code = ""
// CodeOK indicates a successful Map Matching result.
CodeOK Code = "Ok"
// CodeNoMatch indicates no route matched the input trace.
CodeNoMatch Code = "NoMatch"
// CodeNoSegment indicates no road was found within the provided radiuses.
CodeNoSegment Code = "NoSegment"
// CodeTooManyCoordinates indicates more than 100 coordinates were provided.
CodeTooManyCoordinates Code = "TooManyCoordinates"
// CodeInvalidInput indicates a malformed request.
CodeInvalidInput Code = "InvalidInput"
// CodeProfileNotFound indicates an invalid routing profile was specified.
CodeProfileNotFound Code = "ProfileNotFound"
)
// IsSuccess reports whether the code represents a successful Map Matching result.
func (c Code) IsSuccess() bool { return c == CodeOK }
// Error represents an HTTP-level error response from the Mapbox API.
// Use [errors.As] to unwrap, or [CodeOf] to extract the response code.
type Error struct {
// StatusCode is the HTTP status code.
StatusCode int
// Status is the HTTP status text (e.g. "422 Unprocessable Entity").
Status string
// Message is the human-readable error message parsed from the Mapbox JSON
// response body (the "message" field). Empty if the body was not valid JSON
// or did not contain a message field.
Message string
// Body is the raw response body, kept for debugging.
Body string
code Code
}
// Code returns the Mapbox API response code parsed from the error body, or
// [CodeUnknown] if the body did not contain a code field.
func (e *Error) Code() Code { return e.code }
func (e *Error) Error() string {
if e.Message != "" {
return fmt.Sprintf("mapbox: http %d: %s", e.StatusCode, e.Message)
}
if e.Body != "" {
return fmt.Sprintf("mapbox: http %d: %s", e.StatusCode, e.Body)
}
return fmt.Sprintf("mapbox: http %d", e.StatusCode)
}
// CodeOf returns the Mapbox API response code from err, or [CodeUnknown] if err
// is nil, is not a Mapbox error, or the error body contained no code field.
//
// Use this to switch on specific error codes:
//
// switch mapbox.CodeOf(err) {
// case mapbox.CodeTooManyCoordinates:
// // split and retry
// case mapbox.CodeInvalidInput:
// // log and discard
// }
func CodeOf(err error) Code {
var e *Error
if errors.As(err, &e) {
return e.code
}
return CodeUnknown
}
// IsNotFound reports whether err is a 404 Mapbox API error.
func IsNotFound(err error) bool {
var e *Error
return errors.As(err, &e) && e.StatusCode == http.StatusNotFound
}
// IsUnauthorized reports whether err is a 401 Mapbox API error.
func IsUnauthorized(err error) bool {
var e *Error
return errors.As(err, &e) && e.StatusCode == http.StatusUnauthorized
}
// IsForbidden reports whether err is a 403 Mapbox API error.
func IsForbidden(err error) bool {
var e *Error
return errors.As(err, &e) && e.StatusCode == http.StatusForbidden
}
// IsRateLimited reports whether err is a 429 Mapbox API error.
func IsRateLimited(err error) bool {
var e *Error
return errors.As(err, &e) && e.StatusCode == http.StatusTooManyRequests
}
// IsInvalidInput reports whether err is a 422 Mapbox API error.
func IsInvalidInput(err error) bool {
var e *Error
return errors.As(err, &e) && e.StatusCode == http.StatusUnprocessableEntity
}
// IsServerError reports whether err is a 5xx Mapbox API error.
func IsServerError(err error) bool {
var e *Error
return errors.As(err, &e) && e.StatusCode >= 500
}
func newResponseError(resp *http.Response) error {
body, err := io.ReadAll(resp.Body)
if err != nil {
body = fmt.Appendf(nil, "failed to read response body: %s", err)
}
e := &Error{
StatusCode: resp.StatusCode,
Status: resp.Status,
Body: string(body),
}
var parsed struct {
Code Code `json:"code"`
Message string `json:"message"`
}
if json.Unmarshal(body, &parsed) == nil {
e.code = parsed.Code
e.Message = parsed.Message
}
return e
}