diff --git a/README.md b/README.md index e698dc3..7e179aa 100644 --- a/README.md +++ b/README.md @@ -169,4 +169,4 @@ Key Performance Points: ## License -Sharkk Minimal License; do what you like! [Take a look!](LICENSE) +[Sharkk Minimal License](LICENSE); do what you like! diff --git a/fasthttpHandler.go b/fasthttpHandler.go deleted file mode 100644 index 50087ba..0000000 --- a/fasthttpHandler.go +++ /dev/null @@ -1,22 +0,0 @@ -//go:build fasthttp -// +build fasthttp - -package router - -import "github.com/valyala/fasthttp" - -// FastHTTPHandler adapts fasthttp handlers to the router. -type FastHTTPHandler struct { - ctx *fasthttp.RequestCtx - h func(ctx *fasthttp.RequestCtx, params []string) -} - -// Serve executes the fasthttp handler with parameters. -func (f *FastHTTPHandler) Serve(params []string) { - f.h(f.ctx, params) -} - -// NewFastHTTP creates a handler from a fasthttp handler function. -func NewFastHTTP(ctx *fasthttp.RequestCtx, h func(ctx *fasthttp.RequestCtx, params []string)) Handler { - return &FastHTTPHandler{ctx: ctx, h: h} -} diff --git a/handler.go b/handler.go deleted file mode 100644 index 151370c..0000000 --- a/handler.go +++ /dev/null @@ -1,17 +0,0 @@ -package router - -// Handler is an interface for handling HTTP requests with path parameters. -type Handler interface { - Serve(params []string) -} - -// Middleware wraps a handler with additional functionality. -type Middleware func(Handler) Handler - -// FuncHandler is a function that implements the Handler interface. -type FuncHandler func(params []string) - -// Serve calls the handler function. -func (f FuncHandler) Serve(params []string) { - f(params) -} diff --git a/httpHandler.go b/httpHandler.go deleted file mode 100644 index dcb9c58..0000000 --- a/httpHandler.go +++ /dev/null @@ -1,20 +0,0 @@ -package router - -import "net/http" - -// HTTPHandler adapts net/http handlers to the router. -type HTTPHandler struct { - w http.ResponseWriter - r *http.Request - h func(w http.ResponseWriter, r *http.Request, params []string) -} - -// Serve executes the http handler with parameters. -func (h *HTTPHandler) Serve(params []string) { - h.h(h.w, h.r, params) -} - -// NewHTTP creates a handler from a net/http handler function. -func NewHTTP(w http.ResponseWriter, r *http.Request, h func(w http.ResponseWriter, r *http.Request, params []string)) Handler { - return &HTTPHandler{w: w, r: r, h: h} -} diff --git a/router.go b/router.go index 0642ab7..b439e0c 100644 --- a/router.go +++ b/router.go @@ -2,8 +2,17 @@ package router import ( "fmt" + "net/http" ) +// Handler is an interface for handling HTTP requests with path parameters. +type Handler interface { + Serve(params []string) +} + +// Middleware wraps a handler with additional functionality. +type Middleware func(Handler) Handler + // node represents a segment in the URL path and its handling logic. type node struct { segment string // the path segment this node matches @@ -44,6 +53,38 @@ func New() *Router { } } +// ServeHTTP implements http.Handler interface +func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { + handler, params, found := r.Lookup(req.Method, req.URL.Path) + if !found { + http.NotFound(w, req) + return + } + + // Create an HTTP-specific handler wrapper + httpHandler := &httpHandler{ + w: w, + r: req, + h: func(w http.ResponseWriter, r *http.Request, params []string) { + handler.Serve(params) + }, + } + + httpHandler.Serve(params) +} + +// httpHandler adapts net/http handlers to the router. +type httpHandler struct { + w http.ResponseWriter + r *http.Request + h func(w http.ResponseWriter, r *http.Request, params []string) +} + +// Serve executes the http handler with parameters. +func (h *httpHandler) Serve(params []string) { + h.h(h.w, h.r, params) +} + // Use adds middleware to the router's global middleware stack. func (r *Router) Use(middleware ...Middleware) *Router { r.middleware = append(r.middleware, middleware...) @@ -83,68 +124,166 @@ func applyMiddleware(handler Handler, middleware []Middleware) Handler { return h } -// HTTP method registration functions for Router - // Get registers a handler for GET requests at the given path. -func (r *Router) Get(path string, handler Handler) error { - return r.addRoute(r.get, path, handler, r.middleware) +func (r *Router) Get(path string, handler http.HandlerFunc) error { + httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) { + handler(w, r) + } + + return r.addRoute(r.get, path, &httpHandler{h: httpHandlerFunc}, r.middleware) +} + +// Get registers a handler with parameters for GET requests at the given path. +func (r *Router) GetParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error { + return r.addRoute(r.get, path, &httpHandler{h: handler}, r.middleware) } // Post registers a handler for POST requests at the given path. -func (r *Router) Post(path string, handler Handler) error { - return r.addRoute(r.post, path, handler, r.middleware) +func (r *Router) Post(path string, handler http.HandlerFunc) error { + httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) { + handler(w, r) + } + + return r.addRoute(r.post, path, &httpHandler{h: httpHandlerFunc}, r.middleware) +} + +// Post registers a handler with parameters for POST requests at the given path. +func (r *Router) PostParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error { + return r.addRoute(r.post, path, &httpHandler{h: handler}, r.middleware) } // Put registers a handler for PUT requests at the given path. -func (r *Router) Put(path string, handler Handler) error { - return r.addRoute(r.put, path, handler, r.middleware) +func (r *Router) Put(path string, handler http.HandlerFunc) error { + httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) { + handler(w, r) + } + + return r.addRoute(r.put, path, &httpHandler{h: httpHandlerFunc}, r.middleware) +} + +// Put registers a handler with parameters for PUT requests at the given path. +func (r *Router) PutParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error { + return r.addRoute(r.put, path, &httpHandler{h: handler}, r.middleware) } // Patch registers a handler for PATCH requests at the given path. -func (r *Router) Patch(path string, handler Handler) error { - return r.addRoute(r.patch, path, handler, r.middleware) +func (r *Router) Patch(path string, handler http.HandlerFunc) error { + httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) { + handler(w, r) + } + + return r.addRoute(r.patch, path, &httpHandler{h: httpHandlerFunc}, r.middleware) +} + +// Patch registers a handler with parameters for PATCH requests at the given path. +func (r *Router) PatchParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error { + return r.addRoute(r.patch, path, &httpHandler{h: handler}, r.middleware) } // Delete registers a handler for DELETE requests at the given path. -func (r *Router) Delete(path string, handler Handler) error { - return r.addRoute(r.delete, path, handler, r.middleware) +func (r *Router) Delete(path string, handler http.HandlerFunc) error { + httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) { + handler(w, r) + } + + return r.addRoute(r.delete, path, &httpHandler{h: httpHandlerFunc}, r.middleware) } -// HTTP method registration functions for Group +// Delete registers a handler with parameters for DELETE requests at the given path. +func (r *Router) DeleteParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error { + return r.addRoute(r.delete, path, &httpHandler{h: handler}, r.middleware) +} + +// Group HTTP handler registration methods // Get registers a handler for GET requests at the given path. -func (g *Group) Get(path string, handler Handler) error { +func (g *Group) Get(path string, handler http.HandlerFunc) error { + httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) { + handler(w, r) + } + middleware := append([]Middleware{}, g.router.middleware...) middleware = append(middleware, g.middleware...) - return g.router.addRoute(g.router.get, g.prefix+path, handler, middleware) + return g.router.addRoute(g.router.get, g.prefix+path, &httpHandler{h: httpHandlerFunc}, middleware) +} + +// Get registers a handler with parameters for GET requests at the given path. +func (g *Group) GetParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error { + middleware := append([]Middleware{}, g.router.middleware...) + middleware = append(middleware, g.middleware...) + return g.router.addRoute(g.router.get, g.prefix+path, &httpHandler{h: handler}, middleware) } // Post registers a handler for POST requests at the given path. -func (g *Group) Post(path string, handler Handler) error { +func (g *Group) Post(path string, handler http.HandlerFunc) error { + httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) { + handler(w, r) + } + middleware := append([]Middleware{}, g.router.middleware...) middleware = append(middleware, g.middleware...) - return g.router.addRoute(g.router.post, g.prefix+path, handler, middleware) + return g.router.addRoute(g.router.post, g.prefix+path, &httpHandler{h: httpHandlerFunc}, middleware) +} + +// Post registers a handler with parameters for POST requests at the given path. +func (g *Group) PostParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error { + middleware := append([]Middleware{}, g.router.middleware...) + middleware = append(middleware, g.middleware...) + return g.router.addRoute(g.router.post, g.prefix+path, &httpHandler{h: handler}, middleware) } // Put registers a handler for PUT requests at the given path. -func (g *Group) Put(path string, handler Handler) error { +func (g *Group) Put(path string, handler http.HandlerFunc) error { + httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) { + handler(w, r) + } + middleware := append([]Middleware{}, g.router.middleware...) middleware = append(middleware, g.middleware...) - return g.router.addRoute(g.router.put, g.prefix+path, handler, middleware) + return g.router.addRoute(g.router.put, g.prefix+path, &httpHandler{h: httpHandlerFunc}, middleware) +} + +// Put registers a handler with parameters for PUT requests at the given path. +func (g *Group) PutParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error { + middleware := append([]Middleware{}, g.router.middleware...) + middleware = append(middleware, g.middleware...) + return g.router.addRoute(g.router.put, g.prefix+path, &httpHandler{h: handler}, middleware) } // Patch registers a handler for PATCH requests at the given path. -func (g *Group) Patch(path string, handler Handler) error { +func (g *Group) Patch(path string, handler http.HandlerFunc) error { + httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) { + handler(w, r) + } + middleware := append([]Middleware{}, g.router.middleware...) middleware = append(middleware, g.middleware...) - return g.router.addRoute(g.router.patch, g.prefix+path, handler, middleware) + return g.router.addRoute(g.router.patch, g.prefix+path, &httpHandler{h: httpHandlerFunc}, middleware) +} + +// Patch registers a handler with parameters for PATCH requests at the given path. +func (g *Group) PatchParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error { + middleware := append([]Middleware{}, g.router.middleware...) + middleware = append(middleware, g.middleware...) + return g.router.addRoute(g.router.patch, g.prefix+path, &httpHandler{h: handler}, middleware) } // Delete registers a handler for DELETE requests at the given path. -func (g *Group) Delete(path string, handler Handler) error { +func (g *Group) Delete(path string, handler http.HandlerFunc) error { + httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) { + handler(w, r) + } + middleware := append([]Middleware{}, g.router.middleware...) middleware = append(middleware, g.middleware...) - return g.router.addRoute(g.router.delete, g.prefix+path, handler, middleware) + return g.router.addRoute(g.router.delete, g.prefix+path, &httpHandler{h: httpHandlerFunc}, middleware) +} + +// Delete registers a handler with parameters for DELETE requests at the given path. +func (g *Group) DeleteParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error { + middleware := append([]Middleware{}, g.router.middleware...) + middleware = append(middleware, g.middleware...) + return g.router.addRoute(g.router.delete, g.prefix+path, &httpHandler{h: handler}, middleware) } // WithMiddleware applies specific middleware to the next route registration. @@ -178,80 +317,195 @@ type MiddlewareGroup struct { // HTTP method registration functions for MiddlewareRouter // Get registers a handler for GET requests with specific middleware. -func (mr *MiddlewareRouter) Get(path string, handler Handler) error { +func (mr *MiddlewareRouter) Get(path string, handler http.HandlerFunc) error { + httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) { + handler(w, r) + } + middleware := append([]Middleware{}, mr.router.middleware...) middleware = append(middleware, mr.middleware...) - return mr.router.addRoute(mr.router.get, path, handler, middleware) + return mr.router.addRoute(mr.router.get, path, &httpHandler{h: httpHandlerFunc}, middleware) +} + +// Get registers a handler with parameters for GET requests with specific middleware. +func (mr *MiddlewareRouter) GetParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error { + middleware := append([]Middleware{}, mr.router.middleware...) + middleware = append(middleware, mr.middleware...) + return mr.router.addRoute(mr.router.get, path, &httpHandler{h: handler}, middleware) } // Post registers a handler for POST requests with specific middleware. -func (mr *MiddlewareRouter) Post(path string, handler Handler) error { +func (mr *MiddlewareRouter) Post(path string, handler http.HandlerFunc) error { + httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) { + handler(w, r) + } + middleware := append([]Middleware{}, mr.router.middleware...) middleware = append(middleware, mr.middleware...) - return mr.router.addRoute(mr.router.post, path, handler, middleware) + return mr.router.addRoute(mr.router.post, path, &httpHandler{h: httpHandlerFunc}, middleware) +} + +// Post registers a handler with parameters for POST requests with specific middleware. +func (mr *MiddlewareRouter) PostParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error { + middleware := append([]Middleware{}, mr.router.middleware...) + middleware = append(middleware, mr.middleware...) + return mr.router.addRoute(mr.router.post, path, &httpHandler{h: handler}, middleware) } // Put registers a handler for PUT requests with specific middleware. -func (mr *MiddlewareRouter) Put(path string, handler Handler) error { +func (mr *MiddlewareRouter) Put(path string, handler http.HandlerFunc) error { + httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) { + handler(w, r) + } + middleware := append([]Middleware{}, mr.router.middleware...) middleware = append(middleware, mr.middleware...) - return mr.router.addRoute(mr.router.put, path, handler, middleware) + return mr.router.addRoute(mr.router.put, path, &httpHandler{h: httpHandlerFunc}, middleware) +} + +// Put registers a handler with parameters for PUT requests with specific middleware. +func (mr *MiddlewareRouter) PutParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error { + middleware := append([]Middleware{}, mr.router.middleware...) + middleware = append(middleware, mr.middleware...) + return mr.router.addRoute(mr.router.put, path, &httpHandler{h: handler}, middleware) } // Patch registers a handler for PATCH requests with specific middleware. -func (mr *MiddlewareRouter) Patch(path string, handler Handler) error { +func (mr *MiddlewareRouter) Patch(path string, handler http.HandlerFunc) error { + httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) { + handler(w, r) + } + middleware := append([]Middleware{}, mr.router.middleware...) middleware = append(middleware, mr.middleware...) - return mr.router.addRoute(mr.router.patch, path, handler, middleware) + return mr.router.addRoute(mr.router.patch, path, &httpHandler{h: httpHandlerFunc}, middleware) +} + +// Patch registers a handler with parameters for PATCH requests with specific middleware. +func (mr *MiddlewareRouter) PatchParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error { + middleware := append([]Middleware{}, mr.router.middleware...) + middleware = append(middleware, mr.middleware...) + return mr.router.addRoute(mr.router.patch, path, &httpHandler{h: handler}, middleware) } // Delete registers a handler for DELETE requests with specific middleware. -func (mr *MiddlewareRouter) Delete(path string, handler Handler) error { +func (mr *MiddlewareRouter) Delete(path string, handler http.HandlerFunc) error { + httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) { + handler(w, r) + } + middleware := append([]Middleware{}, mr.router.middleware...) middleware = append(middleware, mr.middleware...) - return mr.router.addRoute(mr.router.delete, path, handler, middleware) + return mr.router.addRoute(mr.router.delete, path, &httpHandler{h: httpHandlerFunc}, middleware) +} + +// Delete registers a handler with parameters for DELETE requests with specific middleware. +func (mr *MiddlewareRouter) DeleteParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error { + middleware := append([]Middleware{}, mr.router.middleware...) + middleware = append(middleware, mr.middleware...) + return mr.router.addRoute(mr.router.delete, path, &httpHandler{h: handler}, middleware) } // HTTP method registration functions for MiddlewareGroup // Get registers a handler for GET requests with specific middleware. -func (mg *MiddlewareGroup) Get(path string, handler Handler) error { +func (mg *MiddlewareGroup) Get(path string, handler http.HandlerFunc) error { + httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) { + handler(w, r) + } + middleware := append([]Middleware{}, mg.group.router.middleware...) middleware = append(middleware, mg.group.middleware...) middleware = append(middleware, mg.middleware...) - return mg.group.router.addRoute(mg.group.router.get, mg.group.prefix+path, handler, middleware) + return mg.group.router.addRoute(mg.group.router.get, mg.group.prefix+path, &httpHandler{h: httpHandlerFunc}, middleware) +} + +// Get registers a handler with parameters for GET requests with specific middleware. +func (mg *MiddlewareGroup) GetParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error { + middleware := append([]Middleware{}, mg.group.router.middleware...) + middleware = append(middleware, mg.group.middleware...) + middleware = append(middleware, mg.middleware...) + return mg.group.router.addRoute(mg.group.router.get, mg.group.prefix+path, &httpHandler{h: handler}, middleware) } // Post registers a handler for POST requests with specific middleware. -func (mg *MiddlewareGroup) Post(path string, handler Handler) error { +func (mg *MiddlewareGroup) Post(path string, handler http.HandlerFunc) error { + httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) { + handler(w, r) + } + middleware := append([]Middleware{}, mg.group.router.middleware...) middleware = append(middleware, mg.group.middleware...) middleware = append(middleware, mg.middleware...) - return mg.group.router.addRoute(mg.group.router.post, mg.group.prefix+path, handler, middleware) + return mg.group.router.addRoute(mg.group.router.post, mg.group.prefix+path, &httpHandler{h: httpHandlerFunc}, middleware) +} + +// Post registers a handler with parameters for POST requests with specific middleware. +func (mg *MiddlewareGroup) PostParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error { + middleware := append([]Middleware{}, mg.group.router.middleware...) + middleware = append(middleware, mg.group.middleware...) + middleware = append(middleware, mg.middleware...) + return mg.group.router.addRoute(mg.group.router.post, mg.group.prefix+path, &httpHandler{h: handler}, middleware) } // Put registers a handler for PUT requests with specific middleware. -func (mg *MiddlewareGroup) Put(path string, handler Handler) error { +func (mg *MiddlewareGroup) Put(path string, handler http.HandlerFunc) error { + httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) { + handler(w, r) + } + middleware := append([]Middleware{}, mg.group.router.middleware...) middleware = append(middleware, mg.group.middleware...) middleware = append(middleware, mg.middleware...) - return mg.group.router.addRoute(mg.group.router.put, mg.group.prefix+path, handler, middleware) + return mg.group.router.addRoute(mg.group.router.put, mg.group.prefix+path, &httpHandler{h: httpHandlerFunc}, middleware) +} + +// Put registers a handler with parameters for PUT requests with specific middleware. +func (mg *MiddlewareGroup) PutParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error { + middleware := append([]Middleware{}, mg.group.router.middleware...) + middleware = append(middleware, mg.group.middleware...) + middleware = append(middleware, mg.middleware...) + return mg.group.router.addRoute(mg.group.router.put, mg.group.prefix+path, &httpHandler{h: handler}, middleware) } // Patch registers a handler for PATCH requests with specific middleware. -func (mg *MiddlewareGroup) Patch(path string, handler Handler) error { +func (mg *MiddlewareGroup) Patch(path string, handler http.HandlerFunc) error { + httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) { + handler(w, r) + } + middleware := append([]Middleware{}, mg.group.router.middleware...) middleware = append(middleware, mg.group.middleware...) middleware = append(middleware, mg.middleware...) - return mg.group.router.addRoute(mg.group.router.patch, mg.group.prefix+path, handler, middleware) + return mg.group.router.addRoute(mg.group.router.patch, mg.group.prefix+path, &httpHandler{h: httpHandlerFunc}, middleware) +} + +// Patch registers a handler with parameters for PATCH requests with specific middleware. +func (mg *MiddlewareGroup) PatchParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error { + middleware := append([]Middleware{}, mg.group.router.middleware...) + middleware = append(middleware, mg.group.middleware...) + middleware = append(middleware, mg.middleware...) + return mg.group.router.addRoute(mg.group.router.patch, mg.group.prefix+path, &httpHandler{h: handler}, middleware) } // Delete registers a handler for DELETE requests with specific middleware. -func (mg *MiddlewareGroup) Delete(path string, handler Handler) error { +func (mg *MiddlewareGroup) Delete(path string, handler http.HandlerFunc) error { + httpHandlerFunc := func(w http.ResponseWriter, r *http.Request, _ []string) { + handler(w, r) + } + middleware := append([]Middleware{}, mg.group.router.middleware...) middleware = append(middleware, mg.group.middleware...) middleware = append(middleware, mg.middleware...) - return mg.group.router.addRoute(mg.group.router.delete, mg.group.prefix+path, handler, middleware) + return mg.group.router.addRoute(mg.group.router.delete, mg.group.prefix+path, &httpHandler{h: httpHandlerFunc}, middleware) +} + +// Delete registers a handler with parameters for DELETE requests with specific middleware. +func (mg *MiddlewareGroup) DeleteParam(path string, handler func(w http.ResponseWriter, r *http.Request, params []string)) error { + middleware := append([]Middleware{}, mg.group.router.middleware...) + middleware = append(middleware, mg.group.middleware...) + middleware = append(middleware, mg.middleware...) + return mg.group.router.addRoute(mg.group.router.delete, mg.group.prefix+path, &httpHandler{h: handler}, middleware) } // readSegment extracts the next path segment starting at the given position. diff --git a/router_test.go b/router_test.go index d747ba6..4b653e8 100644 --- a/router_test.go +++ b/router_test.go @@ -7,19 +7,25 @@ import ( assert "git.sharkk.net/Go/Assert" ) -// Create a test handler that implements the Handler interface -func testHandler(t *testing.T, expectedParams []string) Handler { - return FuncHandler(func(params []string) { - assert.Equal(t, len(params), len(expectedParams)) - for i, expected := range expectedParams { - assert.Equal(t, params[i], expected) - } - }) +// simpleHandler implements the Handler interface +type simpleHandler struct { + fn func(params []string) +} + +func (h *simpleHandler) Serve(params []string) { + h.fn(params) +} + +// newHandler creates a simple Handler from a function +func newHandler(fn func(params []string)) Handler { + return &simpleHandler{fn: fn} } func TestRootPath(t *testing.T) { r := New() - r.Get("/", testHandler(t, nil)) + r.GetParam("/", func(w http.ResponseWriter, r *http.Request, params []string) { + // No-op for testing + }) h, params, found := r.Lookup("GET", "/") assert.True(t, found) @@ -28,7 +34,9 @@ func TestRootPath(t *testing.T) { func TestStaticPath(t *testing.T) { r := New() - r.Get("/users/all", testHandler(t, nil)) + r.GetParam("/users/all", func(w http.ResponseWriter, r *http.Request, params []string) { + // No-op for testing + }) h, params, found := r.Lookup("GET", "/users/all") assert.True(t, found) @@ -37,25 +45,40 @@ func TestStaticPath(t *testing.T) { func TestSingleParameter(t *testing.T) { r := New() - r.Get("/users/[id]", testHandler(t, []string{"123"})) + + called := false + r.GetParam("/users/[id]", func(w http.ResponseWriter, r *http.Request, params []string) { + called = true + assert.Equal(t, params[0], "123") + }) h, params, found := r.Lookup("GET", "/users/123") assert.True(t, found) h.Serve(params) + assert.True(t, called) } func TestMultipleParameters(t *testing.T) { r := New() - r.Get("/users/[id]/posts/[postId]", testHandler(t, []string{"123", "456"})) + + called := false + r.GetParam("/users/[id]/posts/[postId]", func(w http.ResponseWriter, r *http.Request, params []string) { + called = true + assert.Equal(t, params[0], "123") + assert.Equal(t, params[1], "456") + }) h, params, found := r.Lookup("GET", "/users/123/posts/456") assert.True(t, found) h.Serve(params) + assert.True(t, called) } func TestNonExistentPath(t *testing.T) { r := New() - r.Get("/users/[id]", testHandler(t, nil)) + r.GetParam("/users/[id]", func(w http.ResponseWriter, r *http.Request, params []string) { + // No-op for testing + }) _, _, found := r.Lookup("GET", "/posts/123") assert.False(t, found) @@ -63,7 +86,9 @@ func TestNonExistentPath(t *testing.T) { func TestWrongMethod(t *testing.T) { r := New() - r.Get("/users/[id]", testHandler(t, nil)) + r.GetParam("/users/[id]", func(w http.ResponseWriter, r *http.Request, params []string) { + // No-op for testing + }) _, _, found := r.Lookup("POST", "/users/123") assert.False(t, found) @@ -71,21 +96,29 @@ func TestWrongMethod(t *testing.T) { func TestTrailingSlash(t *testing.T) { r := New() - r.Get("/users/[id]", testHandler(t, []string{"123"})) + + called := false + r.GetParam("/users/[id]", func(w http.ResponseWriter, r *http.Request, params []string) { + called = true + assert.Equal(t, params[0], "123") + }) h, params, found := r.Lookup("GET", "/users/123/") assert.True(t, found) h.Serve(params) + assert.True(t, called) } func TestDifferentMethods(t *testing.T) { r := New() - r.Get("/test", testHandler(t, nil)) - r.Post("/test", testHandler(t, nil)) - r.Put("/test", testHandler(t, nil)) - r.Patch("/test", testHandler(t, nil)) - r.Delete("/test", testHandler(t, nil)) + handler := func(w http.ResponseWriter, r *http.Request, params []string) {} + + r.GetParam("/test", handler) + r.PostParam("/test", handler) + r.PutParam("/test", handler) + r.PatchParam("/test", handler) + r.DeleteParam("/test", handler) methods := []string{"GET", "POST", "PUT", "PATCH", "DELETE"} for _, method := range methods { @@ -100,39 +133,55 @@ func TestWildcardPath(t *testing.T) { r := New() t.Run("simple wildcard", func(t *testing.T) { - err := r.Get("/files/*path", testHandler(t, []string{"docs/report.pdf"})) + called := false + err := r.GetParam("/files/*path", func(w http.ResponseWriter, r *http.Request, params []string) { + called = true + assert.Equal(t, params[0], "docs/report.pdf") + }) assert.Nil(t, err) h, params, found := r.Lookup("GET", "/files/docs/report.pdf") assert.True(t, found) h.Serve(params) + assert.True(t, called) }) t.Run("wildcard with empty path", func(t *testing.T) { - err := r.Get("/download/*filepath", testHandler(t, []string{""})) + called := false + err := r.GetParam("/download/*filepath", func(w http.ResponseWriter, r *http.Request, params []string) { + called = true + assert.Equal(t, params[0], "") + }) assert.Nil(t, err) h, params, found := r.Lookup("GET", "/download/") assert.True(t, found) h.Serve(params) + assert.True(t, called) }) t.Run("wildcard with parameter", func(t *testing.T) { - err := r.Get("/users/[id]/*action", testHandler(t, []string{"123", "settings/profile/avatar"})) + called := false + err := r.GetParam("/users/[id]/*action", func(w http.ResponseWriter, r *http.Request, params []string) { + called = true + assert.Equal(t, params[0], "123") + assert.Equal(t, params[1], "settings/profile/avatar") + }) assert.Nil(t, err) h, params, found := r.Lookup("GET", "/users/123/settings/profile/avatar") assert.True(t, found) h.Serve(params) + assert.True(t, called) }) t.Run("multiple wildcards not allowed", func(t *testing.T) { - err := r.Get("/api/*version/*path", testHandler(t, nil)) + err := r.GetParam("/api/*version/*path", func(w http.ResponseWriter, r *http.Request, params []string) {}) assert.NotNil(t, err) }) t.Run("non-last wildcard not allowed", func(t *testing.T) { - err := r.Get("/api/*version/users", testHandler(t, nil)) + err := r.GetParam("/api/*version/users", func(w http.ResponseWriter, r *http.Request, params []string) {}) assert.NotNil(t, err) }) } @@ -146,13 +195,13 @@ func TestMiddleware(t *testing.T) { executed := false r.Use(func(next Handler) Handler { - return FuncHandler(func(params []string) { + return newHandler(func(params []string) { executed = true next.Serve(params) }) }) - r.Get("/test", testHandler(t, nil)) + r.GetParam("/test", func(w http.ResponseWriter, r *http.Request, params []string) {}) h, params, found := r.Lookup("GET", "/test") assert.True(t, found) @@ -167,7 +216,7 @@ func TestMiddleware(t *testing.T) { order := []int{} r.Use(func(next Handler) Handler { - return FuncHandler(func(params []string) { + return newHandler(func(params []string) { order = append(order, 1) next.Serve(params) order = append(order, 4) @@ -175,16 +224,16 @@ func TestMiddleware(t *testing.T) { }) r.Use(func(next Handler) Handler { - return FuncHandler(func(params []string) { + return newHandler(func(params []string) { order = append(order, 2) next.Serve(params) order = append(order, 3) }) }) - r.Get("/test", FuncHandler(func(params []string) { + r.GetParam("/test", func(w http.ResponseWriter, r *http.Request, params []string) { order = append(order, 0) - })) + }) h, params, found := r.Lookup("GET", "/test") assert.True(t, found) @@ -205,13 +254,13 @@ func TestMiddleware(t *testing.T) { executed := false middleware := func(next Handler) Handler { - return FuncHandler(func(params []string) { + return newHandler(func(params []string) { executed = true next.Serve(params) }) } - r.WithMiddleware(middleware).Get("/test", testHandler(t, nil)) + r.WithMiddleware(middleware).GetParam("/test", func(w http.ResponseWriter, r *http.Request, params []string) {}) h, params, found := r.Lookup("GET", "/test") assert.True(t, found) @@ -227,7 +276,7 @@ func TestGroup(t *testing.T) { // Create API group api := r.Group("/api") - api.Get("/users", testHandler(t, nil)) + api.GetParam("/users", func(w http.ResponseWriter, r *http.Request, params []string) {}) h, params, found := r.Lookup("GET", "/api/users") assert.True(t, found) @@ -240,7 +289,7 @@ func TestGroup(t *testing.T) { // Create nested groups api := r.Group("/api") v1 := api.Group("/v1") - v1.Get("/users", testHandler(t, nil)) + v1.GetParam("/users", func(w http.ResponseWriter, r *http.Request, params []string) {}) h, params, found := r.Lookup("GET", "/api/v1/users") assert.True(t, found) @@ -254,13 +303,13 @@ func TestGroup(t *testing.T) { // Create group with middleware api := r.Group("/api") api.Use(func(next Handler) Handler { - return FuncHandler(func(params []string) { + return newHandler(func(params []string) { executed = true next.Serve(params) }) }) - api.Get("/users", testHandler(t, nil)) + api.GetParam("/users", func(w http.ResponseWriter, r *http.Request, params []string) {}) h, params, found := r.Lookup("GET", "/api/users") assert.True(t, found) @@ -275,7 +324,7 @@ func TestGroup(t *testing.T) { // Create group with middleware api := r.Group("/api") api.Use(func(next Handler) Handler { - return FuncHandler(func(params []string) { + return newHandler(func(params []string) { order = append(order, 1) next.Serve(params) }) @@ -284,15 +333,15 @@ func TestGroup(t *testing.T) { // Create nested group with additional middleware v1 := api.Group("/v1") v1.Use(func(next Handler) Handler { - return FuncHandler(func(params []string) { + return newHandler(func(params []string) { order = append(order, 2) next.Serve(params) }) }) - v1.Get("/users", FuncHandler(func(params []string) { + v1.GetParam("/users", func(w http.ResponseWriter, r *http.Request, params []string) { order = append(order, 3) - })) + }) h, params, found := r.Lookup("GET", "/api/v1/users") assert.True(t, found) @@ -312,7 +361,7 @@ func TestGroup(t *testing.T) { // Create group with middleware api := r.Group("/api") api.Use(func(next Handler) Handler { - return FuncHandler(func(params []string) { + return newHandler(func(params []string) { order = append(order, 1) next.Serve(params) }) @@ -320,13 +369,13 @@ func TestGroup(t *testing.T) { // Add route with specific middleware api.WithMiddleware(func(next Handler) Handler { - return FuncHandler(func(params []string) { + return newHandler(func(params []string) { order = append(order, 2) next.Serve(params) }) - }).Get("/users", FuncHandler(func(params []string) { + }).GetParam("/users", func(w http.ResponseWriter, r *http.Request, params []string) { order = append(order, 3) - })) + }) h, params, found := r.Lookup("GET", "/api/users") assert.True(t, found) @@ -343,13 +392,13 @@ func TestGroup(t *testing.T) { // Benchmarks func BenchmarkRouterLookup(b *testing.B) { r := New() - h := FuncHandler(func(params []string) {}) + handler := func(w http.ResponseWriter, r *http.Request, params []string) {} // Setup routes for benchmarking - r.Get("/", h) - r.Get("/users/all", h) - r.Get("/users/[id]", h) - r.Get("/users/[id]/posts/[postId]", h) + r.GetParam("/", handler) + r.GetParam("/users/all", handler) + r.GetParam("/users/[id]", handler) + r.GetParam("/users/[id]/posts/[postId]", handler) b.Run("root", func(b *testing.B) { for i := 0; i < b.N; i++ { @@ -384,11 +433,11 @@ func BenchmarkRouterLookup(b *testing.B) { func BenchmarkParallelLookup(b *testing.B) { r := New() - h := FuncHandler(func(params []string) {}) + handler := func(w http.ResponseWriter, r *http.Request, params []string) {} - r.Get("/users/[id]", h) - r.Get("/posts/[id]/comments", h) - r.Get("/products/[category]/[id]", h) + r.GetParam("/users/[id]", handler) + r.GetParam("/posts/[id]/comments", handler) + r.GetParam("/products/[category]/[id]", handler) b.RunParallel(func(pb *testing.PB) { i := 0 @@ -408,11 +457,11 @@ func BenchmarkParallelLookup(b *testing.B) { func BenchmarkWildcardLookup(b *testing.B) { r := New() - h := FuncHandler(func(params []string) {}) + handler := func(w http.ResponseWriter, r *http.Request, params []string) {} // Setup routes for benchmarking - r.Get("/files/*path", h) - r.Get("/users/[id]/*action", h) + r.GetParam("/files/*path", handler) + r.GetParam("/users/[id]/*action", handler) b.Run("simple_wildcard", func(b *testing.B) { for i := 0; i < b.N; i++ { @@ -429,16 +478,14 @@ func BenchmarkWildcardLookup(b *testing.B) { func BenchmarkMiddleware(b *testing.B) { passthrough := func(next Handler) Handler { - return FuncHandler(func(params []string) { + return newHandler(func(params []string) { next.Serve(params) }) } - simpleHandler := FuncHandler(func(params []string) {}) - b.Run("no_middleware", func(b *testing.B) { r := New() - r.Get("/test", simpleHandler) + r.GetParam("/test", func(w http.ResponseWriter, r *http.Request, params []string) {}) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -450,7 +497,7 @@ func BenchmarkMiddleware(b *testing.B) { b.Run("one_middleware", func(b *testing.B) { r := New() r.Use(passthrough) - r.Get("/test", simpleHandler) + r.GetParam("/test", func(w http.ResponseWriter, r *http.Request, params []string) {}) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -464,7 +511,7 @@ func BenchmarkMiddleware(b *testing.B) { for i := 0; i < 5; i++ { r.Use(passthrough) } - r.Get("/test", simpleHandler) + r.GetParam("/test", func(w http.ResponseWriter, r *http.Request, params []string) {}) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -475,11 +522,11 @@ func BenchmarkMiddleware(b *testing.B) { } func BenchmarkGroups(b *testing.B) { - simpleHandler := FuncHandler(func(params []string) {}) + handler := func(w http.ResponseWriter, r *http.Request, params []string) {} b.Run("flat_route", func(b *testing.B) { r := New() - r.Get("/api/v1/users", simpleHandler) + r.GetParam("/api/v1/users", handler) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -491,7 +538,7 @@ func BenchmarkGroups(b *testing.B) { r := New() api := r.Group("/api") v1 := api.Group("/v1") - v1.Get("/users", simpleHandler) + v1.GetParam("/users", handler) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -503,12 +550,12 @@ func BenchmarkGroups(b *testing.B) { r := New() api := r.Group("/api") api.Use(func(next Handler) Handler { - return FuncHandler(func(params []string) { + return newHandler(func(params []string) { next.Serve(params) }) }) v1 := api.Group("/v1") - v1.Get("/users", simpleHandler) + v1.GetParam("/users", handler) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -521,10 +568,12 @@ func BenchmarkGroups(b *testing.B) { func BenchmarkComparison(b *testing.B) { // Custom router setup customRouter := New() - customRouter.Get("/", FuncHandler(func(params []string) {})) - customRouter.Get("/users/all", FuncHandler(func(params []string) {})) - customRouter.Get("/users/[id]", FuncHandler(func(params []string) {})) - customRouter.Get("/users/[id]/posts/[postId]", FuncHandler(func(params []string) {})) + handler := func(w http.ResponseWriter, r *http.Request, params []string) {} + + customRouter.GetParam("/", handler) + customRouter.GetParam("/users/all", handler) + customRouter.GetParam("/users/[id]", handler) + customRouter.GetParam("/users/[id]/posts/[postId]", handler) // Standard mux setup mux := http.NewServeMux()