<commit_before>/*
Package jsonware has a middleware setup for doing RESTful reqeuests with net/http.
It makes it easy to unobtrusively serialize and deserialize json in a type safe
manner, and handle errors from the internal handlers including logging.
*/
package jsonware

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"reflect"
	"strings"
)

/*
JSONHandler handles json api endpoint restful requests. It can be constructed
by passing a suitable function into the JSON function.

Error handling is handled differently depending on whether or not JSONErr was
used to report the error. See JSONErr documentation to understand how to control
error handling.

To log cloaked errors, use the Log() function to set an io.Writer to log to
when reporting a real internal server error.

	// Register a JSONHandler.
	http.Handle("/", JSON(myHandler).Log(myLogger))
*/
type JSONHandler struct {
	logger io.Writer
	fn     reflect.Value
	in     reflect.Type
}

// Log sets the JSONHandler's logging io.Writer for writing out cloaked errors.
func (j *JSONHandler) Log(logger io.Writer) *JSONHandler {
	j.logger = logger
	return j
}

/*
JSONErr can be used in a JSONHandler to override the error mechanism in
JSONHandler's ServeHTTP method. If a status is set it will obey it,
otherwise it will assume 200 OK. The error message will be relayed to the
client. If you wish to use a general server error, simply return error from
the handler.

	func handler(w http.ResponseWriter, r *http.Request) {
		return nil, nil // No response from ServeHTTP, use w to create one.
	}

	func handler(w http.ResponseWriter, r *http.Request) {
		return nil, errors.New("hi") // 500 Response with cloaked+logged error.
	}

	func handler(w http.ResponseWriter, r *http.Request) {
		return nil, JSONErr{Err: errors.New("hi")} // 200 Response with error output to client
	}

	func handler(w http.ResponseWriter, r *http.Request) {
		return nil, JSONErr{400, errors.New("hi")} // 400 Response with error output to client
	}
*/
type JSONErr struct {
	Status int
	Err    error
}

// Error returns Error() from the internal error.
func (j JSONErr) Error() string {
	return j.Err.Error()
}

// ServeHTTP serves an http response, see JSONHandler documentation for details.
func (j JSONHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Ensure request accepts json
	ah := r.Header.Get("Accept")
	if !strings.Contains(ah, "*/*") && !strings.Contains(ah, "application/json") {
		w.Header().Set("Content-Type", "text/plain")
		w.WriteHeader(http.StatusBadRequest)
		io.WriteString(w, "this endpoint only responds to json-accepting clients")
		return
	}

	w.Header().Set("Content-Type", "application/json")

	// Ensure request follows REST principles.
	deserialize := j.fn.Type().NumIn() == 3
	switch {
	case deserialize && !isDataMethod(r.Method):
		fallthrough
	case !deserialize && isDataMethod(r.Method):
		writeJSONError(w, j.logger, JSONErr{
			http.StatusBadRequest,
			fmt.Errorf("invalid http method to this endpoint: %s", r.Method),
		})
		return
	}

	// Set up arguments for handler call.
	in := []reflect.Value{
		reflect.ValueOf(w), reflect.ValueOf(r),
	}
	var deserializeTo reflect.Value
	if deserialize {
		switch j.in.Kind() {
		case reflect.Slice, reflect.Map:
			deserializeTo = reflect.New(j.in)
			in = append(in, deserializeTo.Elem())
		case reflect.Ptr:
			deserializeTo = reflect.New(j.in.Elem())
			in = append(in, deserializeTo)
		}
	}

	// Do json deserialization of body.
	if deserialize {
		dec := json.NewDecoder(r.Body)
		defer r.Body.Close()

		if err := dec.Decode(deserializeTo.Interface()); err != nil {
			writeJSONError(w, j.logger, JSONErr{
				http.StatusBadRequest,
				fmt.Errorf("could not deserialize json request body"),
			})
			return
		}
	}

	out := j.fn.Call(in)

	// Handle error return value
	if !out[1].IsNil() {
		writeJSONError(w, j.logger, out[1].Interface().(error))
		return
	}

	// Serialize the interface{} return value<commit_msg>Stop using defer for no reason.<commit_after>/*
Package jsonware has a middleware setup for doing RESTful reqeuests with net/http.
It makes it easy to unobtrusively serialize and deserialize json in a type safe
manner, and handle errors from the internal handlers including logging.
*/
package jsonware

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"reflect"
	"strings"
)

