package apierror import ( "errors" "fmt" ) type Error struct { Status int Code string Message string Cause error } func (e *Error) Error() string { if e.Cause != nil { return fmt.Sprintf("%s: %v", e.Message, e.Cause) } return e.Message } func (e *Error) Unwrap() error { return e.Cause } // From returns err as *Error if it (or anything in its chain) is one; // otherwise wraps err in an Internal. func From(err error) *Error { if err == nil { return nil } var apiErr *Error if errors.As(err, &apiErr) { return apiErr } return Internal(err) } func NotFound(what string) *Error { return &Error{Status: 404, Code: what + "_not_found", Message: what + " not found"} } func BadRequest(code, message string) *Error { return &Error{Status: 400, Code: code, Message: message} } func Conflict(code, message string) *Error { return &Error{Status: 409, Code: code, Message: message} } func Forbidden(code, message string) *Error { return &Error{Status: 403, Code: code, Message: message} } func Unauthorized(code, message string) *Error { return &Error{Status: 401, Code: code, Message: message} } func Internal(cause error) *Error { return &Error{Status: 500, Code: "server_error", Message: "internal server error", Cause: cause} } // InternalMsg returns a 500 Error with a custom user-facing message // and a cause for logging. Use when the call site has a meaningful // message that should appear on the wire (e.g. "lookup failed") rather // than the generic "internal server error" from Internal. func InternalMsg(message string, cause error) *Error { return &Error{Status: 500, Code: "server_error", Message: message, Cause: cause} } var ( ErrNotFound = &Error{Status: 404, Code: "not_found", Message: "not found"} ErrForbidden = &Error{Status: 403, Code: "forbidden", Message: "forbidden"} ErrUnauthorized = &Error{Status: 401, Code: "unauthorized", Message: "authentication required"} )