func (e UnauthorizedError) Unwrap() error {
	return e.Err
}
Unwrap method allows for unwrapping errors using errors.As
package errs
import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"github.com/rs/zerolog"
)
type ErrResponse struct {
	Error ServiceError `json:"error"`
}
type ServiceError struct {
	Kind    string `json:"kind,omitempty"`
	Code    string `json:"code,omitempty"`
	Param   string `json:"param,omitempty"`
	Message string `json:"message,omitempty"`
}
func HTTPErrorResponse(w http.ResponseWriter, lgr zerolog.Logger, err error) {
	if err == nil {
		nilErrorResponse(w, lgr)
		return
	}
	var unauthenticatedErr *UnauthenticatedError
	if errors.As(err, &unauthenticatedErr) {
		unauthenticatedErrorResponse(w, lgr, unauthenticatedErr)
		return
	}
	var unauthorizedErr *UnauthorizedError
	if errors.As(err, &unauthorizedErr) {
		unauthorizedErrorResponse(w, lgr, unauthorizedErr)
		return
	}
	var typicalErr *Error
	if errors.As(err, &typicalErr) {
		typicalErrorResponse(w, lgr, typicalErr)
		return
	}
	otherErrorResponse(w, lgr, err)
}
func typicalErrorResponse(w http.ResponseWriter, lgr zerolog.Logger, e *Error) {
	httpStatusCode := httpErrorStatusCode(e.Kind)
	if e.isZero() {
		lgr.Error().Stack().Int("http_statuscode", httpStatusCode).Msg("empty error")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	lgr.Error().Stack().Err(e.Err).
		Int("http_statuscode", httpStatusCode).
		Str("Kind", e.Kind.String()).
		Str("Parameter", string(e.Param)).
		Str("Code", string(e.Code)).
		Msg("Error Response Sent")
	er := newErrResponse(e)
	errJSON, _ := json.Marshal(er)
	ejson := string(errJSON)
	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("X-Content-Type-Options", "nosniff")
	w.WriteHeader(httpStatusCode)
	fmt.Fprintln(w, ejson)
}
func newErrResponse(err *Error) ErrResponse {
	const msg string = "internal server error - please contact support"
	switch err.Kind {
	case Internal, Database:
		return ErrResponse{
			Error: ServiceError{
				Kind:    Internal.String(),
				Message: msg,
			},
		}
	default:
		return ErrResponse{
			Error: ServiceError{
				Kind:    err.Kind.String(),
				Code:    string(err.Code),
				Param:   string(err.Param),
				Message: err.Error(),
			},
		}
	}
}
func unauthenticatedErrorResponse(w http.ResponseWriter, lgr zerolog.Logger, err *UnauthenticatedError) {
	lgr.Error().Stack().Err(err.Err).
		Int("http_statuscode", http.StatusUnauthorized).
		Str("realm", string(err.Realm())).
		Msg("Unauthenticated Request")
	w.Header().Set("WWW-Authenticate", fmt.Sprintf(`Bearer realm="%s"`, err.Realm()))
	w.WriteHeader(http.StatusUnauthorized)
}
func unauthorizedErrorResponse(w http.ResponseWriter, lgr zerolog.Logger, err *UnauthorizedError) {
	lgr.Error().Stack().Err(err.Err).
		Int("http_statuscode", http.StatusForbidden).
		Msg("Unauthorized Request")
	w.WriteHeader(http.StatusForbidden)
}
func nilErrorResponse(w http.ResponseWriter, lgr zerolog.Logger) {
	lgr.Error().Stack().
		Int("HTTP Error StatusCode", http.StatusInternalServerError).
		Msg("nil error - no response body sent")
	w.WriteHeader(http.StatusInternalServerError)
}
func otherErrorResponse(w http.ResponseWriter, lgr zerolog.Logger, err error) {
	er := ErrResponse{
		Error: ServiceError{
			Kind:    Unanticipated.String(),
			Code:    "Unanticipated",
			Message: "Unexpected error - contact support",
		},
	}
	lgr.Error().Err(err).Msg("Unknown Error")
	errJSON, _ := json.Marshal(er)
	ejson := string(errJSON)
	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("X-Content-Type-Options", "nosniff")
	w.WriteHeader(http.StatusInternalServerError)
	fmt.Fprintln(w, ejson)
}
func httpErrorStatusCode(k Kind) int {
	switch k {
	case Invalid, Exist, NotExist, Private, BrokenLink, Validation, InvalidRequest:
		return http.StatusBadRequest
	case Other, IO, Internal, Database, Unanticipated:
		return http.StatusInternalServerError
	default:
		return http.StatusInternalServerError
	}
}
func NewUnauthenticatedError(realm string, err error) *UnauthenticatedError {
	return &UnauthenticatedError{WWWAuthenticateRealm: realm, Err: err}
}
type UnauthenticatedError struct {
	WWWAuthenticateRealm string
	Err error
}
func (e UnauthenticatedError) Unwrap() error {
	return e.Err
}
func (e UnauthenticatedError) Error() string {
	return e.Err.Error()
}
func (e UnauthenticatedError) Realm() string {
	realm := e.WWWAuthenticateRealm
	if realm == "" {
		realm = "DefaultRealm"
	}
	return realm
}
func NewUnauthorizedError(err error) *UnauthorizedError {
	return &UnauthorizedError{Err: err}
}
type UnauthorizedError struct {
	Err error
}