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  }