<commit_before>package httptools

import (
	"fmt"
	"net/http"
	"regexp"
	"sort"
)

type regexpRule struct {
	re *regexp.Regexp
	h  http.Handler
}

type regexpSwitch []regexpRule

func (rs regexpSwitch) Len() int {
	return len(rs)
}

func (rs regexpSwitch) Swap(i, j int) {
	rs[i], rs[j] = rs[j], rs[i]
}

func (rs regexpSwitch) Less(i, j int) bool {
	return len(rs[i].re.String()) < len(rs[j].re.String())
}

func (rs regexpSwitch) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	orw, ok := w.(*ourResponseWriter)
	if !ok {
		orw = newOurResponseWriter(w)
	}

	for _, rule := range rs {
		if m := rule.re.FindStringSubmatch(r.URL.Path); m != nil {
			for i := 1; i < len(m); i++ {
				orw.Vars()[fmt.Sprintf("%d", i)] = m[i]
			}
			rule.h.ServeHTTP(orw, r)
			return
		}
	}
	http.Error(w, "Not found", http.StatusNotFound)
}

// A regexp switch takes a map of regexp strings and handlers.
// If a request path matches a regexp, the corresponding handler is
// executed. Submatches will be put inside a VarsResponseWriter with the
// keys "1", "2", ...
// Longer patterns take precedence over shorter ones.
func NewRegexpSwitch(routes map[string]http.Handler) http.Handler {
	rs := regexpSwitch{}
	for re, h := range routes {
		rs = append(rs, regexpRule{
			re: mustRegexp("^" + re + "$"),
			h:  h,
		})
	}
	sort.Sort(sort.Reverse(rs))
	return rs
}

func mustRegexp(re string) *regexp.Regexp {
	r, err := regexp.CompilePOSIX(re)
	if err != nil {
		panic(err)
	}
	return r
}
<commit_msg>Expose RegexpSwitch array for dynamic generation<commit_after>package httptools

import (
	"fmt"
	"net/http"
	"regexp"
	"sort"
)

type regexpRule struct {
	*regexp.Regexp
	http.Handler
}

// RegexpRule represents a single rule in a RegexpSwitch.
type RegexpRule interface {
	// Same method provided by regexp.Regexp.
	// The returned array will be saved to the VarsResponseWriter.
	FindStringSubmatch(s string) []string
	http.Handler
}

// RegexpSwitch is a slice of RegexpRules. They will be checked
// in the order they have been provided. If a rule matches
// (i.e. Regexp.Rule.FindStringSubmatch return value is non-nil), the
// Handler will be called and the slice traversal is stopped.
type RegexpSwitch []RegexpRule

func (rs RegexpSwitch) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	orw, ok := w.(*ourResponseWriter)
	if !ok {
		orw = newOurResponseWriter(w)
	}

	for _, rule := range rs {
		if m := rule.FindStringSubmatch(r.URL.Path); m != nil {
			for i := 1; i < len(m); i++ {
				orw.Vars()[fmt.Sprintf("%d", i)] = m[i]
			}
			rule.ServeHTTP(orw, r)
			return
		}
	}
	http.Error(w, "Not found", http.StatusNotFound)
}

type regexpSwitch []regexpRule

func (rs regexpSwitch) Len() int {
	return len(rs)
}

func (rs regexpSwitch) Swap(i, j int) {
	rs[i], rs[j] = rs[j], rs[i]
}

func (rs regexpSwitch) Less(i, j int) bool {
	return len(rs[i].String()) < len(rs[j].String())
}

// A regexp switch takes a map of regexp strings and handlers.
// A regexp is considered a match if it matches the whole string.
// Longer patterns take precedence over shorter ones.
func NewRegexpSwitch(routes map[string]http.Handler) RegexpSwitch {
	rs := make(regexpSwitch, 0, len(routes))
	for re, h := range routes {
		rs = append(rs, regexpRule{
			Regexp:  regexp.MustCompilePOSIX("^" + re + "$"),
			Handler: h,
		})
	}
	sort.Sort(sort.Reverse(rs))

	nrs := make(RegexpSwitch, 0, len(routes))
	for _, rr := range rs {
		nrs = append(nrs, RegexpRule(rr))
	}
	return nrs
}

{'fraction_non_alphanumeric': 0.14265129682997119, 'fraction_numerical': 0.001729106628242075, 'mean_word_length': 3.703252032520325, 'pattern_counts': {'":': 0, '<': 7, '<?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': '2433549', 'n_tokens_mistral': 1359, 'n_tokens_neox': 1302, 'n_words': 381}