package response import ( "errors" "fmt" "net/http" "ruben/inventory2/internal/consts" "strings" ) type ( // ErrorResponse is an error ErrorResponse struct { err error msg string status int html []byte } ) // Constructors func Errorf(format string, args ...any) ErrorResponse { return ErrorResponse{ err: fmt.Errorf(format, args...), } } func BadRequest() ErrorResponse { return ErrorResponse{ status: http.StatusBadRequest, } } func NotFound() ErrorResponse { return ErrorResponse{ status: http.StatusNotFound, } } func Unauthorized() ErrorResponse { return ErrorResponse{ status: http.StatusUnauthorized, } } func Forbidden() ErrorResponse { return ErrorResponse{ status: http.StatusForbidden, } } func Conflict() ErrorResponse { return ErrorResponse{ status: http.StatusConflict, } } // builder pattern implementation func (e ErrorResponse) Msg(msg string) ErrorResponse { e.msg = msg return e } func (e ErrorResponse) Msgf(format string, args ...any) ErrorResponse { e.msg = fmt.Sprintf(format, args...) return e } func (e ErrorResponse) Status(status int) ErrorResponse { e.status = status return e } func (e ErrorResponse) Wrap(err error) ErrorResponse { e.err = err return e } func (e ErrorResponse) HTML(h []byte) ErrorResponse { e.html = h return e } // error implementation func (e ErrorResponse) Error() string { parts := make([]string, 0, 3) if e.msg != "" { parts = append(parts, e.msg) } else if e.status != 0 { parts = append(parts, fmt.Sprintf("status = %d", e.status)) } if e.err != nil { parts = append(parts, e.err.Error()) } if len(parts) == 0 { return "status = 500" } return strings.Join(parts, ": ") } func (e ErrorResponse) Unwrap() error { return e.err } // nested response value resolution func (e ErrorResponse) GetStatus() (int, bool) { if e.status != 0 { return e.status, true } ce, ok := GetError(e.err) if ok { return ce.GetStatus() } return 0, false } func (e ErrorResponse) GetMsg() (string, bool) { if e.msg != "" { return e.msg, true } ce, ok := GetError(e.err) if ok { return ce.GetMsg() } return "", false } func (e ErrorResponse) GetHTML() ([]byte, bool) { if len(e.html) != 0 { return e.html, true } ce, ok := GetError(e.err) if ok { return ce.GetHTML() } return nil, false } func GetError(err error) (e ErrorResponse, ok bool) { ok = errors.As(err, &e) return e, ok } // error wrapping utilities func ErrorFromConstant(err error) error { cerr := err for cerr != nil { switch cerr { case consts.ErrNotFound: return NotFound() case consts.ErrConflict: return Conflict() } uerr, ok := cerr.(interface { Unwrap() error }) if !ok { return err } cerr = uerr.Unwrap() } return err }