sync from project

This commit is contained in:
2025-06-18 10:12:19 +08:00
parent 61ffeeb3b8
commit fb579e8689
20 changed files with 1332 additions and 103 deletions

View File

@ -11,6 +11,8 @@ import (
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))
)
@ -18,6 +20,12 @@ type FromRequest interface {
FromRequest(*http.Request) error
}
// Marker interface
type TakeRequestBody interface {
TakeRequestBody()
}
// Marker interface
type TakeResponseWriter interface {
TakeResponseWriter(http.ResponseWriter)
}
@ -43,6 +51,42 @@ func RegisterExtractorGeneric[T any](extractor func(*http.Request) (any, error))
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")
@ -70,62 +114,57 @@ func GetExtractor(t reflect.Type) (func(http.ResponseWriter, *http.Request) (any
_, 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)})
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)
}
}
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
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
}
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)})
}
if t.Kind() == reflect.Pointer {
results := z.MethodByName("FromRequest").Call([]reflect.Value{reflect.ValueOf(r)})
if err, ok := results[0].Interface().(error); ok {
return z.Interface(), err
return val.Interface(), nil
}
return z.Interface(), nil
return val.Addr().Interface(), nil
}, isTakeResponseWriter
} else if v, ok := util.PointerImplements[FromRequest](t); ok {
}
if reflect.PointerTo(t).Implements(fromRequestType) {
return func(w http.ResponseWriter, r *http.Request) (any, error) {
// var t T
// return t, t.FromRequest(r)
z := reflect.New(v.Type().Elem())
val := reflect.New(t)
if isTakeResponseWriter {
z.MethodByName("TakeResponseWriter").Call([]reflect.Value{reflect.ValueOf(w)})
if taker, ok := val.Interface().(TakeResponseWriter); ok {
taker.TakeResponseWriter(w)
}
}
results := z.MethodByName("FromRequest").Call([]reflect.Value{reflect.ValueOf(r)})
if err := results[0].Interface(); err == nil {
return z.Elem().Interface(), nil
if fromR, ok := val.Interface().(FromRequest); ok {
if err := fromR.FromRequest(r); err != nil {
return nil, err
}
}
return z.Elem().Interface(), results[0].Interface().(error)
return val.Elem().Interface(), nil
}, isTakeResponseWriter
}
@ -142,7 +181,10 @@ func GetExtractor(t reflect.Type) (func(http.ResponseWriter, *http.Request) (any
if err != nil {
return nil, err
}
return &v, nil
// Create a pointer to the value
vp := reflect.New(reflect.TypeOf(v))
vp.Elem().Set(reflect.ValueOf(v))
return vp.Interface(), nil
}, false
}
}
@ -160,7 +202,10 @@ func GetExtractor(t reflect.Type) (func(http.ResponseWriter, *http.Request) (any
if err != nil {
return nil, err
}
return &v, nil
// Create a pointer to the value
vp := reflect.New(reflect.TypeOf(v))
vp.Elem().Set(reflect.ValueOf(v))
return vp.Interface(), nil
}, true
}
}