<commit_before>package glug

import (
	// "github.com/davecgh/go-spew/spew"
	"net/http"
	"net/url"
	"strings"
)

// Conn is a struct to carry all request related data through plugs.
type Conn struct {
	uuid    string
	halted  bool
	Writer  http.ResponseWriter
	Request *http.Request
	Params  url.Values
}

// Router is a http.Handler.
type Router struct {
	node node
}

// Plug is a function type we should use to make a pipeline for request.
type Plug func(Conn) Conn

type node struct {
	path     string
	methods  map[string][]Plug
	children map[string]*node
}

// Halt will stop execution of plugs.
func (conn Conn) Halt() Conn {
	conn.halted = true
	return conn
}

// newConn returns new Conn struct.
func newConn(w http.ResponseWriter, r *http.Request, p url.Values) Conn {
	return Conn{Writer: w, Request: r, Params: p, halted: false}
}

// New will initialize new router.
func New() *Router {
	return &Router{node: node{children: make(map[string]*node)}}
}

// HandleFunc will add new endpoint to router.
func (router *Router) HandleFunc(method string, path string, plugs ...Plug) {
	if path == "/" {
		router.node.path = path
		router.node.methods = make(map[string][]Plug)
		router.node.methods[method] = plugs
	} else {
		var curr *node

		parts := strings.Split(path, "/")[1:]
		depth := len(parts)
		prev := &router.node

		for index, part := range parts {
			if child, ok := prev.children[part]; ok {
				curr = child
			} else {
				curr = &node{path: part, children: make(map[string]*node)}
			}

			if depth == index+1 {
				curr.methods = make(map[string][]Plug)
				curr.methods[method] = plugs
			}

			prev.children[part] = curr
			prev = curr
		}
	}
}

func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/favicon.ico" {
		return
	}

	logRequest(r.Method, r.URL.Path)

	r.ParseForm()

	curr := &router.node
	conn := newConn(w, r, r.Form)

	if r.URL.Path != "/" {
		parts := strings.Split(r.URL.Path, "/")[1:]
		depth := len(parts)

		for index, part := range parts {
			curr = curr.children[part]

			if depth == index+1 {
				break
			}
		}
	}

	for _, plug := range curr.methods[r.Method] {
		result := plug(conn)

		if result.halted == false {
			logPlug(plug)
			conn = result
		} else {
			logHalt(plug)
			break
		}
	}
}<commit_msg>Allow named parameters in router nodes<commit_after>package glug

import (
	// "github.com/davecgh/go-spew/spew"
	"net/http"
	"net/url"
	"strings"
)

// Conn is a struct to carry all request related data through plugs.
type Conn struct {
	uuid    string
	halted  bool
	Writer  http.ResponseWriter
	Request *http.Request
	Params  url.Values
}

// Router is a http.Handler.
type Router struct {
	node node
}

// Plug is a function type we should use to make a pipeline for request.
type Plug func(Conn) Conn

type node struct {
	path     string
	isParam  bool
	methods  map[string][]Plug
	children map[string]*node
}

// Halt will stop execution of plugs.
func (conn Conn) Halt() Conn {
	conn.halted = true
	return conn
}

// newConn returns new Conn struct.
func newConn(w http.ResponseWriter, r *http.Request, p url.Values) Conn {
	return Conn{Writer: w, Request: r, Params: p, halted: false}
}

// New will initialize new router.
func New() *Router {
	return &Router{node: node{children: make(map[string]*node)}}
}

// HandleFunc will add new endpoint to router.
func (router *Router) HandleFunc(method string, path string, plugs ...Plug) {
	if path == "/" {
		router.node.path = path
		router.node.methods = make(map[string][]Plug)
		router.node.methods[method] = plugs
	} else {
		var curr *node

		parts := strings.Split(path, "/")[1:]
		depth := len(parts)
		prev := &router.node

		for index, part := range parts {
			if child, ok := prev.children[part]; ok {
				curr = child
			} else {
				isParam := false

				if part[0] == ':' {
					isParam = true
				}

				curr = &node{
					path:     part,
					children: make(map[string]*node),
					isParam:  isParam,
				}
			}

			if depth == index+1 {
				curr.methods = make(map[string][]Plug)
				curr.methods[method] = plugs
			}

			prev.children[part] = curr
			prev = curr
		}
	}
}

func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/favicon.ico" {
		return
	}

	logRequest(r.Method, r.URL.Path)

	r.ParseForm()

	curr := &router.node
	conn := newConn(w, r, r.Form)

	if r.URL.Path != "/" {
		parts := strings.Split(r.URL.Path, "/")[1:]
		depth := len(parts)

		for index, part := range parts {
			if curr.children[part] == nil {
				for _, child := range curr.children {
					if child.isParam == true {
						conn.Params.Add(child.path[1:], part)
						curr = child
						break
					}
				}
			} else {
				curr = curr.children[part]
			}

			if depth == index+1 {
				break
			}
		}
	}

	for _, plug := range curr.methods[r.Method] {
		result := plug(conn)

		if result.halted == false {
			logPlug(plug)
			conn = result
		} else {
			logHalt(plug)
			break
		}
	}
}
{'fraction_non_alphanumeric': 0.1393525035190026, 'fraction_numerical': 0.0020108586366378444, 'mean_word_length': 2.935126582278481, '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': '1486345', 'n_tokens_mistral': 2001, 'n_tokens_neox': 1749, 'n_words': 500}