<commit_before>package rest

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

const (
	IntParam      = "i"
	AlphaNumParam = "an"
)

var regParam = regexp.MustCompile(`{(\w+)(:\w+)?}`)
var regMap = map[string]string{
	IntParam:      `([0-9]+)`,
	AlphaNumParam: `([0-9A-Za-z]+)`,
}

type Route struct {
	path        string
	actions     map[string]ControllerAction
	params      []string
	paramValues map[string]interface{}
	regex       *regexp.Regexp
}

func (r *Route) init() {
	toSub := regParam.FindAllStringSubmatch(r.path, -1)

	regString := r.path

	if len(toSub) > 0 {
		r.params = make([]string, len(toSub))

		for i, v := range toSub {
			whole, param, pType, regex := v[0], v[1], v[2], `([^/]+)`

			r.params[i] = param

			if len(pType) > 1 {
				if r, ok := regMap[pType[1:]]; ok {
					regex = r
				}
			}
			regString = strings.Replace(regString, whole, regex, -1)
		}
	}
	r.regex = regexp.MustCompile(regString + "/?")
}

func (r *Route) match(test string) bool {
	matches := r.regex.FindStringSubmatch(test)
	if matches != nil && matches[0] == test {
		r.paramValues = make(map[string]interface{})

		for i, m := range matches[1:] {
			r.paramValues[r.params[i]] = m
		}

		return true
	}

	return false
}

func (r *Route) Params() map[string]interface{} {
	pv := make(map[string]interface{})

	for i, v := range r.paramValues {
		pv[i] = v
		if n, err := strconv.Atoi(v.(string)); err == nil {
			pv[i] = n
		}
	}

	return pv
}

func (r *Route) Build(m map[string]interface{}) (string, error) {
	if len(r.params) != len(m) {
		return "", fmt.Errorf("wrong number of parameters: %v given, %v required", len(m), len(r.params))
	}

	s := r.path
	for p, v := range m {
		reg := regexp.MustCompile(fmt.Sprintf(`{%v(:\w+)?}`, p))

		if !reg.MatchString(s) {
			return "", fmt.Errorf("parameter not valid: %v", p)<commit_msg>making the route concurrent safe<commit_after>package rest

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"sync"
)

const (
	IntParam      = "i"
	AlphaNumParam = "an"
)

var regParam = regexp.MustCompile(`{(\w+)(:\w+)?}`)
var regMap = map[string]string{
	IntParam:      `([0-9]+)`,
	AlphaNumParam: `([0-9A-Za-z]+)`,
}

type Route struct {
	path        string
	actions     map[string]ControllerAction
	params      []string
	paramValues map[string]interface{}
	regex       *regexp.Regexp
	sync.RWMutex
}

func (r *Route) init() {
	toSub := regParam.FindAllStringSubmatch(r.path, -1)

	regString := r.path

	if len(toSub) > 0 {
		r.params = make([]string, len(toSub))

		for i, v := range toSub {
			whole, param, pType, regex := v[0], v[1], v[2], `([^/]+)`

			r.params[i] = param

			if len(pType) > 1 {
				if r, ok := regMap[pType[1:]]; ok {
					regex = r
				}
			}
			regString = strings.Replace(regString, whole, regex, -1)
		}
	}
	r.regex = regexp.MustCompile(regString + "/?")
}

func (r *Route) match(test string) bool {
	matches := r.regex.FindStringSubmatch(test)
	if matches != nil && matches[0] == test {
		r.Lock()
		defer r.Unlock()

		r.paramValues = make(map[string]interface{})

		for i, m := range matches[1:] {
			r.paramValues[r.params[i]] = m
		}
		return true
	}
	return false
}

func (r *Route) Params() map[string]interface{} {
	r.RLock()
	defer r.RUnlock()

	pv := make(map[string]interface{})

	for i, v := range r.paramValues {
		pv[i] = v
		if n, err := strconv.Atoi(v.(string)); err == nil {
			pv[i] = n
		}
	}
	return pv
}

func (r *Route) Build(m map[string]interface{}) (string, error) {
	r.RLock()
	defer r.RUnlock()

	if len(r.params) != len(m) {
		return "", fmt.Errorf("wrong number of parameters: %v given, %v required", len(m), len(r.params))
	}

	s := r.path
	for p, v := range m {
		reg := regexp.MustCompile(fmt.Sprintf(`{%v(:\w+)?}`, p))

		if !reg.MatchString(s) {
			return "", fmt.Errorf("parameter not valid: %v", p)
{'fraction_non_alphanumeric': 0.1907392791370692, 'fraction_numerical': 0.007366482504604052, 'mean_word_length': 3.3253697383390217, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, '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': '1802892', 'n_tokens_mistral': 1630, 'n_tokens_neox': 1501, 'n_words': 354}