/*
JSONHandler handles json api endpoint restful requests. It can be constructed
by passing a suitable function into the JSON function.

Error handling is handled differently depending on whether or not JSONErr was
used to report the error. See JSONErr documentation to understand how to control
error handling.

To log cloaked errors, use the Log() function to set an io.Writer to log to
when reporting a real internal server error.

	// Register a JSONHandler.
	http.Handle("/", JSON(myHandler).Log(myLogger))
*/
type JSONHandler struct {
	logger io.Writer
	fn     reflect.Value
	in     reflect.Type
}

// Log sets the JSONHandler's logging io.Writer for writing out cloaked errors.
func (j *JSONHandler) Log(logger io.Writer) *JSONHandler {
	j.logger = logger
	return j
}

/*
JSONErr can be used in a JSONHandler to override the error mechanism in
JSONHandler's ServeHTTP method. If a status is set it will obey it,
otherwise it will assume 200 OK. The error message will be relayed to the
client. If you wish to use a general server error, simply return error from
the handler.

	func handler(w http.ResponseWriter, r *http.Request) {
		return nil, nil // No response from ServeHTTP, use w to create one.
	}

	func handler(w http.ResponseWriter, r *http.Request) {
		return nil, errors.New("hi") // 500 Response with cloaked+logged error.
	}

	func handler(w http.ResponseWriter, r *http.Request) {
		return nil, JSONErr{Err: errors.New("hi")} // 200 Response with error output to client
	}

	func handler(w http.ResponseWriter, r *http.Request) {
		return nil, JSONErr{400, errors.New("hi")} // 400 Response with error output to client
	}
*/
type JSONErr struct {
	Status int
	Err    error
}

// Error returns Error() from the internal error.
func (j JSONErr) Error() string {
	return j.Err.Error()
}

// ServeHTTP serves an http response, see JSONHandler documentation for details.
func (j JSONHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Ensure request accepts json
	ah := r.Header.Get("Accept")
	if !strings.Contains(ah, "*/*") && !strings.Contains(ah, "application/json") {
		w.Header().Set("Content-Type", "text/plain")
		w.WriteHeader(http.StatusBadRequest)
		io.WriteString(w, "this endpoint only responds to json-accepting clients")
		return
	}

	w.Header().Set("Content-Type", "application/json")

	// Ensure request follows REST principles.
	deserialize := j.fn.Type().NumIn() == 3
	switch {
	case deserialize && !isDataMethod(r.Method):
		fallthrough
	case !deserialize && isDataMethod(r.Method):
		writeJSONError(w, j.logger, JSONErr{
			http.StatusBadRequest,
			fmt.Errorf("invalid http method to this endpoint: %s", r.Method),
		})
		return
	}

	// Set up arguments for handler call.
	in := []reflect.Value{
		reflect.ValueOf(w), reflect.ValueOf(r),
	}
	var deserializeTo reflect.Value
	if deserialize {
		switch j.in.Kind() {
		case reflect.Slice, reflect.Map:
			deserializeTo = reflect.New(j.in)
			in = append(in, deserializeTo.Elem())
		case reflect.Ptr:
			deserializeTo = reflect.New(j.in.Elem())
			in = append(in, deserializeTo)
		}
	}

	// Do json deserialization of body.
	if deserialize {
		dec := json.NewDecoder(r.Body)

		if err := dec.Decode(deserializeTo.Interface()); err != nil {
			writeJSONError(w, j.logger, JSONErr{
				http.StatusBadRequest,
				fmt.Errorf("could not deserialize json request body"),
			})
			return
		}
		r.Body.Close()
	}

	out := j.fn.Call(in)

	// Handle error return value
	if !out[1].IsNil() {
		writeJSONError(w, j.logger, out[1].Interface().(error))
		return
	}

	// Serialize the interface{} return value
{'fraction_non_alphanumeric': 0.1102570564516129, 'fraction_numerical': 0.0045362903225806455, 'mean_word_length': 3.905438813349815, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '7596783', 'n_tokens_mistral': 2588, 'n_tokens_neox': 2426, 'n_words': 871}