<commit_before>package rest

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

// JSON является вспомогательным типом для быстрого создания JSON-структур.
type JSON map[string]interface{}

// Compress разрешает поддержку сжатия данных, если это поддерживается браузером.
// Если сжатие данных уже поддерживается, например, на уровне вашего обработчика, то вы можете
// заблокировать двойное сжатие, установив значение false.
var Compress = true

// Context содержит контекстную информацию HTTP-запроса и методы удобного формирования ответа
// на них. Т.к. http.Request импортируется в контекст напрямую, то можно совершенно спокойно
// использовать все его свойства и методы, как родные свойства и методы самого контекста.
type Context struct {
	// HTTP запрос в разобранном виде
	*http.Request
	// именованные параметры из пути запроса
	Params []Param
	// тип информации в ответе
	ContentType string
	// интерфейс для публикации ответа на запрос
	response http.ResponseWriter
	// код HTTP-ответа
	status int
	// разобранные параметры запроса в URL (кеш)
	urlQuery url.Values
	// дополнительные данные, устанавливаемые пользователем
	// в качестве ключа рекомендуется использовать приватный тип
	// и какое-нибудь его значение, что позволит застраховаться от
	// случайной перезаписи этих данных
	data map[interface{}]interface{}
}

// newContext возвращает новый инициализированный контекст. В отличии от просто создания нового
// контекста, вызов данного метода использует пул контекстов.
func newContext(w http.ResponseWriter, r *http.Request) *Context {
	context := contexts.Get().(*Context)
	context.Request = r
	context.Params = nil
	context.ContentType = ""
	context.response = w
	context.status = 0
	context.urlQuery = nil
	context.data = nil
	return context
}

// free возвращает контекст в пул используемых контекстов для дальнейшего использования.
// Вызывается автоматически после того, как контекст перестает использоваться.
func (c *Context) free() {
	contexts.Put(c)
}

// Get возвращает значение именованного параметра. Если параметр с таким именем не найден,
// то возвращается значение параметра из URL с тем же именем. Разбор параметров запроса сохраняется
// внутри Context и повторного его разбора уже не требует. Но это происходит только при первом
// к ним обращении.
func (c *Context) Get(key string) string {
	for _, param := range c.Params {
		if param.Key == key {
			return param.Value
		}
	}
	if c.urlQuery == nil {
		c.urlQuery = c.Request.URL.Query()
	}
	return c.urlQuery.Get(key)
}

// Data возвращает пользовательские данные, сохраненные в контексте запроса с указанным ключем.
// Обычно, такие данные сохраняются в контексте запроса, если его нужно передать между несколькими
// обработчиками. В частности, очень удобно использовать c Middleware-обработчиками.
func (c *Context) Data(key interface{}) interface{} {
	if c.data == nil {
		return nil
	}
	return c.data[key]
}

// SetData сохраняет пользовательские данные в контексте запроса с указанным ключем.
// Рекомендуется в качестве ключа использовать какой-нибудь приватный тип и его значение,
// чтобы избежать случайного затирания данных другими обработчиками: это гарантированно обезопасит
// от случайного доступа к ним.
func (c *Context) SetDataSet(key, value interface{}) {
	if c.data == nil {
		c.data = make(map[interface{}]interface{})
	}
	c.data[key] = value
}

// Status устанавливает код HTTP-ответа, который будет отправлен сервером. Данный метод возвращает
// ссылку на основной контекст, чтобы можно было использовать его в последовательности выполнения
// команд. Например, можно сразу установить код ответа и тут же опубликовать данные.
func (c *Context) Status(code int) *Context {
	if code >= 200 && code < 600 {
		c.status = code
	}
	return c
}

// SetHeader устанавливает новое значение для указанного HTTP-заголовка. Если передаваемое
// значение заголовка пустое, то данный заголовок будет удален.
func (c *Context) SetHeader(key, value string) {
	if value == "" {
		c.response.Header().Del(key)
	} else {
		c.response.Header().Set(key, value)
	}
}

// Parse декодирует содержимое запроса в объект. После чтения из запроса
// http.Request.Body автоматически закрывается и дополнительного закрытия не требуется.
//
// На данный момент поддерживается только разбор объектов в формате JSON.
func (c *Context) Parse(obj interface{}) error {
	defer c.Request.Body.Close()
	return json.NewDecoder(c.Request.Body).Decode(obj)
}

// Send публикует данные, переданные в параметре, в качестве ответа. Если ContentType не указан,
// то используется "application/json".
//
// В зависимости от типа передаваемых данных, ответ формируется по разному.
// Если данные являются бинарными ([]byte) или поддерживают интерфейс io.Reader, то отдаются
// как есть, без какого-либо изменения. Если io.Reader поддерживает io.Close, то он будет
// автоматически закрыт. Строки и ошибки преобразуются в простое JSON-сообщение, состоящие из кода
// статуса и текста сообщения. Остальные типы приводятся к формату JSON.
//<commit_msg>SetDataSet -> SetData<commit_after>package rest

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

// JSON является вспомогательным типом для быстрого создания JSON-структур.
type JSON map[string]interface{}

