package query import ( "context" "net/http" "go.pkg.cx/middleware" ) // Context keys var ( FilterCtxKey = &middleware.CtxKey{Pkg: "go.pkg.cx/middleware/query", Name: "Filter"} ) // FilterOptions turns a list of option instances into an option func FilterOptions[T any](opts ...FilterOption[T]) FilterOption[T] { return func(f *filter[T]) { for _, opt := range opts { opt(f) } } } // FilterOption configures filter middleware type FilterOption[T any] func(f *filter[T]) // WithParseFilterFn adds filter parse function to the list func WithParseFilterFn[T any](fn func(r *http.Request) (T, error)) FilterOption[T] { return func(f *filter[T]) { f.parseFilterFns = append(f.parseFilterFns, fn) } } // SetFilterDefault sets filter default value func SetFilterDefault[T any](defaultValue T) FilterOption[T] { return func(f *filter[T]) { f.defaultFilter = defaultValue } } // SetResponseHandler sets filter response handler func SetResponseHandler[T any](fn middleware.ResponseHandle) FilterOption[T] { return func(f *filter[T]) { f.responseHandler = fn } } type filter[T any] struct { defaultFilter T parseFilterFns []func(r *http.Request) (T, error) responseHandler middleware.ResponseHandle } // Filter returns filter middleware func Filter[T any](opts ...FilterOption[T]) func(next http.Handler) http.Handler { f := &filter[T]{ defaultFilter: *new(T), parseFilterFns: []func(r *http.Request) (T, error){}, responseHandler: middleware.RespondWithBadRequest, } for _, opt := range opts { opt(f) } return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { filter := f.defaultFilter var err error for _, fn := range f.parseFilterFns { if filter, err = fn(r); err == nil { break } } if err != nil { f.responseHandler(w, r, err) return } ctx := r.Context() ctx = context.WithValue(ctx, FilterCtxKey, filter) next.ServeHTTP(w, r.WithContext(ctx)) }) } } // FilterFromContext returns filter from context func FilterFromContext[T any](ctx context.Context) T { if filter, ok := ctx.Value(FilterCtxKey).(T); ok { return filter } return *new(T) } // FilterFromQuery returns filter string from query params func FilterFromQuery(filterParam string) func(r *http.Request) string { return func(r *http.Request) string { return r.URL.Query().Get(filterParam) } }