go.uber.org/yarpc@v1.72.1/yarpcerrors/errors.go (about) 1 // Copyright (c) 2022 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package yarpcerrors 22 23 import ( 24 "bytes" 25 "errors" 26 "fmt" 27 ) 28 29 // Newf returns a new Status. 30 // 31 // The Code should never be CodeOK, if it is, this will return nil. 32 func Newf(code Code, format string, args ...interface{}) *Status { 33 if code == CodeOK { 34 return nil 35 } 36 37 var err error 38 if len(args) == 0 { 39 err = errors.New(format) 40 } else { 41 err = fmt.Errorf(format, args...) 42 } 43 44 return &Status{ 45 code: code, 46 err: err, 47 } 48 } 49 50 type yarpcError interface{ YARPCError() *Status } 51 52 // FromError returns the Status for the provided error. 53 // 54 // If the error: 55 // - is nil, return nil 56 // - is a 'Status', return the 'Status' 57 // - has a 'YARPCError() *Status' method, returns the 'Status' 58 // Otherwise, return a wrapped error with code 'CodeUnknown'. 59 func FromError(err error) *Status { 60 if err == nil { 61 return nil 62 } 63 64 if st, ok := fromError(err); ok { 65 return st 66 } 67 68 // Extra wrapping ensures Unwrap works consistently across *Status created 69 // by FromError and Newf. 70 // https://github.com/yarpc/yarpc-go/pull/1966 71 return &Status{ 72 code: CodeUnknown, 73 err: &wrapError{err: err}, 74 } 75 } 76 77 func fromError(err error) (st *Status, ok bool) { 78 if errors.As(err, &st) { 79 return st, true 80 } 81 82 var yerr yarpcError 83 if errors.As(err, &yerr) { 84 return yerr.YARPCError(), true 85 } 86 return nil, false 87 } 88 89 // Unwrap supports errors.Unwrap. 90 // 91 // See "errors" package documentation for details. 92 func (s *Status) Unwrap() error { 93 if s == nil { 94 return nil 95 } 96 return errors.Unwrap(s.err) 97 } 98 99 // IsStatus returns whether the provided error is a YARPC error, or has a 100 // YARPCError() function to represent the error as a YARPC error. This includes 101 // wrapped errors. 102 // 103 // This is false if the error is nil. 104 func IsStatus(err error) bool { 105 _, ok := fromError(err) 106 return ok 107 } 108 109 // Status represents a YARPC error. 110 type Status struct { 111 code Code 112 name string 113 err error 114 details []byte 115 } 116 117 // WithName returns a new Status with the given name. 118 // 119 // This should be used for user-defined errors. 120 // 121 // Deprecated: Use only error codes to represent the type of the error. 122 func (s *Status) WithName(name string) *Status { 123 // TODO: We plan to add a WithDetails method to add semantic metadata to 124 // Statuses soon. 125 if s == nil { 126 return nil 127 } 128 129 return &Status{ 130 code: s.code, 131 name: name, 132 err: s.err, 133 details: s.details, 134 } 135 } 136 137 // WithDetails returns a new status with the given details bytes. 138 func (s *Status) WithDetails(details []byte) *Status { 139 if s == nil { 140 return nil 141 } 142 if len(details) == 0 { 143 // this ensures that the details field is not set to some pointer if 144 // there's nothing in details. 145 details = nil 146 } 147 return &Status{ 148 code: s.code, 149 name: s.name, 150 err: s.err, 151 details: details, 152 } 153 } 154 155 // Code returns the error code for this Status. 156 func (s *Status) Code() Code { 157 if s == nil { 158 return CodeOK 159 } 160 return s.code 161 } 162 163 // Name returns the name of the error for this Status. 164 // 165 // This is an empty string for all built-in YARPC errors. It may be customized 166 // by using WithName. 167 func (s *Status) Name() string { 168 if s == nil { 169 return "" 170 } 171 return s.name 172 } 173 174 // Message returns the error message for this Status. 175 func (s *Status) Message() string { 176 if s == nil { 177 return "" 178 } 179 return s.err.Error() 180 } 181 182 // Details returns the error details for this Status. 183 func (s *Status) Details() []byte { 184 if s == nil { 185 return nil 186 } 187 return s.details 188 } 189 190 // Error implements the error interface. 191 func (s *Status) Error() string { 192 buffer := bytes.NewBuffer(nil) 193 _, _ = buffer.WriteString(`code:`) 194 _, _ = buffer.WriteString(s.code.String()) 195 if s.name != "" { 196 _, _ = buffer.WriteString(` name:`) 197 _, _ = buffer.WriteString(s.name) 198 } 199 if s.err != nil && s.err.Error() != "" { 200 _, _ = buffer.WriteString(` message:`) 201 _, _ = buffer.WriteString(s.err.Error()) 202 } 203 return buffer.String() 204 } 205 206 // wrapError does what it says on the tin. 207 type wrapError struct { 208 err error 209 } 210 211 // Error returns the inner error message. 212 func (e *wrapError) Error() string { 213 if e == nil || e.err == nil { 214 return "" 215 } 216 return e.err.Error() 217 } 218 219 // Unwrap returns the inner error. 220 func (e *wrapError) Unwrap() error { 221 if e == nil { 222 return nil 223 } 224 return e.err 225 } 226 227 // CancelledErrorf returns a new Status with code CodeCancelled 228 // by calling Newf(CodeCancelled, format, args...). 229 func CancelledErrorf(format string, args ...interface{}) error { 230 return Newf(CodeCancelled, format, args...) 231 } 232 233 // UnknownErrorf returns a new Status with code CodeUnknown 234 // by calling Newf(CodeUnknown, format, args...). 235 func UnknownErrorf(format string, args ...interface{}) error { 236 return Newf(CodeUnknown, format, args...) 237 } 238 239 // InvalidArgumentErrorf returns a new Status with code CodeInvalidArgument 240 // by calling Newf(CodeInvalidArgument, format, args...). 241 func InvalidArgumentErrorf(format string, args ...interface{}) error { 242 return Newf(CodeInvalidArgument, format, args...) 243 } 244 245 // DeadlineExceededErrorf returns a new Status with code CodeDeadlineExceeded 246 // by calling Newf(CodeDeadlineExceeded, format, args...). 247 func DeadlineExceededErrorf(format string, args ...interface{}) error { 248 return Newf(CodeDeadlineExceeded, format, args...) 249 } 250 251 // NotFoundErrorf returns a new Status with code CodeNotFound 252 // by calling Newf(CodeNotFound, format, args...). 253 func NotFoundErrorf(format string, args ...interface{}) error { 254 return Newf(CodeNotFound, format, args...) 255 } 256 257 // AlreadyExistsErrorf returns a new Status with code CodeAlreadyExists 258 // by calling Newf(CodeAlreadyExists, format, args...). 259 func AlreadyExistsErrorf(format string, args ...interface{}) error { 260 return Newf(CodeAlreadyExists, format, args...) 261 } 262 263 // PermissionDeniedErrorf returns a new Status with code CodePermissionDenied 264 // by calling Newf(CodePermissionDenied, format, args...). 265 func PermissionDeniedErrorf(format string, args ...interface{}) error { 266 return Newf(CodePermissionDenied, format, args...) 267 } 268 269 // ResourceExhaustedErrorf returns a new Status with code CodeResourceExhausted 270 // by calling Newf(CodeResourceExhausted, format, args...). 271 func ResourceExhaustedErrorf(format string, args ...interface{}) error { 272 return Newf(CodeResourceExhausted, format, args...) 273 } 274 275 // FailedPreconditionErrorf returns a new Status with code CodeFailedPrecondition 276 // by calling Newf(CodeFailedPrecondition, format, args...). 277 func FailedPreconditionErrorf(format string, args ...interface{}) error { 278 return Newf(CodeFailedPrecondition, format, args...) 279 } 280 281 // AbortedErrorf returns a new Status with code CodeAborted 282 // by calling Newf(CodeAborted, format, args...). 283 func AbortedErrorf(format string, args ...interface{}) error { 284 return Newf(CodeAborted, format, args...) 285 } 286 287 // OutOfRangeErrorf returns a new Status with code CodeOutOfRange 288 // by calling Newf(CodeOutOfRange, format, args...). 289 func OutOfRangeErrorf(format string, args ...interface{}) error { 290 return Newf(CodeOutOfRange, format, args...) 291 } 292 293 // UnimplementedErrorf returns a new Status with code CodeUnimplemented 294 // by calling Newf(CodeUnimplemented, format, args...). 295 func UnimplementedErrorf(format string, args ...interface{}) error { 296 return Newf(CodeUnimplemented, format, args...) 297 } 298 299 // InternalErrorf returns a new Status with code CodeInternal 300 // by calling Newf(CodeInternal, format, args...). 301 func InternalErrorf(format string, args ...interface{}) error { 302 return Newf(CodeInternal, format, args...) 303 } 304 305 // UnavailableErrorf returns a new Status with code CodeUnavailable 306 // by calling Newf(CodeUnavailable, format, args...). 307 func UnavailableErrorf(format string, args ...interface{}) error { 308 return Newf(CodeUnavailable, format, args...) 309 } 310 311 // DataLossErrorf returns a new Status with code CodeDataLoss 312 // by calling Newf(CodeDataLoss, format, args...). 313 func DataLossErrorf(format string, args ...interface{}) error { 314 return Newf(CodeDataLoss, format, args...) 315 } 316 317 // UnauthenticatedErrorf returns a new Status with code CodeUnauthenticated 318 // by calling Newf(CodeUnauthenticated, format, args...). 319 func UnauthenticatedErrorf(format string, args ...interface{}) error { 320 return Newf(CodeUnauthenticated, format, args...) 321 } 322 323 // IsCancelled returns true if FromError(err).Code() == CodeCancelled. 324 func IsCancelled(err error) bool { 325 return FromError(err).Code() == CodeCancelled 326 } 327 328 // IsUnknown returns true if FromError(err).Code() == CodeUnknown. 329 func IsUnknown(err error) bool { 330 return FromError(err).Code() == CodeUnknown 331 } 332 333 // IsInvalidArgument returns true if FromError(err).Code() == CodeInvalidArgument. 334 func IsInvalidArgument(err error) bool { 335 return FromError(err).Code() == CodeInvalidArgument 336 } 337 338 // IsDeadlineExceeded returns true if FromError(err).Code() == CodeDeadlineExceeded. 339 func IsDeadlineExceeded(err error) bool { 340 return FromError(err).Code() == CodeDeadlineExceeded 341 } 342 343 // IsNotFound returns true if FromError(err).Code() == CodeNotFound. 344 func IsNotFound(err error) bool { 345 return FromError(err).Code() == CodeNotFound 346 } 347 348 // IsAlreadyExists returns true if FromError(err).Code() == CodeAlreadyExists. 349 func IsAlreadyExists(err error) bool { 350 return FromError(err).Code() == CodeAlreadyExists 351 } 352 353 // IsPermissionDenied returns true if FromError(err).Code() == CodePermissionDenied. 354 func IsPermissionDenied(err error) bool { 355 return FromError(err).Code() == CodePermissionDenied 356 } 357 358 // IsResourceExhausted returns true if FromError(err).Code() == CodeResourceExhausted. 359 func IsResourceExhausted(err error) bool { 360 return FromError(err).Code() == CodeResourceExhausted 361 } 362 363 // IsFailedPrecondition returns true if FromError(err).Code() == CodeFailedPrecondition. 364 func IsFailedPrecondition(err error) bool { 365 return FromError(err).Code() == CodeFailedPrecondition 366 } 367 368 // IsAborted returns true if FromError(err).Code() == CodeAborted. 369 func IsAborted(err error) bool { 370 return FromError(err).Code() == CodeAborted 371 } 372 373 // IsOutOfRange returns true if FromError(err).Code() == CodeOutOfRange. 374 func IsOutOfRange(err error) bool { 375 return FromError(err).Code() == CodeOutOfRange 376 } 377 378 // IsUnimplemented returns true if FromError(err).Code() == CodeUnimplemented. 379 func IsUnimplemented(err error) bool { 380 return FromError(err).Code() == CodeUnimplemented 381 } 382 383 // IsInternal returns true if FromError(err).Code() == CodeInternal. 384 func IsInternal(err error) bool { 385 return FromError(err).Code() == CodeInternal 386 } 387 388 // IsUnavailable returns true if FromError(err).Code() == CodeUnavailable. 389 func IsUnavailable(err error) bool { 390 return FromError(err).Code() == CodeUnavailable 391 } 392 393 // IsDataLoss returns true if FromError(err).Code() == CodeDataLoss. 394 func IsDataLoss(err error) bool { 395 return FromError(err).Code() == CodeDataLoss 396 } 397 398 // IsUnauthenticated returns true if FromError(err).Code() == CodeUnauthenticated. 399 func IsUnauthenticated(err error) bool { 400 return FromError(err).Code() == CodeUnauthenticated 401 } 402 403 // IsYARPCError returns whether the provided error is a YARPC error. 404 // 405 // This is always false if the error is nil. 406 // 407 // Deprecated: use IsStatus instead. 408 func IsYARPCError(err error) bool { 409 return IsStatus(err) 410 } 411 412 // ErrorCode returns the Code for the given error, CodeOK if the error is nil, 413 // or CodeUnknown if the given error is not a YARPC error. 414 // 415 // Deprecated: Use FromError and Code instead. 416 func ErrorCode(err error) Code { 417 return FromError(err).Code() 418 } 419 420 // ErrorName returns the name for the given error, or "" if the given 421 // error is not a YARPC error created with NamedErrorf that has a non-empty name. 422 // 423 // Deprecated: Use FromError and Name instead. 424 func ErrorName(err error) string { 425 return FromError(err).Name() 426 } 427 428 // ErrorMessage returns the message for the given error, or "" if the given 429 // error is nil, or err.Error() if the given error is not a YARPC error or 430 // the YARPC error had no message. 431 // 432 // Deprecated: Use FromError and Message instead. 433 func ErrorMessage(err error) string { 434 return FromError(err).Message() 435 } 436 437 // NamedErrorf returns a new Status with code CodeUnknown and the given name. 438 // 439 // This should be used for user-defined errors. 440 // 441 // The name must only contain lowercase letters from a-z and dashes (-), and 442 // cannot start or end in a dash. If the name is something else, an error with 443 // code CodeInternal will be returned. 444 // 445 // Deprecated: Use Newf and WithName instead. 446 func NamedErrorf(name string, format string, args ...interface{}) error { 447 return Newf(CodeUnknown, format, args...).WithName(name) 448 } 449 450 // FromHeaders returns a new Status from headers transmitted from the server side. 451 // 452 // If the specified code is CodeOK, this will return nil. 453 // 454 // The name must only contain lowercase letters from a-z and dashes (-), and 455 // cannot start or end in a dash. If the name is something else, an error with 456 // code CodeInternal will be returned. 457 // 458 // This function should not be used by server implementations, use the individual 459 // error constructors instead. This should only be used by transport implementations. 460 // 461 // Deprecated: Use Newf and WithName instead. 462 func FromHeaders(code Code, name string, message string) error { 463 return Newf(code, message).WithName(name) 464 }