<commit_before>package redis

import (
	"bytes"
	"errors"
	"fmt"
	"reflect"
	"time"
)

type CheckerFn func(request *Request) (reflect.Value, ReplyWriter)

// type AutoHandler interface {
// 	GET(key string) ([]byte, error)
// 	SET(key string, value []byte) error
// 	HMSET(key string, values *map[string][]byte) error
// 	HGETALL(key string) (*map[string][]byte, error)
// 	HGET(hash string, key string) ([]byte, error)
// 	HSET(hash string, key string, value []byte) error
// 	BRPOP(channels ...[]byte) ([][]byte, error)
// 	SUBSCRIBE(channels ...[]byte) (*ChannelWriter, error)
// 	DEL(key string, keys ...[]byte) (int, error)
// }

func NewAutoHandler(autoHandler interface{}) (*Handler, error) {
	handler := &Handler{}

	rh := reflect.TypeOf(autoHandler)
	for i := 0; i < rh.NumMethod(); i++ {
		method := rh.Method(i)
		handlerFn, err := createHandlerFn(autoHandler, &method)
		if err != nil {
			return nil, err
		}
		handler.Register(method.Name, handlerFn)
	}
	return handler, nil
}

func createHandlerFn(autoHandler interface{}, method *reflect.Method) (HandlerFn, error) {
	errorType := reflect.TypeOf(createHandlerFn).Out(1)
	mtype := method.Func.Type()
	checkers, err := createCheckers(method)
	if err != nil {
		return nil, err
	}

	// Check output
	if mtype.NumOut() == 0 {
		return nil, errors.New("Not enough return values")
	}
	if mtype.NumOut() > 2 {
		return nil, errors.New("Too many return values")
	}
	if t := mtype.Out(mtype.NumOut() - 1); t != errorType {
		return nil, fmt.Errorf("Last return value must be an error (not %s)", t)
	}

	return handlerFn(autoHandler, method, checkers)
}

func handlerFn(autoHandler interface{}, method *reflect.Method, checkers []CheckerFn) (HandlerFn, error) {
	return func(request *Request, c chan struct{}, monitorChans *[]chan string) (ReplyWriter, error) {
		input := []reflect.Value{reflect.ValueOf(autoHandler)}
		if method.Func.Type().NumIn() < len(request.Args) && !method.Func.Type().IsVariadic() {
			return ErrTooMuchArgs, nil
		}
		for _, checker := range checkers {
			value, reply := checker(request)
			if reply != nil {
				return reply, nil
			}
			input = append(input, value)
		}
		monitorString := fmt.Sprintf("%.6f [0 %s] \"%s\" \"%s\"",
			float64(time.Now().UTC().UnixNano())/1e9,
			request.Host,
			request.Name,
			bytes.Join(request.Args, []byte{'"', ' ', '"'}))
		for _, c := range *monitorChans {
			select {
			case c <- monitorString:
			default:
			}
		}
		Debugf("Monitors: %d\n", len(*monitorChans))
		Debugf("%s\n", monitorString)
		var result []reflect.Value
		if method.Func.Type().IsVariadic() {
			result = method.Func.CallSlice(input)
		} else {
			result = method.Func.Call(input)
		}

		var ret interface{}
		if ierr := result[len(result)-1].Interface(); ierr != nil {
			// Last return value is an error, wrap it to redis error
			err := ierr.(error)<commit_msg>Remove args checking as it breaks request.GetMap<commit_after>package redis

import (
	"bytes"
	"errors"
	"fmt"
	"reflect"
	"time"
)

type CheckerFn func(request *Request) (reflect.Value, ReplyWriter)

// type AutoHandler interface {
// 	GET(key string) ([]byte, error)
// 	SET(key string, value []byte) error
// 	HMSET(key string, values *map[string][]byte) error
// 	HGETALL(key string) (*map[string][]byte, error)
// 	HGET(hash string, key string) ([]byte, error)
// 	HSET(hash string, key string, value []byte) error
// 	BRPOP(channels ...[]byte) ([][]byte, error)
// 	SUBSCRIBE(channels ...[]byte) (*ChannelWriter, error)
// 	DEL(key string, keys ...[]byte) (int, error)
// }

func NewAutoHandler(autoHandler interface{}) (*Handler, error) {
	handler := &Handler{}

	rh := reflect.TypeOf(autoHandler)
	for i := 0; i < rh.NumMethod(); i++ {
		method := rh.Method(i)
		handlerFn, err := createHandlerFn(autoHandler, &method)
		if err != nil {
			return nil, err
		}
		handler.Register(method.Name, handlerFn)
	}
	return handler, nil
}

func createHandlerFn(autoHandler interface{}, method *reflect.Method) (HandlerFn, error) {
	errorType := reflect.TypeOf(createHandlerFn).Out(1)
	mtype := method.Func.Type()
	checkers, err := createCheckers(method)
	if err != nil {
		return nil, err
	}

	// Check output
	if mtype.NumOut() == 0 {
		return nil, errors.New("Not enough return values")
	}
	if mtype.NumOut() > 2 {
		return nil, errors.New("Too many return values")
	}
	if t := mtype.Out(mtype.NumOut() - 1); t != errorType {
		return nil, fmt.Errorf("Last return value must be an error (not %s)", t)
	}

	return handlerFn(autoHandler, method, checkers)
}

func handlerFn(autoHandler interface{}, method *reflect.Method, checkers []CheckerFn) (HandlerFn, error) {
	return func(request *Request, c chan struct{}, monitorChans *[]chan string) (ReplyWriter, error) {
		input := []reflect.Value{reflect.ValueOf(autoHandler)}

		for _, checker := range checkers {
			value, reply := checker(request)
			if reply != nil {
				return reply, nil
			}
			input = append(input, value)
		}
		monitorString := fmt.Sprintf("%.6f [0 %s] \"%s\" \"%s\"",
			float64(time.Now().UTC().UnixNano())/1e9,
			request.Host,
			request.Name,
			bytes.Join(request.Args, []byte{'"', ' ', '"'}))
		for _, c := range *monitorChans {
			select {
			case c <- monitorString:
			default:
			}
		}
		Debugf("Monitors: %d\n", len(*monitorChans))
		Debugf("%s\n", monitorString)
		var result []reflect.Value
		if method.Func.Type().IsVariadic() {
			result = method.Func.CallSlice(input)
		} else {
			result = method.Func.Call(input)
		}

		var ret interface{}
		if ierr := result[len(result)-1].Interface(); ierr != nil {
			// Last return value is an error, wrap it to redis error
			err := ierr.(error)
{'fraction_non_alphanumeric': 0.16980127750177432, 'fraction_numerical': 0.0042583392476933995, 'mean_word_length': 3.88474870017331, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2569111', 'n_tokens_mistral': 2105, 'n_tokens_neox': 1950, 'n_words': 512}