github.com/jxskiss/gopkg@v0.17.3/errcode/code.go (about)

     1  package errcode
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  )
     7  
     8  // ErrCode is the interface implemented by an error code.
     9  type ErrCode interface {
    10  
    11  	// Error returns the error message, it implements the error interface.
    12  	Error() string
    13  
    14  	// Code returns the integer error code.
    15  	Code() int32
    16  
    17  	// Message returns the registered message for the error code.
    18  	// If message is not available, it returns an empty string "".
    19  	Message() string
    20  
    21  	// Details returns the error details attached to the Code.
    22  	// It may return nil if no details is attached.
    23  	Details() []interface{}
    24  }
    25  
    26  // Code represents an error code. It can be created by calling
    27  // Registry.Register or Registry.RegisterReserved.
    28  // Code implements the ErrCode interface.
    29  type Code struct {
    30  	code    int32
    31  	msg     string
    32  	details []interface{}
    33  	reg     *Registry
    34  }
    35  
    36  func (e *Code) String() string { return e.Error() }
    37  
    38  // Error returns the error message, it implements the error interface.
    39  // If message is not registered for the error code, it uses
    40  // "(no message)" as a default message.
    41  func (e *Code) Error() string {
    42  	code := e.Code()
    43  	msg := e.Message()
    44  	if msg == "" {
    45  		msg = "(no message)"
    46  	}
    47  	return fmt.Sprintf("[%d] %s", code, msg)
    48  }
    49  
    50  // Code returns the integer error code.
    51  func (e *Code) Code() int32 { return e.code }
    52  
    53  // Message returns the error message associated with the error code.
    54  // If message is not available, it returns an empty string "".
    55  func (e *Code) Message() string {
    56  	if e.msg != "" {
    57  		return e.msg
    58  	}
    59  	return e.reg.getMessage(e.code)
    60  }
    61  
    62  func (e *Code) clone() *Code {
    63  	detailsLen := len(e.details)
    64  	return &Code{
    65  		code:    e.code,
    66  		msg:     e.msg,
    67  		details: e.details[:detailsLen:detailsLen],
    68  		reg:     e.reg,
    69  	}
    70  }
    71  
    72  // Details returns the error details attached to the Code.
    73  // It may return nil if no details is attached.
    74  func (e *Code) Details() []interface{} { return e.details }
    75  
    76  // WithDetails returns a copy of Code with new error details attached.
    77  func (e *Code) WithDetails(details ...interface{}) (code *Code) {
    78  	code = e.clone()
    79  	code.details = append(code.details, details...)
    80  	return
    81  }
    82  
    83  // RemoveDetails returns a copy of Code without the error details.
    84  // If the Code does not have error details, it returns the Code
    85  // directly instead of a copy.
    86  // When returning an error code to end-users, you may want to remove
    87  // the error details which generally should not be exposed to them.
    88  func (e *Code) RemoveDetails() (code *Code) {
    89  	if len(e.details) == 0 {
    90  		return e
    91  	}
    92  	return &Code{code: e.code, msg: e.msg, reg: e.reg}
    93  }
    94  
    95  // WithMessage returns a copy of Code with the given message.
    96  // If error details are given, the new error details will be attached
    97  // to the returned Code.
    98  func (e *Code) WithMessage(msg string, details ...interface{}) (code *Code) {
    99  	code = e.clone()
   100  	code.msg = msg
   101  	if len(details) > 0 {
   102  		code.details = append(code.details, details...)
   103  	}
   104  	return
   105  }
   106  
   107  type jsonCode struct {
   108  	Code    int32         `json:"code"`
   109  	Message string        `json:"message,omitempty"`
   110  	Details []interface{} `json:"details,omitempty"`
   111  }
   112  
   113  // MarshalJSON implements json.Marshaler.
   114  func (e *Code) MarshalJSON() ([]byte, error) {
   115  	out := &jsonCode{
   116  		Code:    e.Code(),
   117  		Message: e.Message(),
   118  		Details: e.details,
   119  	}
   120  	return json.Marshal(out)
   121  }
   122  
   123  // UnmarshalJSON implements json.Unmarshaler.
   124  func (e *Code) UnmarshalJSON(data []byte) error {
   125  	tmp := &jsonCode{}
   126  	err := json.Unmarshal(data, tmp)
   127  	if err != nil {
   128  		return err
   129  	}
   130  	e.code = tmp.Code
   131  	e.msg = tmp.Message
   132  	e.details = tmp.Details
   133  	return nil
   134  }
   135  
   136  // Is reports whether any error in err's chain matches the target ErrCode.
   137  func Is(err error, target ErrCode) bool {
   138  	errCode := unwrapErrCode(err)
   139  	return errCode != nil && errCode.Code() == target.Code()
   140  }
   141  
   142  // IsErrCode reports whether any error in err's chain is an ErrCode.
   143  func IsErrCode(err error) bool {
   144  	if errCode := unwrapErrCode(err); errCode != nil {
   145  		return true
   146  	}
   147  	return false
   148  }
   149  
   150  func unwrapErrCode(err error) ErrCode {
   151  	if err == nil {
   152  		return nil
   153  	}
   154  
   155  	type causer interface {
   156  		Cause() error
   157  	}
   158  	type wrapper interface {
   159  		Unwrap() error
   160  	}
   161  
   162  	for _err := err; _err != nil; {
   163  		if code, ok := _err.(ErrCode); ok && code != nil {
   164  			return code
   165  		}
   166  		wrapped, ok := _err.(causer)
   167  		if !ok {
   168  			break
   169  		}
   170  		_err = wrapped.Cause()
   171  	}
   172  	for _err := err; err != nil; {
   173  		if code, ok := _err.(ErrCode); ok && code != nil {
   174  			return code
   175  		}
   176  		wrapped, ok := _err.(wrapper)
   177  		if !ok {
   178  			break
   179  		}
   180  		_err = wrapped.Unwrap()
   181  	}
   182  	return nil
   183  }