| package errors |
|
|
| import ( |
| "errors" |
| "fmt" |
| "net/http" |
| ) |
|
|
| const ( |
| UnknownCode = http.StatusInternalServerError |
| UnknownReason = "" |
| UnknownMessage = "internal error" |
| ) |
|
|
| type Status struct { |
| Code int32 `json:"code"` |
| Reason string `json:"reason,omitempty"` |
| Message string `json:"message"` |
| Metadata map[string]string `json:"metadata,omitempty"` |
| } |
|
|
| |
| |
| |
| type ApplicationError struct { |
| Status |
| cause error |
| } |
|
|
| |
| type Error = ApplicationError |
|
|
| func (e *ApplicationError) Error() string { |
| if e == nil { |
| return "<nil>" |
| } |
| if e.cause == nil { |
| return fmt.Sprintf("error: code=%d reason=%q message=%q metadata=%v", e.Code, e.Reason, e.Message, e.Metadata) |
| } |
| return fmt.Sprintf("error: code=%d reason=%q message=%q metadata=%v cause=%v", e.Code, e.Reason, e.Message, e.Metadata, e.cause) |
| } |
|
|
| |
| func (e *ApplicationError) Unwrap() error { return e.cause } |
|
|
| |
| func (e *ApplicationError) Is(err error) bool { |
| if se := new(ApplicationError); errors.As(err, &se) { |
| return se.Code == e.Code && se.Reason == e.Reason |
| } |
| return false |
| } |
|
|
| |
| func (e *ApplicationError) WithCause(cause error) *ApplicationError { |
| err := Clone(e) |
| err.cause = cause |
| return err |
| } |
|
|
| |
| func (e *ApplicationError) WithMetadata(md map[string]string) *ApplicationError { |
| err := Clone(e) |
| if md == nil { |
| err.Metadata = nil |
| return err |
| } |
| err.Metadata = make(map[string]string, len(md)) |
| for k, v := range md { |
| err.Metadata[k] = v |
| } |
| return err |
| } |
|
|
| |
| func New(code int, reason, message string) *ApplicationError { |
| return &ApplicationError{ |
| Status: Status{ |
| Code: int32(code), |
| Message: message, |
| Reason: reason, |
| }, |
| } |
| } |
|
|
| |
| func Newf(code int, reason, format string, a ...any) *ApplicationError { |
| return New(code, reason, fmt.Sprintf(format, a...)) |
| } |
|
|
| |
| func Errorf(code int, reason, format string, a ...any) error { |
| return New(code, reason, fmt.Sprintf(format, a...)) |
| } |
|
|
| |
| |
| func Code(err error) int { |
| if err == nil { |
| return http.StatusOK |
| } |
| return int(FromError(err).Code) |
| } |
|
|
| |
| |
| func Reason(err error) string { |
| if err == nil { |
| return UnknownReason |
| } |
| return FromError(err).Reason |
| } |
|
|
| |
| |
| func Message(err error) string { |
| if err == nil { |
| return "" |
| } |
| return FromError(err).Message |
| } |
|
|
| |
| func Clone(err *ApplicationError) *ApplicationError { |
| if err == nil { |
| return nil |
| } |
| var metadata map[string]string |
| if err.Metadata != nil { |
| metadata = make(map[string]string, len(err.Metadata)) |
| for k, v := range err.Metadata { |
| metadata[k] = v |
| } |
| } |
| return &ApplicationError{ |
| cause: err.cause, |
| Status: Status{ |
| Code: err.Code, |
| Reason: err.Reason, |
| Message: err.Message, |
| Metadata: metadata, |
| }, |
| } |
| } |
|
|
| |
| |
| func FromError(err error) *ApplicationError { |
| if err == nil { |
| return nil |
| } |
| if se := new(ApplicationError); errors.As(err, &se) { |
| return se |
| } |
|
|
| |
| return New(UnknownCode, UnknownReason, UnknownMessage).WithCause(err) |
| } |
|
|