Compare commits

..

No commits in common. "1bd1a884c43a0dc664c78a709f48d21bfa2481dc" and "cfdd687bb76facc630035cb21ccfbad093e9a246" have entirely different histories.

8 changed files with 217 additions and 220 deletions

View File

@ -21,8 +21,8 @@ router.Add("GET", "/hello", "...")
router.Add("GET", "/world", "...") router.Add("GET", "/world", "...")
// Parameter routes // Parameter routes
router.Add("GET", "/users/[id]", "...") router.Add("GET", "/users/:id", "...")
router.Add("GET", "/users/[id]/comments", "...") router.Add("GET", "/users/:id/comments", "...")
// Wildcard routes // Wildcard routes
router.Add("GET", "/images/*path", "...") router.Add("GET", "/images/*path", "...")

9
flow.go Normal file
View File

@ -0,0 +1,9 @@
package router
type Flow int
const (
flowStop Flow = iota
flowBegin
flowNext
)

6
parameter.go Normal file
View File

@ -0,0 +1,6 @@
package router
type Parameter struct {
Key string
Value string
}

View File

@ -1,18 +1,5 @@
package router package router
type Flow int
const (
flowStop Flow = iota
flowBegin
flowNext
)
type Parameter struct {
Key string
Value string
}
type Router[T any] struct { type Router[T any] struct {
get Tree[T] get Tree[T]
post Tree[T] post Tree[T]

View File

@ -1,4 +1,4 @@
GET / GET /
GET /[slug] GET /:slug
GET /tags GET /tags
GET /tag/[tag] GET /tag/:tag

View File

@ -1,203 +1,203 @@
GET /authorizations GET /authorizations
GET /authorizations/[id] GET /authorizations/:id
POST /authorizations POST /authorizations
DELETE /authorizations/[id] DELETE /authorizations/:id
GET /applications/[client_id]/tokens/[access_token] GET /applications/:client_id/tokens/:access_token
DELETE /applications/[client_id]/tokens DELETE /applications/:client_id/tokens
DELETE /applications/[client_id]/tokens/[access_token] DELETE /applications/:client_id/tokens/:access_token
GET /events GET /events
GET /repos/[owner]/[repo]/events GET /repos/:owner/:repo/events
GET /networks/[owner]/[repo]/events GET /networks/:owner/:repo/events
GET /orgs/[org]/events GET /orgs/:org/events
GET /users/[user]/received_events GET /users/:user/received_events
GET /users/[user]/received_events/public GET /users/:user/received_events/public
GET /users/[user]/events GET /users/:user/events
GET /users/[user]/events/public GET /users/:user/events/public
GET /users/[user]/events/orgs/[org] GET /users/:user/events/orgs/:org
GET /feeds GET /feeds
GET /notifications GET /notifications
GET /repos/[owner]/[repo]/notifications GET /repos/:owner/:repo/notifications
PUT /notifications PUT /notifications
PUT /repos/[owner]/[repo]/notifications PUT /repos/:owner/:repo/notifications
GET /notifications/threads/[id] GET /notifications/threads/:id
GET /notifications/threads/[id]/subscription GET /notifications/threads/:id/subscription
PUT /notifications/threads/[id]/subscription PUT /notifications/threads/:id/subscription
DELETE /notifications/threads/[id]/subscription DELETE /notifications/threads/:id/subscription
GET /repos/[owner]/[repo]/stargazers GET /repos/:owner/:repo/stargazers
GET /users/[user]/starred GET /users/:user/starred
GET /user/starred GET /user/starred
GET /user/starred/[owner]/[repo] GET /user/starred/:owner/:repo
PUT /user/starred/[owner]/[repo] PUT /user/starred/:owner/:repo
DELETE /user/starred/[owner]/[repo] DELETE /user/starred/:owner/:repo
GET /repos/[owner]/[repo]/subscribers GET /repos/:owner/:repo/subscribers
GET /users/[user]/subscriptions GET /users/:user/subscriptions
GET /user/subscriptions GET /user/subscriptions
GET /repos/[owner]/[repo]/subscription GET /repos/:owner/:repo/subscription
PUT /repos/[owner]/[repo]/subscription PUT /repos/:owner/:repo/subscription
DELETE /repos/[owner]/[repo]/subscription DELETE /repos/:owner/:repo/subscription
GET /user/subscriptions/[owner]/[repo] GET /user/subscriptions/:owner/:repo
PUT /user/subscriptions/[owner]/[repo] PUT /user/subscriptions/:owner/:repo
DELETE /user/subscriptions/[owner]/[repo] DELETE /user/subscriptions/:owner/:repo
GET /users/[user]/gists GET /users/:user/gists
GET /gists GET /gists
GET /gists/[id] GET /gists/:id
POST /gists POST /gists
PUT /gists/[id]/star PUT /gists/:id/star
DELETE /gists/[id]/star DELETE /gists/:id/star
GET /gists/[id]/star GET /gists/:id/star
POST /gists/[id]/forks POST /gists/:id/forks
DELETE /gists/[id] DELETE /gists/:id
GET /repos/[owner]/[repo]/git/blobs/[sha] GET /repos/:owner/:repo/git/blobs/:sha
POST /repos/[owner]/[repo]/git/blobs POST /repos/:owner/:repo/git/blobs
GET /repos/[owner]/[repo]/git/commits/[sha] GET /repos/:owner/:repo/git/commits/:sha
POST /repos/[owner]/[repo]/git/commits POST /repos/:owner/:repo/git/commits
GET /repos/[owner]/[repo]/git/refs GET /repos/:owner/:repo/git/refs
POST /repos/[owner]/[repo]/git/refs POST /repos/:owner/:repo/git/refs
GET /repos/[owner]/[repo]/git/tags/[sha] GET /repos/:owner/:repo/git/tags/:sha
POST /repos/[owner]/[repo]/git/tags POST /repos/:owner/:repo/git/tags
GET /repos/[owner]/[repo]/git/trees/[sha] GET /repos/:owner/:repo/git/trees/:sha
POST /repos/[owner]/[repo]/git/trees POST /repos/:owner/:repo/git/trees
GET /issues GET /issues
GET /user/issues GET /user/issues
GET /orgs/[org]/issues GET /orgs/:org/issues
GET /repos/[owner]/[repo]/issues GET /repos/:owner/:repo/issues
GET /repos/[owner]/[repo]/issues/[number] GET /repos/:owner/:repo/issues/:number
POST /repos/[owner]/[repo]/issues POST /repos/:owner/:repo/issues
GET /repos/[owner]/[repo]/assignees GET /repos/:owner/:repo/assignees
GET /repos/[owner]/[repo]/assignees/[assignee] GET /repos/:owner/:repo/assignees/:assignee
GET /repos/[owner]/[repo]/issues/[number]/comments GET /repos/:owner/:repo/issues/:number/comments
POST /repos/[owner]/[repo]/issues/[number]/comments POST /repos/:owner/:repo/issues/:number/comments
GET /repos/[owner]/[repo]/issues/[number]/events GET /repos/:owner/:repo/issues/:number/events
GET /repos/[owner]/[repo]/labels GET /repos/:owner/:repo/labels
GET /repos/[owner]/[repo]/labels/[name] GET /repos/:owner/:repo/labels/:name
POST /repos/[owner]/[repo]/labels POST /repos/:owner/:repo/labels
DELETE /repos/[owner]/[repo]/labels/[name] DELETE /repos/:owner/:repo/labels/:name
GET /repos/[owner]/[repo]/issues/[number]/labels GET /repos/:owner/:repo/issues/:number/labels
POST /repos/[owner]/[repo]/issues/[number]/labels POST /repos/:owner/:repo/issues/:number/labels
DELETE /repos/[owner]/[repo]/issues/[number]/labels/[name] DELETE /repos/:owner/:repo/issues/:number/labels/:name
PUT /repos/[owner]/[repo]/issues/[number]/labels PUT /repos/:owner/:repo/issues/:number/labels
DELETE /repos/[owner]/[repo]/issues/[number]/labels DELETE /repos/:owner/:repo/issues/:number/labels
GET /repos/[owner]/[repo]/milestones/[number]/labels GET /repos/:owner/:repo/milestones/:number/labels
GET /repos/[owner]/[repo]/milestones GET /repos/:owner/:repo/milestones
GET /repos/[owner]/[repo]/milestones/[number] GET /repos/:owner/:repo/milestones/:number
POST /repos/[owner]/[repo]/milestones POST /repos/:owner/:repo/milestones
DELETE /repos/[owner]/[repo]/milestones/[number] DELETE /repos/:owner/:repo/milestones/:number
GET /emojis GET /emojis
GET /gitignore/templates GET /gitignore/templates
GET /gitignore/templates/[name] GET /gitignore/templates/:name
POST /markdown POST /markdown
POST /markdown/raw POST /markdown/raw
GET /meta GET /meta
GET /rate_limit GET /rate_limit
GET /users/[user]/orgs GET /users/:user/orgs
GET /user/orgs GET /user/orgs
GET /orgs/[org] GET /orgs/:org
GET /orgs/[org]/members GET /orgs/:org/members
GET /orgs/[org]/members/[user] GET /orgs/:org/members/:user
DELETE /orgs/[org]/members/[user] DELETE /orgs/:org/members/:user
GET /orgs/[org]/public_members GET /orgs/:org/public_members
GET /orgs/[org]/public_members/[user] GET /orgs/:org/public_members/:user
PUT /orgs/[org]/public_members/[user] PUT /orgs/:org/public_members/:user
DELETE /orgs/[org]/public_members/[user] DELETE /orgs/:org/public_members/:user
GET /orgs/[org]/teams GET /orgs/:org/teams
GET /teams/[id] GET /teams/:id
POST /orgs/[org]/teams POST /orgs/:org/teams
DELETE /teams/[id] DELETE /teams/:id
GET /teams/[id]/members GET /teams/:id/members
GET /teams/[id]/members/[user] GET /teams/:id/members/:user
PUT /teams/[id]/members/[user] PUT /teams/:id/members/:user
DELETE /teams/[id]/members/[user] DELETE /teams/:id/members/:user
GET /teams/[id]/repos GET /teams/:id/repos
GET /teams/[id]/repos/[owner]/[repo] GET /teams/:id/repos/:owner/:repo
PUT /teams/[id]/repos/[owner]/[repo] PUT /teams/:id/repos/:owner/:repo
DELETE /teams/[id]/repos/[owner]/[repo] DELETE /teams/:id/repos/:owner/:repo
GET /user/teams GET /user/teams
GET /repos/[owner]/[repo]/pulls GET /repos/:owner/:repo/pulls
GET /repos/[owner]/[repo]/pulls/[number] GET /repos/:owner/:repo/pulls/:number
POST /repos/[owner]/[repo]/pulls POST /repos/:owner/:repo/pulls
GET /repos/[owner]/[repo]/pulls/[number]/commits GET /repos/:owner/:repo/pulls/:number/commits
GET /repos/[owner]/[repo]/pulls/[number]/files GET /repos/:owner/:repo/pulls/:number/files
GET /repos/[owner]/[repo]/pulls/[number]/merge GET /repos/:owner/:repo/pulls/:number/merge
PUT /repos/[owner]/[repo]/pulls/[number]/merge PUT /repos/:owner/:repo/pulls/:number/merge
GET /repos/[owner]/[repo]/pulls/[number]/comments GET /repos/:owner/:repo/pulls/:number/comments
PUT /repos/[owner]/[repo]/pulls/[number]/comments PUT /repos/:owner/:repo/pulls/:number/comments
GET /user/repos GET /user/repos
GET /users/[user]/repos GET /users/:user/repos
GET /orgs/[org]/repos GET /orgs/:org/repos
GET /repositories GET /repositories
POST /user/repos POST /user/repos
POST /orgs/[org]/repos POST /orgs/:org/repos
GET /repos/[owner]/[repo] GET /repos/:owner/:repo
GET /repos/[owner]/[repo]/contributors GET /repos/:owner/:repo/contributors
GET /repos/[owner]/[repo]/languages GET /repos/:owner/:repo/languages
GET /repos/[owner]/[repo]/teams GET /repos/:owner/:repo/teams
GET /repos/[owner]/[repo]/tags GET /repos/:owner/:repo/tags
GET /repos/[owner]/[repo]/branches GET /repos/:owner/:repo/branches
GET /repos/[owner]/[repo]/branches/[branch] GET /repos/:owner/:repo/branches/:branch
DELETE /repos/[owner]/[repo] DELETE /repos/:owner/:repo
GET /repos/[owner]/[repo]/collaborators GET /repos/:owner/:repo/collaborators
GET /repos/[owner]/[repo]/collaborators/[user] GET /repos/:owner/:repo/collaborators/:user
PUT /repos/[owner]/[repo]/collaborators/[user] PUT /repos/:owner/:repo/collaborators/:user
DELETE /repos/[owner]/[repo]/collaborators/[user] DELETE /repos/:owner/:repo/collaborators/:user
GET /repos/[owner]/[repo]/comments GET /repos/:owner/:repo/comments
GET /repos/[owner]/[repo]/commits/[sha]/comments GET /repos/:owner/:repo/commits/:sha/comments
POST /repos/[owner]/[repo]/commits/[sha]/comments POST /repos/:owner/:repo/commits/:sha/comments
GET /repos/[owner]/[repo]/comments/[id] GET /repos/:owner/:repo/comments/:id
DELETE /repos/[owner]/[repo]/comments/[id] DELETE /repos/:owner/:repo/comments/:id
GET /repos/[owner]/[repo]/commits GET /repos/:owner/:repo/commits
GET /repos/[owner]/[repo]/commits/[sha] GET /repos/:owner/:repo/commits/:sha
GET /repos/[owner]/[repo]/readme GET /repos/:owner/:repo/readme
GET /repos/[owner]/[repo]/keys GET /repos/:owner/:repo/keys
GET /repos/[owner]/[repo]/keys/[id] GET /repos/:owner/:repo/keys/:id
POST /repos/[owner]/[repo]/keys POST /repos/:owner/:repo/keys
DELETE /repos/[owner]/[repo]/keys/[id] DELETE /repos/:owner/:repo/keys/:id
GET /repos/[owner]/[repo]/downloads GET /repos/:owner/:repo/downloads
GET /repos/[owner]/[repo]/downloads/[id] GET /repos/:owner/:repo/downloads/:id
DELETE /repos/[owner]/[repo]/downloads/[id] DELETE /repos/:owner/:repo/downloads/:id
GET /repos/[owner]/[repo]/forks GET /repos/:owner/:repo/forks
POST /repos/[owner]/[repo]/forks POST /repos/:owner/:repo/forks
GET /repos/[owner]/[repo]/hooks GET /repos/:owner/:repo/hooks
GET /repos/[owner]/[repo]/hooks/[id] GET /repos/:owner/:repo/hooks/:id
POST /repos/[owner]/[repo]/hooks POST /repos/:owner/:repo/hooks
POST /repos/[owner]/[repo]/hooks/[id]/tests POST /repos/:owner/:repo/hooks/:id/tests
DELETE /repos/[owner]/[repo]/hooks/[id] DELETE /repos/:owner/:repo/hooks/:id
POST /repos/[owner]/[repo]/merges POST /repos/:owner/:repo/merges
GET /repos/[owner]/[repo]/releases GET /repos/:owner/:repo/releases
GET /repos/[owner]/[repo]/releases/[id] GET /repos/:owner/:repo/releases/:id
POST /repos/[owner]/[repo]/releases POST /repos/:owner/:repo/releases
DELETE /repos/[owner]/[repo]/releases/[id] DELETE /repos/:owner/:repo/releases/:id
GET /repos/[owner]/[repo]/releases/[id]/assets GET /repos/:owner/:repo/releases/:id/assets
GET /repos/[owner]/[repo]/stats/contributors GET /repos/:owner/:repo/stats/contributors
GET /repos/[owner]/[repo]/stats/commit_activity GET /repos/:owner/:repo/stats/commit_activity
GET /repos/[owner]/[repo]/stats/code_frequency GET /repos/:owner/:repo/stats/code_frequency
GET /repos/[owner]/[repo]/stats/participation GET /repos/:owner/:repo/stats/participation
GET /repos/[owner]/[repo]/stats/punch_card GET /repos/:owner/:repo/stats/punch_card
GET /repos/[owner]/[repo]/statuses/[ref] GET /repos/:owner/:repo/statuses/:ref
POST /repos/[owner]/[repo]/statuses/[ref] POST /repos/:owner/:repo/statuses/:ref
GET /search/repositories GET /search/repositories
GET /search/code GET /search/code
GET /search/issues GET /search/issues
GET /search/users GET /search/users
GET /legacy/issues/search/[owner]/[repository]/[state]/[keyword] GET /legacy/issues/search/:owner/:repository/:state/:keyword
GET /legacy/repos/search/[keyword] GET /legacy/repos/search/:keyword
GET /legacy/user/search/[keyword] GET /legacy/user/search/:keyword
GET /legacy/user/email/[email] GET /legacy/user/email/:email
GET /users/[user] GET /users/:user
GET /user GET /user
GET /users GET /users
GET /user/emails GET /user/emails
POST /user/emails POST /user/emails
DELETE /user/emails DELETE /user/emails
GET /users/[user]/followers GET /users/:user/followers
GET /user/followers GET /user/followers
GET /users/[user]/following GET /users/:user/following
GET /user/following GET /user/following
GET /user/following/[user] GET /user/following/:user
GET /users/[user]/following/[target_user] GET /users/:user/following/:target_user
PUT /user/following/[user] PUT /user/following/:user
DELETE /user/following/[user] DELETE /user/following/:user
GET /users/[user]/keys GET /users/:user/keys
GET /user/keys GET /user/keys
GET /user/keys/[id] GET /user/keys/:id
POST /user/keys POST /user/keys
DELETE /user/keys/[id] DELETE /user/keys/:id

15
tree.go
View File

@ -1,12 +1,13 @@
package router package router
// Representation of a node tree // Super-fancy radix tree
type Tree[T any] struct { type Tree[T any] struct {
root Node[T] root TreeNode[T]
} }
// Adds a new path to the tree // Adds a new element to the tree
func (tree *Tree[T]) Add(path string, data T) { func (tree *Tree[T]) Add(path string, data T) {
// Search tree for equal parts until we can no longer proceed
i := 0 i := 0
offset := 0 offset := 0
node := &tree.root node := &tree.root
@ -16,8 +17,8 @@ func (tree *Tree[T]) Add(path string, data T) {
switch node.kind { switch node.kind {
case parameter: case parameter:
// This only occurs when the same parameter based route is added twice. // This only occurs when the same parameter based route is added twice.
// node: /post/[id| // node: /post/:id|
// path: /post/[id| // path: /post/:id|
if i == len(path) { if i == len(path) {
node.data = data node.data = data
return return
@ -93,7 +94,7 @@ func (tree *Tree[T]) LookupNoAlloc(path string, addParameter func(key string, va
var ( var (
i uint i uint
wildcardPath string wildcardPath string
wildcard *Node[T] wildcard *TreeNode[T]
node = &tree.root node = &tree.root
) )
@ -188,7 +189,7 @@ notFound:
// Binds all handlers to a new one provided by the callback. // Binds all handlers to a new one provided by the callback.
func (tree *Tree[T]) Map(transform func(T) T) { func (tree *Tree[T]) Map(transform func(T) T) {
tree.root.each(func(node *Node[T]) { tree.root.each(func(node *TreeNode[T]) {
node.data = transform(node.data) node.data = transform(node.data)
}) })
} }

View File

@ -5,17 +5,17 @@ import "strings"
// Node types // Node types
const ( const (
separator = '/' separator = '/'
parameter = '[' parameter = ':'
wildcard = '*' wildcard = '*'
) )
// A node on our radix tree // A node on our radix tree
type Node[T any] struct { type TreeNode[T any] struct {
prefix string prefix string
data T data T
children []*Node[T] children []*TreeNode[T]
parameter *Node[T] parameter *TreeNode[T]
wildcard *Node[T] wildcard *TreeNode[T]
indexes []uint8 indexes []uint8
start uint8 start uint8
end uint8 end uint8
@ -26,7 +26,7 @@ type Node[T any] struct {
// node with the given path and data. If path is empty, it will // node with the given path and data. If path is empty, it will
// not create another child node and instead assign the data // not create another child node and instead assign the data
// directly to the node. // directly to the node.
func (node *Node[T]) split(index int, path string, data T) { func (node *TreeNode[T]) split(index int, path string, data T) {
// Create split node with the remaining string // Create split node with the remaining string
splitNode := node.clone(node.prefix[index:]) splitNode := node.clone(node.prefix[index:])
@ -48,8 +48,8 @@ func (node *Node[T]) split(index int, path string, data T) {
} }
// Clone the node with a new prefix // Clone the node with a new prefix
func (node *Node[T]) clone(prefix string) *Node[T] { func (node *TreeNode[T]) clone(prefix string) *TreeNode[T] {
return &Node[T]{ return &TreeNode[T]{
prefix: prefix, prefix: prefix,
data: node.data, data: node.data,
children: node.children, children: node.children,
@ -63,7 +63,7 @@ func (node *Node[T]) clone(prefix string) *Node[T] {
} }
// Reset the node, set the prefix // Reset the node, set the prefix
func (node *Node[T]) reset(prefix string) { func (node *TreeNode[T]) reset(prefix string) {
var empty T var empty T
node.prefix = prefix node.prefix = prefix
node.data = empty node.data = empty
@ -77,7 +77,7 @@ func (node *Node[T]) reset(prefix string) {
} }
// Append the given path to the tree // Append the given path to the tree
func (node *Node[T]) append(path string, data T) { func (node *TreeNode[T]) append(path string, data T) {
// At this point, all we know is that somewhere // At this point, all we know is that somewhere
// in the remaining string we have parameters. // in the remaining string we have parameters.
// node: /user| // node: /user|
@ -106,7 +106,7 @@ func (node *Node[T]) append(path string, data T) {
return return
} }
child := &Node[T]{ child := &TreeNode[T]{
prefix: path, prefix: path,
data: data, data: data,
} }
@ -119,21 +119,15 @@ func (node *Node[T]) append(path string, data T) {
// If we're directly in front of a parameter, // If we're directly in front of a parameter,
// add a parameter node. // add a parameter node.
if paramStart == 0 { if paramStart == 0 {
paramEnd := strings.IndexByte(path[1:], ']') paramEnd := strings.IndexByte(path, separator)
if paramEnd == -1 { if paramEnd == -1 {
paramEnd = len(path) paramEnd = len(path)
} else {
paramEnd = paramEnd + 1 // Account for the offset from path[1:]
} }
nextSep := strings.IndexByte(path[paramEnd+1:], separator) child := &TreeNode[T]{
if nextSep == -1 { prefix: path[1:paramEnd],
nextSep = len(path[paramEnd+1:]) kind: path[paramStart],
}
child := &Node[T]{
prefix: path[1:paramEnd], // Exclude the opening '['
kind: parameter,
} }
switch child.kind { switch child.kind {
@ -141,7 +135,7 @@ func (node *Node[T]) append(path string, data T) {
child.addTrailingSlash(data) child.addTrailingSlash(data)
node.parameter = child node.parameter = child
node = child node = child
path = path[paramEnd+nextSep+1:] path = path[paramEnd:]
continue continue
case wildcard: case wildcard:
@ -162,7 +156,7 @@ func (node *Node[T]) append(path string, data T) {
} }
// Add a normal node with the path before the parameter start. // Add a normal node with the path before the parameter start.
child := &Node[T]{ child := &TreeNode[T]{
prefix: path[:paramStart], prefix: path[:paramStart],
} }
@ -179,7 +173,7 @@ func (node *Node[T]) append(path string, data T) {
} }
// Add a child tree node // Add a child tree node
func (node *Node[T]) addChild(child *Node[T]) { func (node *TreeNode[T]) addChild(child *TreeNode[T]) {
if len(node.children) == 0 { if len(node.children) == 0 {
node.children = append(node.children, nil) node.children = append(node.children, nil)
} }
@ -219,19 +213,19 @@ func (node *Node[T]) addChild(child *Node[T]) {
node.children[index] = child node.children[index] = child
} }
func (node *Node[T]) addTrailingSlash(data T) { func (node *TreeNode[T]) addTrailingSlash(data T) {
if strings.HasSuffix(node.prefix, "/") || node.kind == wildcard || (separator >= node.start && separator < node.end && node.indexes[separator-node.start] != 0) { if strings.HasSuffix(node.prefix, "/") || node.kind == wildcard || (separator >= node.start && separator < node.end && node.indexes[separator-node.start] != 0) {
return return
} }
node.addChild(&Node[T]{ node.addChild(&TreeNode[T]{
prefix: "/", prefix: "/",
data: data, data: data,
}) })
} }
// Traverses the tree and calls the given function on every node. // Traverses the tree and calls the given function on every node.
func (node *Node[T]) each(callback func(*Node[T])) { func (node *TreeNode[T]) each(callback func(*TreeNode[T])) {
callback(node) callback(node)
for _, child := range node.children { for _, child := range node.children {
@ -253,7 +247,7 @@ func (node *Node[T]) each(callback func(*Node[T])) {
// Called when the node was fully parsed and needs to decide the next control flow. // Called when the node was fully parsed and needs to decide the next control flow.
// finish is only called from `tree.Add`. // finish is only called from `tree.Add`.
func (node *Node[T]) finish(path string, data T, i int, offset int) (*Node[T], int, Flow) { func (node *TreeNode[T]) finish(path string, data T, i int, offset int) (*TreeNode[T], int, Flow) {
char := path[i] char := path[i]
if char >= node.start && char < node.end { if char >= node.start && char < node.end {