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)) extractorsTakesResponseWriter = make(map[reflect.Type]func(http.ResponseWriter, *http.Request) (any, error)) ) type FromRequest interface { FromRequest(*http.Request) error } 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 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 } if _, ok := util.Implements[FromRequest](t); ok { if t.Kind() == reflect.Pointer { // T.Implement(FromRequest) and T is Pointer // create new actual T and call T.FromRequest on it // if error happens, no error is returned. just return nil return func(w http.ResponseWriter, r *http.Request) (any, error) { // var t T // return t, t.FromRequest(r) z := reflect.New(t.Elem()) if isTakeResponseWriter { z.MethodByName("TakeResponseWriter").Call([]reflect.Value{reflect.ValueOf(w)}) } results := z.MethodByName("FromRequest").Call([]reflect.Value{reflect.ValueOf(r)}) if err, ok := results[0].Interface().(error); ok && err != nil { if errResponse, ok := err.(ErrorResponse); ok { return reflect.Zero(t).Interface(), errResponse } return reflect.Zero(t).Interface(), nil } return z.Interface(), nil }, isTakeResponseWriter } // T.Implement(FromRequest) and T is not Pointer // create zero T and call T.FromRequest directly on it return func(w http.ResponseWriter, r *http.Request) (any, error) { // var t T // return t, t.FromRequest(r) z := reflect.Zero(t) if isTakeResponseWriter { z.MethodByName("TakeResponseWriter").Call([]reflect.Value{reflect.ValueOf(w)}) } results := z.MethodByName("FromRequest").Call([]reflect.Value{reflect.ValueOf(r)}) if err, ok := results[0].Interface().(error); ok { return z.Interface(), err } return z.Interface(), nil }, isTakeResponseWriter } else if v, ok := util.PointerImplements[FromRequest](t); ok { return func(w http.ResponseWriter, r *http.Request) (any, error) { // var t T // return t, t.FromRequest(r) z := reflect.New(v.Type().Elem()) if isTakeResponseWriter { z.MethodByName("TakeResponseWriter").Call([]reflect.Value{reflect.ValueOf(w)}) } results := z.MethodByName("FromRequest").Call([]reflect.Value{reflect.ValueOf(r)}) if err := results[0].Interface(); err == nil { return z.Elem().Interface(), nil } return z.Elem().Interface(), results[0].Interface().(error) }, 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 } return &v, 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 } return &v, nil }, true } } return nil, isTakeResponseWriter }