Files
inventory-plus-plus/server/response/error.go
T

187 lines
2.8 KiB
Go

package response
import (
"errors"
"fmt"
"net/http"
"strings"
"ruben/inventory2/consts"
)
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) {
if ok = errors.As(err, &e); ok {
return e, true
}
var ptr *ErrorResponse
if ok = errors.As(err, &ptr); ok {
return *ptr, true
}
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
}