215 lines
6.1 KiB
Go
215 lines
6.1 KiB
Go
package magic
|
|
|
|
import (
|
|
"log/slog"
|
|
"net/http"
|
|
"reflect"
|
|
|
|
"git.jeffthecoder.xyz/public/lazyhandler/util"
|
|
)
|
|
|
|
var (
|
|
extractors = make(map[reflect.Type]func(*http.Request) (any, error))
|
|
|
|
extractorsTakeBody = make(map[reflect.Type]bool)
|
|
|
|
extractorsTakesResponseWriter = make(map[reflect.Type]func(http.ResponseWriter, *http.Request) (any, error))
|
|
)
|
|
|
|
type FromRequest interface {
|
|
FromRequest(*http.Request) error
|
|
}
|
|
|
|
// Marker interface
|
|
type TakeRequestBody interface {
|
|
TakeRequestBody()
|
|
}
|
|
|
|
// Marker interface
|
|
type TakeResponseWriter interface {
|
|
TakeResponseWriter(http.ResponseWriter)
|
|
}
|
|
|
|
func RegisterExtractor(o any, extractor func(*http.Request) (any, error)) {
|
|
if t, ok := o.(reflect.Type); ok {
|
|
slog.With("type", t.String()).Debug("extractor type registered with reflect.Type")
|
|
extractors[t] = extractor
|
|
} else if v, ok := o.(reflect.Value); ok {
|
|
slog.With("type", v.Type().String(), "value", v.Interface()).Debug("extractor type registered with reflect.Value")
|
|
|
|
extractors[v.Type()] = extractor
|
|
} else {
|
|
t := reflect.TypeOf(o)
|
|
slog.With("type", t.String(), "value", o).Debug("extractor type registered with object")
|
|
|
|
extractors[t] = extractor
|
|
}
|
|
}
|
|
|
|
func RegisterExtractorGeneric[T any](extractor func(*http.Request) (any, error)) {
|
|
var pointerToT *T
|
|
RegisterExtractor(reflect.TypeOf(pointerToT).Elem(), extractor)
|
|
}
|
|
|
|
func RegisterExtractorThatTakesBody(o any, extractor func(*http.Request) (any, error)) {
|
|
if t, ok := o.(reflect.Type); ok {
|
|
slog.With("type", t.String()).Debug("extractor type registered with reflect.Type")
|
|
extractors[t] = extractor
|
|
extractorsTakeBody[t] = true
|
|
} else if v, ok := o.(reflect.Value); ok {
|
|
slog.With("type", v.Type().String(), "value", v.Interface()).Debug("extractor type registered with reflect.Value")
|
|
|
|
extractors[v.Type()] = extractor
|
|
extractorsTakeBody[v.Type()] = true
|
|
} else {
|
|
t := reflect.TypeOf(o)
|
|
slog.With("type", t.String(), "value", o).Debug("extractor type registered with object")
|
|
|
|
extractors[t] = extractor
|
|
extractorsTakeBody[t] = true
|
|
}
|
|
}
|
|
|
|
func RegisterExtractorThatTakesBodyGeneric[T any](extractor func(*http.Request) (any, error)) {
|
|
var pointerToT *T
|
|
RegisterExtractorThatTakesBody(reflect.TypeOf(pointerToT).Elem(), extractor)
|
|
}
|
|
|
|
func IsTakeBody(t reflect.Type) bool {
|
|
if isTakeBody := extractorsTakeBody[t]; isTakeBody {
|
|
return isTakeBody
|
|
}
|
|
|
|
if _, ok := util.Implements[TakeRequestBody](t); ok {
|
|
return ok
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func RegisterExtractorThatTakesResponseWriter(o any, extractor func(http.ResponseWriter, *http.Request) (any, error)) {
|
|
if t, ok := o.(reflect.Type); ok {
|
|
slog.With("type", t.String()).Debug("extractor type registered with reflect.Type")
|
|
extractorsTakesResponseWriter[t] = extractor
|
|
} else if v, ok := o.(reflect.Value); ok {
|
|
slog.With("type", v.Type().String(), "value", v.Interface()).Debug("extractor type registered with reflect.Value")
|
|
|
|
extractorsTakesResponseWriter[v.Type()] = extractor
|
|
} else {
|
|
t := reflect.TypeOf(o)
|
|
slog.With("type", t.String(), "value", o).Debug("extractor type registered with object")
|
|
|
|
extractorsTakesResponseWriter[t] = extractor
|
|
}
|
|
}
|
|
|
|
func RegisterExtractorThatTakesResponseWriterGeneric[T any](extractor func(http.ResponseWriter, *http.Request) (any, error)) {
|
|
var pointerToT *T
|
|
RegisterExtractorThatTakesResponseWriter(reflect.TypeOf(pointerToT).Elem(), extractor)
|
|
}
|
|
|
|
func GetExtractor(t reflect.Type) (func(http.ResponseWriter, *http.Request) (any, error), bool) {
|
|
_, isTakeResponseWriter := util.Implements[TakeResponseWriter](t)
|
|
if t.Kind() == reflect.Pointer {
|
|
_, ptrIsTakeResponseWriter := util.Implements[TakeResponseWriter](t.Elem())
|
|
isTakeResponseWriter = isTakeResponseWriter || ptrIsTakeResponseWriter
|
|
}
|
|
|
|
fromRequestType := reflect.TypeOf((*FromRequest)(nil)).Elem()
|
|
if t.Implements(fromRequestType) {
|
|
return func(w http.ResponseWriter, r *http.Request) (any, error) {
|
|
val := reflect.New(t).Elem()
|
|
if t.Kind() == reflect.Pointer {
|
|
val = reflect.New(t.Elem())
|
|
}
|
|
|
|
if isTakeResponseWriter {
|
|
if taker, ok := val.Addr().Interface().(TakeResponseWriter); ok {
|
|
taker.TakeResponseWriter(w)
|
|
}
|
|
}
|
|
|
|
if fromR, ok := val.Interface().(FromRequest); ok {
|
|
if err := fromR.FromRequest(r); err != nil {
|
|
return nil, err
|
|
}
|
|
} else if val.CanAddr() {
|
|
if fromR, ok := val.Addr().Interface().(FromRequest); ok {
|
|
if err := fromR.FromRequest(r); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
if t.Kind() == reflect.Pointer {
|
|
|
|
return val.Interface(), nil
|
|
}
|
|
return val.Addr().Interface(), nil
|
|
}, isTakeResponseWriter
|
|
}
|
|
|
|
if reflect.PointerTo(t).Implements(fromRequestType) {
|
|
return func(w http.ResponseWriter, r *http.Request) (any, error) {
|
|
val := reflect.New(t)
|
|
|
|
if isTakeResponseWriter {
|
|
if taker, ok := val.Interface().(TakeResponseWriter); ok {
|
|
taker.TakeResponseWriter(w)
|
|
}
|
|
}
|
|
|
|
if fromR, ok := val.Interface().(FromRequest); ok {
|
|
if err := fromR.FromRequest(r); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return val.Elem().Interface(), nil
|
|
}, isTakeResponseWriter
|
|
}
|
|
|
|
if extractor, ok := extractors[t]; ok {
|
|
return func(_ http.ResponseWriter, r *http.Request) (any, error) {
|
|
return extractor(r)
|
|
}, false
|
|
}
|
|
|
|
if t.Kind() == reflect.Pointer {
|
|
if extractor, ok := extractors[t.Elem()]; ok {
|
|
return func(w http.ResponseWriter, r *http.Request) (any, error) {
|
|
v, err := extractor(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Create a pointer to the value
|
|
vp := reflect.New(reflect.TypeOf(v))
|
|
vp.Elem().Set(reflect.ValueOf(v))
|
|
return vp.Interface(), nil
|
|
}, false
|
|
}
|
|
}
|
|
|
|
if extractor, ok := extractorsTakesResponseWriter[t]; ok {
|
|
return func(w http.ResponseWriter, r *http.Request) (any, error) {
|
|
return extractor(w, r)
|
|
}, true
|
|
}
|
|
|
|
if t.Kind() == reflect.Pointer {
|
|
if extractor, ok := extractorsTakesResponseWriter[t.Elem()]; ok {
|
|
return func(w http.ResponseWriter, r *http.Request) (any, error) {
|
|
v, err := extractor(w, r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Create a pointer to the value
|
|
vp := reflect.New(reflect.TypeOf(v))
|
|
vp.Elem().Set(reflect.ValueOf(v))
|
|
return vp.Interface(), nil
|
|
}, true
|
|
}
|
|
}
|
|
|
|
return nil, isTakeResponseWriter
|
|
}
|