package errors import ( stdErrors "errors" "net/http" ) // HTTPStatus resolves the HTTP status code for err. // // If err (or anything wrapped by it) is an AppError with a non-zero HTTPCode, // that code is returned; otherwise the fallback is http.StatusInternalServerError // (500). New and Wrap propagate the cause's status, so wrapping a sentinel such // as ErrNotFound keeps status 404. func HTTPStatus(err error) int { var appErr *AppError if stdErrors.As(err, &appErr) && appErr.HTTPCode != 0 { return appErr.HTTPCode } return http.StatusInternalServerError } // ToJSON renders err as an RFC 7807 style error object, aligned with the client // API contract {error:{code,message}}. // // For an AppError (or one wrapped by it) the Code and Message are returned // verbatim. For any other error a generic internal error is returned so that // internal details are never leaked to the user; the underlying error is kept // only in the Cause field for logging. func ToJSON(err error) map[string]any { var appErr *AppError if stdErrors.As(err, &appErr) { return map[string]any{ "error": map[string]any{ "code": appErr.Code, "message": appErr.Message, }, } } return map[string]any{ "error": map[string]any{ "code": ErrInternal.Code, "message": ErrInternal.Message, }, } }