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 }