-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patherrors.go
More file actions
210 lines (195 loc) · 7.12 KB
/
errors.go
File metadata and controls
210 lines (195 loc) · 7.12 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
package arcp
import (
"errors"
"fmt"
)
// ErrorCode is the canonical error code taxonomy defined in RFC §18.2.
// Implementations MUST use these codes when applicable; deployment-
// specific codes MUST be namespaced (e.g. "arcpx.acme.QUOTA_EXCEEDED").
type ErrorCode string
// Canonical error codes (RFC §18.2). The string values are the wire
// representation.
const (
CodeOK ErrorCode = "OK"
CodeCancelled ErrorCode = "CANCELLED"
CodeUnknown ErrorCode = "UNKNOWN"
CodeInvalidArgument ErrorCode = "INVALID_ARGUMENT"
CodeDeadlineExceeded ErrorCode = "DEADLINE_EXCEEDED"
CodeNotFound ErrorCode = "NOT_FOUND"
CodeAlreadyExists ErrorCode = "ALREADY_EXISTS"
CodePermissionDenied ErrorCode = "PERMISSION_DENIED"
CodeResourceExhausted ErrorCode = "RESOURCE_EXHAUSTED"
CodeFailedPrecondition ErrorCode = "FAILED_PRECONDITION"
CodeAborted ErrorCode = "ABORTED"
CodeOutOfRange ErrorCode = "OUT_OF_RANGE"
CodeUnimplemented ErrorCode = "UNIMPLEMENTED"
CodeInternal ErrorCode = "INTERNAL"
CodeUnavailable ErrorCode = "UNAVAILABLE"
CodeDataLoss ErrorCode = "DATA_LOSS"
CodeUnauthenticated ErrorCode = "UNAUTHENTICATED"
CodeHeartbeatLost ErrorCode = "HEARTBEAT_LOST"
CodeLeaseExpired ErrorCode = "LEASE_EXPIRED"
CodeLeaseRevoked ErrorCode = "LEASE_REVOKED"
CodeBackpressureOverflow ErrorCode = "BACKPRESSURE_OVERFLOW"
)
// Error is the structured error type used throughout this
// implementation (RFC §18.1). Wraps a cause via Unwrap so
// errors.Is/As/Unwrap work as expected.
type Error struct {
// Code is the canonical or namespaced error code.
Code ErrorCode
// Message is a human-readable description; optional but recommended.
Message string
// Retryable indicates whether the operation MAY succeed on retry.
// Defaults are filled in by NewError per RFC §18.3.
Retryable bool
// Details carries free-form key/value details, e.g.
// {"retry_after_seconds": 30}.
Details map[string]any
// Cause is the wrapped error (chained per RFC §18.1).
Cause error
}
// Error implements the standard library's error interface.
func (e *Error) Error() string {
if e == nil {
return "<nil arcp.Error>"
}
if e.Cause != nil {
return fmt.Sprintf("arcp: [%s] %s: %v", e.Code, e.Message, e.Cause)
}
return fmt.Sprintf("arcp: [%s] %s", e.Code, e.Message)
}
// Unwrap returns the wrapped cause for compatibility with errors.Unwrap.
func (e *Error) Unwrap() error {
if e == nil {
return nil
}
return e.Cause
}
// Is implements the matching protocol used by errors.Is. Two
// arcp.Errors match when their Codes are equal. Sentinels declared in
// this package compare on Code only; concrete details are inspected via
// errors.As.
func (e *Error) Is(target error) bool {
if e == nil {
return target == nil
}
var t *Error
if !errors.As(target, &t) {
return false
}
return e.Code == t.Code
}
// WithCause returns a copy of e with Cause set.
func (e *Error) WithCause(cause error) *Error {
if e == nil {
return nil
}
c := *e
c.Cause = cause
return &c
}
// WithMessage returns a copy of e with the given message.
func (e *Error) WithMessage(msg string) *Error {
if e == nil {
return nil
}
c := *e
c.Message = msg
return &c
}
// WithDetails returns a copy of e with the given details merged in.
// The original details map (if any) is not mutated.
func (e *Error) WithDetails(details map[string]any) *Error {
if e == nil {
return nil
}
c := *e
merged := make(map[string]any, len(e.Details)+len(details))
for k, v := range e.Details {
merged[k] = v
}
for k, v := range details {
merged[k] = v
}
c.Details = merged
return &c
}
// NewError constructs an Error with the given code and message.
// Retryable is initialized from DefaultRetryable(code).
func NewError(code ErrorCode, msg string) *Error {
return &Error{Code: code, Message: msg, Retryable: DefaultRetryable(code)}
}
// DefaultRetryable returns the default retryability for a code per
// RFC §18.3.
func DefaultRetryable(code ErrorCode) bool {
switch code {
case CodeResourceExhausted, CodeUnavailable, CodeDeadlineExceeded, CodeInternal, CodeAborted:
return true
default:
return false
}
}
// IsRetryable returns true if err (or any wrapped arcp.Error in its
// chain) reports Retryable. Non-arcp errors return false.
func IsRetryable(err error) bool {
var e *Error
if errors.As(err, &e) {
return e.Retryable
}
return false
}
// Code returns the ErrorCode of err if err is or wraps an arcp.Error,
// or CodeUnknown otherwise.
func Code(err error) ErrorCode {
var e *Error
if errors.As(err, &e) {
return e.Code
}
if err == nil {
return CodeOK
}
return CodeUnknown
}
// Sentinel errors. Use errors.Is to compare. Each sentinel is a
// distinct *Error keyed on its canonical Code.
var (
// ErrUnauthenticated indicates missing or invalid credentials
// (RFC §18.2).
ErrUnauthenticated = &Error{Code: CodeUnauthenticated, Message: "unauthenticated"}
// ErrPermissionDenied indicates the caller lacks the required
// permission or lease (RFC §15, §18.2).
ErrPermissionDenied = &Error{Code: CodePermissionDenied, Message: "permission denied"}
// ErrLeaseExpired indicates an operation attempted with an expired
// lease (RFC §15.5).
ErrLeaseExpired = &Error{Code: CodeLeaseExpired, Message: "lease expired"}
// ErrLeaseRevoked indicates an operation attempted with a revoked
// lease (RFC §15.5).
ErrLeaseRevoked = &Error{Code: CodeLeaseRevoked, Message: "lease revoked"}
// ErrUnimplemented indicates the runtime does not support the
// requested feature (RFC §18.2). v0.1 returns this for deferred
// surfaces (mtls, oauth2, sidecar binary, scheduled jobs, etc.).
ErrUnimplemented = &Error{Code: CodeUnimplemented, Message: "not implemented in this runtime"}
// ErrDeadlineExceeded indicates an operation timed out
// (RFC §18.2). Retryable by default.
ErrDeadlineExceeded = &Error{Code: CodeDeadlineExceeded, Message: "deadline exceeded", Retryable: true}
// ErrCancelled indicates the operation was cancelled
// (RFC §10.4, §18.2).
ErrCancelled = &Error{Code: CodeCancelled, Message: "cancelled"}
// ErrNotFound indicates a referenced entity does not exist
// (RFC §18.2).
ErrNotFound = &Error{Code: CodeNotFound, Message: "not found"}
// ErrAlreadyExists indicates an entity creation conflicted
// (RFC §18.2). Used for duplicate envelope ids in the event log.
ErrAlreadyExists = &Error{Code: CodeAlreadyExists, Message: "already exists"}
// ErrInvalidArgument indicates a malformed argument (RFC §18.2).
ErrInvalidArgument = &Error{Code: CodeInvalidArgument, Message: "invalid argument"}
// ErrInternal indicates an internal runtime error (RFC §18.2).
ErrInternal = &Error{Code: CodeInternal, Message: "internal error", Retryable: true}
// ErrBackpressureOverflow indicates a stream or subscription was
// dropped due to overflow (RFC §18.2).
ErrBackpressureOverflow = &Error{Code: CodeBackpressureOverflow, Message: "backpressure overflow"}
// ErrHeartbeatLost indicates a job missed required heartbeats
// (RFC §10.3, §18.2).
ErrHeartbeatLost = &Error{Code: CodeHeartbeatLost, Message: "heartbeat lost"}
)