Files
x/smalltrip/exceptions/exceptions.go

127 lines
2.7 KiB
Go

package exceptions
import (
"errors"
"fmt"
"net/http"
)
type Exception struct {
Status int `json:"status"` // HTTP Status Code
Code string `json:"code"` // Application error code
Message string `json:"message"` // User friendly message
Err error `json:"error,omitempty"` // Go error
Data map[string]any `json:"data,omitempty"` // Additional contextual data
Severity Severity `json:"severity"` // Exception level
// Handler to be used. This is normally provided by a middleware via the
// request context. Setting this field overrides any provided by the middleware
// and can be used to add a handler when using a middleware is not possible.
handler HandlerFunc `json:"-"`
headers http.Header
}
var (
_ fmt.Stringer = Exception{}
_ error = Exception{}
_ http.Handler = Exception{}
)
func (e Exception) String() string {
return fmt.Sprintf("%s %3d %s Exception %q", e.Severity, e.Status, e.Code, e.Message)
}
func (e Exception) Error() string {
return e.String()
}
func (e Exception) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if e.handler != nil {
e.handler(e, w, r)
}
e.handler = HandlerJSON(HandlerText)
handler, ok := r.Context().Value(handlerFuncCtxKey).(HandlerFunc)
if !ok {
e.handler(e, w, r)
}
handler(e, w, r)
}
func newException(options ...Option) Exception {
e := Exception{
Status: http.StatusInternalServerError,
Code: "Internal Server Error",
Message: "",
Err: nil,
Severity: ERROR,
}
for _, option := range options {
option(&e)
}
return e
}
type Option = func(*Exception)
func WithStatus(s int) Option {
return func(e *Exception) { e.Status = s }
}
func WithCode(c string) Option {
return func(e *Exception) { e.Code = c }
}
func WithMessage(m string) Option {
return func(e *Exception) { e.Message = m }
}
func WithError(err error, errs ...error) Option {
if len(errs) > 0 {
es := []error{err}
es = append(es, errs...)
err = errors.Join(es...)
}
return func(e *Exception) { e.Err = err }
}
func WithSeverity(s Severity) Option {
return func(e *Exception) { e.Severity = s }
}
func WithData(key string, v any) Option {
return func(e *Exception) {
if e.Data == nil {
e.Data = make(map[string]any)
}
e.Data[key] = v
}
}
func WithHeader(header string, v string) Option {
return func(e *Exception) {
if e.headers == nil {
e.headers = http.Header{}
}
e.headers.Add(header, v)
}
}
func WithoutHeader(header string) Option {
return func(e *Exception) {
if e.headers == nil {
e.headers = http.Header{}
}
e.headers.Del(header)
}
}
func WithHandler(h HandlerFunc) Option {
return func(e *Exception) { e.handler = h }
}