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 }