// Compress разрешает поддержку сжатия данных, если это поддерживается браузером.
// Если сжатие данных уже поддерживается, например, на уровне вашего обработчика, то вы можете
// заблокировать двойное сжатие, установив значение false.
var Compress = true

// Context содержит контекстную информацию HTTP-запроса и методы удобного формирования ответа
// на них. Т.к. http.Request импортируется в контекст напрямую, то можно совершенно спокойно
// использовать все его свойства и методы, как родные свойства и методы самого контекста.
type Context struct {
	// HTTP запрос в разобранном виде
	*http.Request
	// именованные параметры из пути запроса
	Params []Param
	// тип информации в ответе
	ContentType string
	// интерфейс для публикации ответа на запрос
	response http.ResponseWriter
	// код HTTP-ответа
	status int
	// разобранные параметры запроса в URL (кеш)
	urlQuery url.Values
	// дополнительные данные, устанавливаемые пользователем
	// в качестве ключа рекомендуется использовать приватный тип
	// и какое-нибудь его значение, что позволит застраховаться от
	// случайной перезаписи этих данных
	data map[interface{}]interface{}
}

// newContext возвращает новый инициализированный контекст. В отличии от просто создания нового
// контекста, вызов данного метода использует пул контекстов.
func newContext(w http.ResponseWriter, r *http.Request) *Context {
	context := contexts.Get().(*Context)
	context.Request = r
	context.Params = nil
	context.ContentType = ""
	context.response = w
	context.status = 0
	context.urlQuery = nil
	context.data = nil
	return context
}

// free возвращает контекст в пул используемых контекстов для дальнейшего использования.
// Вызывается автоматически после того, как контекст перестает использоваться.
func (c *Context) free() {
	contexts.Put(c)
}

// Get возвращает значение именованного параметра. Если параметр с таким именем не найден,
// то возвращается значение параметра из URL с тем же именем. Разбор параметров запроса сохраняется
// внутри Context и повторного его разбора уже не требует. Но это происходит только при первом
// к ним обращении.
func (c *Context) Get(key string) string {
	for _, param := range c.Params {
		if param.Key == key {
			return param.Value
		}
	}
	if c.urlQuery == nil {
		c.urlQuery = c.Request.URL.Query()
	}
	return c.urlQuery.Get(key)
}

// Data возвращает пользовательские данные, сохраненные в контексте запроса с указанным ключем.
// Обычно, такие данные сохраняются в контексте запроса, если его нужно передать между несколькими
// обработчиками. В частности, очень удобно использовать c Middleware-обработчиками.
func (c *Context) Data(key interface{}) interface{} {
	if c.data == nil {
		return nil
	}
	return c.data[key]
}

// SetData сохраняет пользовательские данные в контексте запроса с указанным ключем.
// Рекомендуется в качестве ключа использовать какой-нибудь приватный тип и его значение,
// чтобы избежать случайного затирания данных другими обработчиками: это гарантированно обезопасит
// от случайного доступа к ним.
func (c *Context) SetData(key, value interface{}) {
	if c.data == nil {
		c.data = make(map[interface{}]interface{})
	}
	c.data[key] = value
}

// Status устанавливает код HTTP-ответа, который будет отправлен сервером. Данный метод возвращает
// ссылку на основной контекст, чтобы можно было использовать его в последовательности выполнения
// команд. Например, можно сразу установить код ответа и тут же опубликовать данные.
func (c *Context) Status(code int) *Context {
	if code >= 200 && code < 600 {
		c.status = code
	}
	return c
}

// SetHeader устанавливает новое значение для указанного HTTP-заголовка. Если передаваемое
// значение заголовка пустое, то данный заголовок будет удален.
func (c *Context) SetHeader(key, value string) {
	if value == "" {
		c.response.Header().Del(key)
	} else {
		c.response.Header().Set(key, value)
	}
}

// Parse декодирует содержимое запроса в объект. После чтения из запроса
// http.Request.Body автоматически закрывается и дополнительного закрытия не требуется.
//
// На данный момент поддерживается только разбор объектов в формате JSON.
func (c *Context) Parse(obj interface{}) error {
	defer c.Request.Body.Close()
	return json.NewDecoder(c.Request.Body).Decode(obj)
}

// Send публикует данные, переданные в параметре, в качестве ответа. Если ContentType не указан,
// то используется "application/json".
//
// В зависимости от типа передаваемых данных, ответ формируется по разному.
// Если данные являются бинарными ([]byte) или поддерживают интерфейс io.Reader, то отдаются
// как есть, без какого-либо изменения. Если io.Reader поддерживает io.ReadCloser, то он будет
// автоматически закрыт. Строки и ошибки преобразуются в простое JSON-сообщение, состоящие из кода
// статуса и текста сообщения. Остальные типы приводятся к формату JSON.
//
{'fraction_non_alphanumeric': 0.08481613285883749, 'fraction_numerical': 0.001383946223803875, 'mean_word_length': 4.258316008316008, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 6, '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': '1135693', 'n_tokens_mistral': 3755, 'n_tokens_neox': 4304, 'n_words': 1133}